热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

实现《魔影》音视频模块开发

1.音视频采样编码的基本原理将模拟信号转化为数字信号,隔段时间进行采样2.ffmpeg的基本情况介绍Fmpeg是领先的多媒体框架,能够解码、编码

1.音视频采样编码的基本原理

将模拟信号转化为数字信号,隔段时间进行采样
在这里插入图片描述

2.ffmpeg的基本情况


介绍

Fmpeg 是领先的多媒体框架,能够解码、编码、转码、混合、解密、流媒体、过滤和播放人类和机器创造的几乎所有东西。它支持最晦涩的古老格式,直到最尖端的格式。无论它们是由某个标准委员会、社区还是公司设计的。它还具有高度的便携性。
FFmpeg 可以在 多个平台构建环境、机器架构和配置下编译、运行,并通过测试基础设施 FATE。

FFmpeg命令行工具获取


  • 下载地址
    • 访问FFmpeg官网(http://ffmpeg.org)选择Download,选择Windows Package进入Zeranoe FFmpeg网站。
    • 注意不要直接从FFmpeg官网下载源代码。
  • 版本说明
    • Zeranoe网站中的FFmpeg分为3个版本:
      • static:只包含3个体积很大的exe文件。
      • shared:除了3个体积较小的exe文件外,还包含了dll动态库文件
      • Dev:只包含了开发用的头文件(*.h)和导入库文件(*.lib)
  • 一般使用命令行时下载Static或者Shared版本就可以了
  • 进行开发需要下载Dev版本

ffmpeg.exe的使用


  • 命令格式

    • 功能
      ffmpeg.exe用于视频的转码

    • 最简单的命令

      ffmpeg -i input.avi -b:v 640k output.ts

    • 该命令将当前文件夹下的input.avi转换为output.ts文件,并将其文件视频的码率设置为640kbps。

    • 命令格式

      ffmpeg -i (输入文件路径) -b:v (输出视频码率) (输出文件路径)

    • 命令参数

    参数说明
    -h帮助
    -i filename输入文件
    -t duration设置处理时间,格式为hh:mm:ss
    -ss duration设置起始时间,格式为hh:mm:ss
    -b:v bltrate设置视频码率
    -b:a bitrate设置音频码率
    -r fps设置帧率
    -s wxh设置分辨率,格式为WxH(用*也可以)
    -c:v codec设置视频编码器
    -c:a codec设置音频编码器
    -ar freq设置音频采样率

    如: ffmpeg -i video.mkv video.mp4 将video从MKV格式转码为MP4格式并另外保存


ffplay.exe的使用


  • 命令格式

    • 功能
      用于视频的播放

    • 最简单的命令

      ffplay input.avi

    • 该命令将播放当前文件夹下的input.avi文件。

    • 命令格式

      ffmpeg (输入文件路径)

    • 命令参数

    快捷键说明
    q,ESC退出
    p,空格暂停
    鼠标右键屏幕跳转到指定位置

解封装


  • av+register_all():注册所有组件

  • avformat_network_init()

  • avformat_open_input(…):打开输入视频文件

    • 确保av_register_all、avformat_network_init已经停用
    • AVFormatContext **ps
    • const char *url
    • AVInputFormat *fmt
    • AVDIctionary **options
  • avformat_find_stream_info(…):获取视频文件信息

  • avcodec_find_decoder():查找解码器

  • avcodec_open2():打开解码器

  • av_find_best_stream(…)

  • AVFormatContext (封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息)

    • AVIOContext *pd;char filename[1024];
    • iformat //输入视频的AVInputFormat
    • unsigned int nb_streams; //输入视频的AVStream
    • AVStream **streams; //输入视频的AVStream []数组
    • int64_t duration; //AV_TIME_BASE 输入视频的时长(以微秒为单位)
    • int64_t bit_rate; //输入视频码率
    • void avformat_close_input(AVFormatContent **s); //关闭输入视频文件,释放空间
  • AVInputFormat (每种封装格式(例如FLV,MKV,MP4,AVI)对应一个结构体)

    • name:封装格式名称
    • long_name:封装格式的长名称
    • extensions:封装格式的扩展名
    • id:封装格式ID
    • 一些封装格式处理的接口函数
  • AVStream (视频文件中每个视频(音频)流对应一个结构体)

    • AVCodecContext *codec; //过时了
    • id //序号
    • AVRational time_base; //分数表示,减少精度损失
    • int64_t duration; //毫秒 duration (time_base,num/(double)time_base.den) 1000毫秒
    • int64_t nb_frames;
    • AVRational avg_frame_rate;
    • AVCodecParameters *codecpar:(音视频参数)用于替代过时的1
      • AVBufferRef *buff; //删除的时候-1
      • int64_t pts; //pts * (num/den) 显示时间戳
      • int64_t dts; //解码时间戳
      • unit8_t *data; //压缩编码数据
      • int size; //压缩编码数据大小
      • stream_index //所属的AVStream
      • AVPacket *av_packet_alloc(void); //创建并初始化,申请了堆上的空间,需要自己释放
      • AVPacket *av_packet_clone(const AVPacket *src); //创建和并用计数
      • int av_packet_ref(AVPacket *dst,const AVPacket *src);
      • av_packet_unred(AVPacket *pkt);
      • void av_packet_free(AVPacket **pkt); //清空对象并减引用计数
      • void av_init_packet(AVPacket *pkt); //默认值
      • int av_packet_from_data(AVPacket *pkt,unit7_t *data,int size);
      • int av_copy_packet(AVPacket *dst,const AVPacket *src); ///attribute_deprecated用clone函数代替了
  • av_read_frame(…):从输入文件读取一帧压缩数据

    • AVFormatContext *s;
    • AVPacket *pkt; //不能传null
    • return 0 if OK, <0 on error or end of file
  • avcodec_decode_video2()&#xff1a;解码一帧压缩数据

  • avcodec_close()&#xff1a;关闭解码器


找寻


3.PCM过程的一些基本参数

PCM音频数据

4.视频的封装格式及播放的基本流程

视频播放器原理

  • 封装格式(MP4,RMVB,TS,FLV,AVI)
  • 视频编码数据(H.264.MPEG2,VC-1)
  • 音频编码数据(AAC,MP3,AC-3)
  • 视频像素数据(YUV420P,RGB)
  • 音频采样数据(PCM)

播放视频流程

在这里插入图片描述
可以使用软件mediainfo进行读取。

5. ffmpeg的解码大致流程

在这里插入图片描述

FFmpeg解码的流程图如下所示

在这里插入图片描述

6.ffmpeg程序的环境配置

配置ffmpeg所依赖的文件(此处是VS的配置&#xff0c;Qt Creator单独配置)&#xff1a;

  • C&#43;&#43; -> 常规 -> 附加包含目录(ffmpeg的include目录)
  • 链接器 -> 附加库目录(debug下面的x64)
  • 调试->工作目录(代码中包含的文件(资料文件&#xff0c;视频文件)所需要的路径)
  • 针对C语言需要多添加一句&#xff1a;

extern "C"{#include "libacformat/avformat.h"
}

  • 代码包含或者配置"附加依赖项"(包含lib库)

7.ffmpeg编写解码程序


第一部分

#include
#include
extern "C" {#include "libacformat/avformat.h"
}#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")static double FractionToDouble(AVRational fraction)//AVRational为分数表示&#xff0c;其目的为避免精度损失&#xff0c;分数表示为分子除以分母
{return fraction.den &#61;&#61; 0?0:(double)fraction.num/(double)fraction.den;
}int main(int argc,char *argv[])
{QCoreApplication a(argc,argv);const char* path &#61; "test.mp4";//初始化封装库av_register_all();//打开输入视频文件AVFormatContext *avfc &#61; NULL;int ret &#61; avformat_open_input(&avfc,path,NULL,NULL); //传二级指针一般是对指针进行赋值的时候if(ret !&#61; 0){qDebug()<<"OPen"<<path<<"failed!";exit(-1);}//获取流信息ret &#61; avformat_find_stream_info(avfc,NULL);if(ret < 0){qDebug()<<"avformat_find_stream_info_failed!";exit(-1);}//总时长-秒数qDebug()<<(avfc->duration / AV_TIME_BASE);//avfc->streamsqDebug()<<"streams number is:"<<avfc->nb_streams;for(int i &#61; 0; i < avfc->nb->streams;i&#43;&#43;){qDebug()<<"i is "<<i;AVStream* as &#61; avfc->streams[i];qDebug()<<as->codecpar->codec_id; //AV_CODEC_ID_H264(解码)qDebug()<<as->codecpar->format;qDebug();//音频 AVMEDIA_TYPE_AUDIOif(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_AUDIO){qDebug()<<i<<"audio info";qDebug()<<"sample_rate&#61;"<<as->codecpar->sample_rate;qDebug()<<"Channels&#61;"<<as->codecpar->channels;}//视频 AVMEDIA_TYPE_VIDEOelse if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO){qDebug()<<i<<"video info";qDebug()<<"width&#61;"<<as->codecpar->width;qDebug()<<"height&#61;"<<as->codecpar->height;//帧率fpsqDebug()<<"video fps&#61;"<<FractionToDouble(as->avg_frame_rate);}}return a.exec();
}

第二部分

#include
#include
extern "C" {#include "libacformat/avformat.h"#include "libavcodec/avcodec.h" //解码
}#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")static double FractionToDouble(AVRational fraction)
{return fraction.den &#61;&#61; 0?0:(double)fraction.num/(double)fraction.den;
}int main(int argc,char *argv[])
{QCoreApplication a(argc,argv);const char* path &#61; "test.mp4";av_register_all();//初始化解码器avcodec_register_all();//音视频索引&#xff0c;读取时可以区别音视频int videoStream &#61; 0; //int audioStream &#61; 1; //AVFormatContext *avfc &#61; NULL;int ret &#61; avformat_open_input(&avfc,path,NULL,NULL);if(ret !&#61; 0){qDebug()<<"OPen"<<path<<"failed!";exit(-1);}ret &#61; avformat_find_stream_info(avfc,NULL);if(ret < 0){qDebug()<<"avformat_find_stream_info_failed!";exit(-1);}qDebug()<<(avfc->duration / AV_TIME_BASE);//avfc->streamsqDebug()<<"streams number is:"<<avfc->nb_streams;for(int i &#61; 0; i < avfc->nb->streams;i&#43;&#43;){qDebug()<<"i is "<<i;AVStream* as &#61; avfc->streams[i];qDebug()<<as->codecpar->codec_id;qDebug()<<as->codecpar->format;qDebug();if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_AUDIO){audioStream &#61; i; //qDebug()<<"sample_rate&#61;"<<as->codecpar->sample_rate;qDebug()<<"Channels&#61;"<<as->codecpar->channels;}else if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO){videoStream &#61; i; //qDebug()<<"width&#61;"<<as->codecpar->width;qDebug()<<"height&#61;"<<as->codecpar->height;qDebug()<<"video fps&#61;"<<FractionToDouble(as->avg_frame_rate);}}//找到视频解码器&#xff0c;创建并打开解码器AVCodec* vcodec &#61; avcodec_find_decoder(avfc->streams[videoStream]->codecpar->code_id);if (vcodec){qDebug()<<"video Can&#39;t find the video codec";}//上下文AVCodecCOntext* vc &#61; avcodec_alloc_context3(vcodec);//参数ret &#61; avcodec_parameters_to_context(vc,avfc->streams[videoStream]->codecpar);if (ret < 0){qDebug()<<"video avcodec_parameters_to_context failed";}//8线程同时解码vc->thread_count &#61; 8;avcodec_open2(vc,NULL,NULL);if (ret < 0){qDebug()<<"video avcodec_open2 failed";}//找到音频解码器&#xff0c;创建并打开解码器AVCodec* acodec &#61; avcodec_find_decoder(avfc->streams[audiooStream]->codecpar->code_id);if (acodec){qDebug()<<"audio Can&#39;t find the video codec";}//上下文AVCodecCOntext* ac &#61; avcodec_alloc_context3(acodec);//参数ret &#61; avcodec_parameters_to_context(ac,avfc->streams[audiooStream]->codecpar);if (ret < 0){qDebug()<<"audio avcodec_parameters_to_context failed";}//8线程同时解码ac->thread_count &#61; 8;avcodec_open2(ac,NULL,NULL);if (ret < 0){qDebug()<<"audio accodec_open2 failed";}//Packet,FrameAVPacket* pkt &#61; av_packet_alloc(); //using av_packet_free()AVFrame* frame &#61; av_frame_alloc(); //using av_frame_free()ret &#61; 0;while(ret >&#61; 0){ret &#61; av_read_frame(avfc,pkt); //一帧帧的读qDebug()<<"pkt->size&#61;"<<pkt->size;qDebug()<<"pkt->pts&#61;"<<pkt->pts;qDebug()<<"pkt->dts&#61;"<<pkt->dts;//完成解码操作AVCodecContext* cc &#61; NULL;if(pkt->stream_index &#61;&#61; videoStream){qDebug()<<"视频数据";cc &#61; vc;}else if(pkt->stream_index &#61;&#61; audeoStream){qDebug()<<"音频数据";cc &#61; ac;}int ret2 &#61; avcodec_send_packet(cc,pkt);//以frame形式输出av_packet_unref(pkt);//引用计数-1if(ret2 !&#61; 0){qDebug<<"Send packet failed";}//接受framewhile(true){//有可能多处接受decode发过来的frameint ret3 &#61; avcodec_receive_frame(cc,frame);if(ret3 !&#61; 0){break;}qDebug<<"recv frame"<<frame->format<<" "<<frame->linesize[0];}}//释放资源av_frame_free(&frame);av_packet_free(&pkt);if(avfc){avformat_close_input(&avfc);}avfc &#61; NULL;return a.exec();
}


推荐阅读
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
author-avatar
mobiledu2502922617
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有