- 浏览: 952589 次
文章分类
最新评论
FFMPEG源码分析之媒体打开过程
avformat_open_input
- //参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,
- //会返回一个AVFormatContext的实例.
- //参数filename是媒体文件名或URL.
- //参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以
- //传入一个调用者定义的inputFormat,对应命令行中的-fxxx段,如果指定了它,
- //在打开文件中就不会探测文件的实际格式了,以它为准了.
- //参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入
- //特殊的操作参数而建的,为了了解流程,完全可以无视它.
- intavformat_open_input(AVFormatContext**ps,
- constchar*filename,
- AVInputFormat*fmt,
- AVDictionary**options)
- {
- AVFormatContext*s=*ps;
- intret=0;
- AVFormatParametersap={{0}};
- AVDictionary*tmp=NULL;
- //创建上下文结构
- if(!s&&!(s=avformat_alloc_context()))
- returnAVERROR(ENOMEM);
- //如果用户指定了输入格式,直接使用它
- if(fmt)
- s->iformat=fmt;
- //忽略
- if(options)
- av_dict_copy(&tmp,*options,0);
- if((ret=av_opt_set_dict(s,&tmp))<0)
- gotofail;
- //打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如
- //AVIOContext,AVInputFormat等等
- if((ret=init_input(s,filename))<0)
- gotofail;
- //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它
- //把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格
- //式进行分析,也就是说pb在底层,iformat在上层.
- //很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式
- //名为image2.此处还不是很了解具体细节,作不得准哦.
- /*checkfilenameincaseanimagenumberisexpected*/
- if(s->iformat->flags&AVFMT_NEEDNUMBER){
- if(!av_filename_number_test(filename)){
- ret=AVERROR(EINVAL);
- gotofail;
- }
- }
- s->duration=s->start_time=AV_NOPTS_VALUE;
- //上下文中保存下文件名
- av_strlcpy(s->filename,filename,sizeof(s->filename));
- /*allocateprivatedata*/
- //为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构.
- //此结构的大小在定义AVInputFormat时已指定了.
- if(s->iformat->priv_data_size>0){
- if(!(s->priv_data=av_mallocz(s->iformat->priv_data_size))){
- ret=AVERROR(ENOMEM);
- gotofail;
- }
- //这个可以先不必管它
- if(s->iformat->priv_class){
- *(constAVClass**)s->priv_data=s->iformat->priv_class;
- av_opt_set_defaults(s->priv_data);
- if((ret=av_opt_set_dict(s->priv_data,&tmp))<0)
- gotofail;
- }
- }
- /*e.g.AVFMT_NOFILEformatswillnothaveaAVIOContext*/
- //从mp3文件中读ID3数据并保存之.
- if(s->pb)
- ff_id3v2_read(s,ID3v2_DEFAULT_MAGIC);
- //读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的
- //私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.
- if(!(s->flags&AVFMT_FLAG_PRIV_OPT)&&s->iformat->read_header)
- if((ret=s->iformat->read_header(s,&ap))<0)
- gotofail;
- //保存数据区开始的位置
- if(!(s->flags&AVFMT_FLAG_PRIV_OPT)&&s->pb&&!s->data_offset)
- s->data_offset=avio_tell(s->pb);
- s->raw_packet_buffer_remaining_size=RAW_PACKET_BUFFER_SIZE;
- if(options){
- av_dict_free(options);
- *options=tmp;
- }
- *ps=s;
- //执行成功
- return0;
- //执行失败
- fail:av_dict_free(&tmp);
- if(s->pb&&!(s->flags&AVFMT_FLAG_CUSTOM_IO))
- avio_close(s->pb);
- avformat_free_context(s);
- *ps=NULL;
- returnret;
- }
init_input
- //打开输入媒体并填充其AVInputFormat结构
- staticintinit_input(AVFormatContext*s,constchar*filename)
- {
- intret;
- AVProbeDatapd={filename,NULL,0};
- //当调用者已指定了pb(数据取得的方式)--一般不会这样.
- if(s->pb){
- s->flags|=AVFMT_FLAG_CUSTOM_IO;
- if(!s->iformat)
- //如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat.
- returnav_probe_input_buffer(s->pb,&s->iformat,filename,s,0,0);
- elseif(s->iformat->flags&AVFMT_NOFILE)
- //如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了,
- //此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错.
- av_log(s,AV_LOG_WARNING,"CustomAVIOContextmakesnosenseand"
- "willbeignoredwithAVFMT_NOFILEformat.\n");
- return0;
- }
- //一般会执行到这里
- if((s->iformat&&s->iformat->flags&AVFMT_NOFILE)
- ||(!s->iformat&&(s->iformat=av_probe_input_format(&pd,0))))
- //如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回
- //如果没指定iformat,但是可以从文件名中猜出iformat,也成功.
- return0;
- //如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件
- if((ret=avio_open(&s->pb,filename,AVIO_FLAG_READ))<0)
- returnret;
- if(s->iformat)
- return0;
- //再探测之
- returnav_probe_input_buffer(s->pb,&s->iformat,filename,s,0,0);
- }
- //打开一个地址指向的媒体
- intavio_open(AVIOContext**s,constchar*filename,intflags)
- {
- //URLContext代表一个URL地址指向的媒体文件,本地路径也算一种.它封装了
- //操作一个媒体文件的相关数据,最重要的是prot变量,是URLProtocol型的.
- //prot代表一个特定的协义和协议操作函数们,URLContext包含不同的prot,
- //就可以通过URLContext使用不同的协议读写媒体数据,比如tcp,http,本地
- //文件用file协议.
- URLContext*h;
- interr;
- //创建并初始化URLContext,其prot通过文件名确定.然后打开这个媒体文件
- err=ffurl_open(&h,filename,flags);
- if(err<0)
- returnerr;
- //其实文件已经在上边真正打开了.这里只是填充AVIOContext.使它记录下
- //URLContext,以及填充读写数据的函数指针.
- err=ffio_fdopen(s,h);
- if(err<0){
- ffurl_close(h);
- returnerr;
- }
- return0;
- }
av_probe_input_buffer
- intav_probe_input_buffer(AVIOContext*pb,
- AVInputFormat**fmt,
- constchar*filename,
- void*logctx,
- unsignedintoffset,
- unsignedintmax_probe_size)
- {
- AVProbeDatapd={filename?filename:"",NULL,-offset};
- unsignedchar*buf=NULL;
- intret=0,probe_size;
- //计算最多探测数据的字节数
- if(!max_probe_size){
- max_probe_size=PROBE_BUF_MAX;
- }elseif(max_probe_size>PROBE_BUF_MAX){
- max_probe_size=PROBE_BUF_MAX;
- }elseif(max_probe_size<PROBE_BUF_MIN){
- returnAVERROR(EINVAL);
- }
- if(offset>=max_probe_size){
- returnAVERROR(EINVAL);
- }
- //循环直到探测完指定的数据
- for(probe_size=PROBE_BUF_MIN;
- probe_size<=max_probe_size&&!*fmt;
- probe_size=
- FFMIN(probe_size<<1,FFMAX(max_probe_size,probe_size+1))){
- intscore=probe_size<max_probe_size?AVPROBE_SCORE_MAX/4:0;
- intbuf_offset=(probe_size==PROBE_BUF_MIN)?0:probe_size>>1;
- void*buftmp;
- if(probe_size<offset){
- continue;
- }
- /*readprobedata*/
- //分配读取数据存放的缓冲
- buftmp=av_realloc(buf,probe_size+AVPROBE_PADDING_SIZE);
- if(!buftmp){
- av_free(buf);
- returnAVERROR(ENOMEM);
- }
- buf=buftmp;
- //利用pb读数据到缓冲的剩余空间中
- if((ret=avio_read(pb,buf+buf_offset,probe_size-buf_offset))
- <0){
- /*failiferrorwasnotendoffile,otherwise,lowerscore*/
- if(ret!=AVERROR_EOF){
- av_free(buf);
- returnret;
- }
- score=0;
- ret=0;/*errorwasendoffile,nothingread*/
- }
- pd.buf_size+=ret;
- pd.buf=&buf[offset];
- //缓冲中没有数据的部分要清0
- memset(pd.buf+pd.buf_size,0,AVPROBE_PADDING_SIZE);
- /*guessfileformat*/
- //从一个打开的文件只探测媒体格式
- *fmt=av_probe_input_format2(&pd,1,&score);
- if(*fmt){
- if(score<=AVPROBE_SCORE_MAX/4){//thiscanonlybetrueinthelastiteration
- av_log(
- logctx,
- AV_LOG_WARNING,
- "Format%sdetectedonlywithlowscoreof%d,misdetectionpossible!\n",
- (*fmt)->name,score);
- }else
- av_log(logctx,AV_LOG_DEBUG,
- "Format%sprobedwithsize=%dandscore=%d\n",
- (*fmt)->name,probe_size,score);
- }
- //不成功,继续
- }
- if(!*fmt){
- av_free(buf);
- returnAVERROR_INVALIDDATA;
- }
- /*rewind.reuseprobebuffertoavoidseeking*/
- //把探测时读入的数据保存到pb中,为的是真正读时直接利用之.
- if((ret=ffio_rewind_with_probe_data(pb,buf,pd.buf_size))<0)
- av_free(buf);
- returnret;
- }
avformat_open_input
- //参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,
- //会返回一个AVFormatContext的实例.
- //参数filename是媒体文件名或URL.
- //参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以
- //传入一个调用者定义的inputFormat,对应命令行中的-fxxx段,如果指定了它,
- //在打开文件中就不会探测文件的实际格式了,以它为准了.
- //参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入
- //特殊的操作参数而建的,为了了解流程,完全可以无视它.
- intavformat_open_input(AVFormatContext**ps,
- constchar*filename,
- AVInputFormat*fmt,
- AVDictionary**options)
- {
- AVFormatContext*s=*ps;
- intret=0;
- AVFormatParametersap={{0}};
- AVDictionary*tmp=NULL;
- //创建上下文结构
- if(!s&&!(s=avformat_alloc_context()))
- returnAVERROR(ENOMEM);
- //如果用户指定了输入格式,直接使用它
- if(fmt)
- s->iformat=fmt;
- //忽略
- if(options)
- av_dict_copy(&tmp,*options,0);
- if((ret=av_opt_set_dict(s,&tmp))<0)
- gotofail;
- //打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如
- //AVIOContext,AVInputFormat等等
- if((ret=init_input(s,filename))<0)
- gotofail;
- //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它
- //把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格
- //式进行分析,也就是说pb在底层,iformat在上层.
- //很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式
- //名为image2.此处还不是很了解具体细节,作不得准哦.
- /*checkfilenameincaseanimagenumberisexpected*/
- if(s->iformat->flags&AVFMT_NEEDNUMBER){
- if(!av_filename_number_test(filename)){
- ret=AVERROR(EINVAL);
- gotofail;
- }
- }
- s->duration=s->start_time=AV_NOPTS_VALUE;
- //上下文中保存下文件名
- av_strlcpy(s->filename,filename,sizeof(s->filename));
- /*allocateprivatedata*/
- //为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构.
- //此结构的大小在定义AVInputFormat时已指定了.
- if(s->iformat->priv_data_size>0){
- if(!(s->priv_data=av_mallocz(s->iformat->priv_data_size))){
- ret=AVERROR(ENOMEM);
- gotofail;
- }
- //这个可以先不必管它
- if(s->iformat->priv_class){
- *(constAVClass**)s->priv_data=s->iformat->priv_class;
- av_opt_set_defaults(s->priv_data);
- if((ret=av_opt_set_dict(s->priv_data,&tmp))<0)
- gotofail;
- }
- }
- /*e.g.AVFMT_NOFILEformatswillnothaveaAVIOContext*/
- //从mp3文件中读ID3数据并保存之.
- if(s->pb)
- ff_id3v2_read(s,ID3v2_DEFAULT_MAGIC);
- //读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的
- //私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.
- if(!(s->flags&AVFMT_FLAG_PRIV_OPT)&&s->iformat->read_header)
- if((ret=s->iformat->read_header(s,&ap))<0)
- gotofail;
- //保存数据区开始的位置
- if(!(s->flags&AVFMT_FLAG_PRIV_OPT)&&s->pb&&!s->data_offset)
- s->data_offset=avio_tell(s->pb);
- s->raw_packet_buffer_remaining_size=RAW_PACKET_BUFFER_SIZE;
- if(options){
- av_dict_free(options);
- *options=tmp;
- }
- *ps=s;
- //执行成功
- return0;
- //执行失败
- fail:av_dict_free(&tmp);
- if(s->pb&&!(s->flags&AVFMT_FLAG_CUSTOM_IO))
- avio_close(s->pb);
- avformat_free_context(s);
- *ps=NULL;
- returnret;
- }
init_input
- //打开输入媒体并填充其AVInputFormat结构
- staticintinit_input(AVFormatContext*s,constchar*filename)
- {
- intret;
- AVProbeDatapd={filename,NULL,0};
- //当调用者已指定了pb(数据取得的方式)--一般不会这样.
- if(s->pb){
- s->flags|=AVFMT_FLAG_CUSTOM_IO;
- if(!s->iformat)
- //如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat.
- returnav_probe_input_buffer(s->pb,&s->iformat,filename,s,0,0);
- elseif(s->iformat->flags&AVFMT_NOFILE)
- //如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了,
- //此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错.
- av_log(s,AV_LOG_WARNING,"CustomAVIOContextmakesnosenseand"
- "willbeignoredwithAVFMT_NOFILEformat.\n");
- return0;
- }
- //一般会执行到这里
- if((s->iformat&&s->iformat->flags&AVFMT_NOFILE)
- ||(!s->iformat&&(s->iformat=av_probe_input_format(&pd,0))))
- //如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回
- //如果没指定iformat,但是可以从文件名中猜出iformat,也成功.
- return0;
- //如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件
- if((ret=avio_open(&s->pb,filename,AVIO_FLAG_READ))<0)
- returnret;
- if(s->iformat)
- return0;
- //再探测之
- returnav_probe_input_buffer(s->pb,&s->iformat,filename,s,0,0);
- }
- //打开一个地址指向的媒体
- intavio_open(AVIOContext**s,constchar*filename,intflags)
- {
- //URLContext代表一个URL地址指向的媒体文件,本地路径也算一种.它封装了
- //操作一个媒体文件的相关数据,最重要的是prot变量,是URLProtocol型的.
- //prot代表一个特定的协义和协议操作函数们,URLContext包含不同的prot,
- //就可以通过URLContext使用不同的协议读写媒体数据,比如tcp,http,本地
- //文件用file协议.
- URLContext*h;
- interr;
- //创建并初始化URLContext,其prot通过文件名确定.然后打开这个媒体文件
- err=ffurl_open(&h,filename,flags);
- if(err<0)
- returnerr;
- //其实文件已经在上边真正打开了.这里只是填充AVIOContext.使它记录下
- //URLContext,以及填充读写数据的函数指针.
- err=ffio_fdopen(s,h);
- if(err<0){
- ffurl_close(h);
- returnerr;
- }
- return0;
- }
av_probe_input_buffer
- intav_probe_input_buffer(AVIOContext*pb,
- AVInputFormat**fmt,
- constchar*filename,
- void*logctx,
- unsignedintoffset,
- unsignedintmax_probe_size)
- {
- AVProbeDatapd={filename?filename:"",NULL,-offset};
- unsignedchar*buf=NULL;
- intret=0,probe_size;
- //计算最多探测数据的字节数
- if(!max_probe_size){
- max_probe_size=PROBE_BUF_MAX;
- }elseif(max_probe_size>PROBE_BUF_MAX){
- max_probe_size=PROBE_BUF_MAX;
- }elseif(max_probe_size<PROBE_BUF_MIN){
- returnAVERROR(EINVAL);
- }
- if(offset>=max_probe_size){
- returnAVERROR(EINVAL);
- }
- //循环直到探测完指定的数据
- for(probe_size=PROBE_BUF_MIN;
- probe_size<=max_probe_size&&!*fmt;
- probe_size=
- FFMIN(probe_size<<1,FFMAX(max_probe_size,probe_size+1))){
- intscore=probe_size<max_probe_size?AVPROBE_SCORE_MAX/4:0;
- intbuf_offset=(probe_size==PROBE_BUF_MIN)?0:probe_size>>1;
- void*buftmp;
- if(probe_size<offset){
- continue;
- }
- /*readprobedata*/
- //分配读取数据存放的缓冲
- buftmp=av_realloc(buf,probe_size+AVPROBE_PADDING_SIZE);
- if(!buftmp){
- av_free(buf);
- returnAVERROR(ENOMEM);
- }
- buf=buftmp;
- //利用pb读数据到缓冲的剩余空间中
- if((ret=avio_read(pb,buf+buf_offset,probe_size-buf_offset))
- <0){
- /*failiferrorwasnotendoffile,otherwise,lowerscore*/
- if(ret!=AVERROR_EOF){
- av_free(buf);
- returnret;
- }
- score=0;
- ret=0;/*errorwasendoffile,nothingread*/
- }
- pd.buf_size+=ret;
- pd.buf=&buf[offset];
- //缓冲中没有数据的部分要清0
- memset(pd.buf+pd.buf_size,0,AVPROBE_PADDING_SIZE);
- /*guessfileformat*/
- //从一个打开的文件只探测媒体格式
- *fmt=av_probe_input_format2(&pd,1,&score);
- if(*fmt){
- if(score<=AVPROBE_SCORE_MAX/4){//thiscanonlybetrueinthelastiteration
- av_log(
- logctx,
- AV_LOG_WARNING,
- "Format%sdetectedonlywithlowscoreof%d,misdetectionpossible!\n",
- (*fmt)->name,score);
- }else
- av_log(logctx,AV_LOG_DEBUG,
- "Format%sprobedwithsize=%dandscore=%d\n",
- (*fmt)->name,probe_size,score);
- }
- //不成功,继续
- }
- if(!*fmt){
- av_free(buf);
- returnAVERROR_INVALIDDATA;
- }
- /*rewind.reuseprobebuffertoavoidseeking*/
- //把探测时读入的数据保存到pb中,为的是真正读时直接利用之.
- if((ret=ffio_rewind_with_probe_data(pb,buf,pd.buf_size))<0)
- av_free(buf);
- returnret;
- }
相关推荐
FFMPEG源码深入解析, 墙裂推荐, 深入理解不容错过. FFMPEG源码深入解析, 墙裂推荐, 深入理解不容错过.
ffmpeg源码包编译安装过程, 详细讲述了ffmpeg及其相关音视频编解码库的源码编译安装过程
作者裁剪了ffplay, 只留下AVI解码播放, 详细分析了代码结构. 是学习ffmpeg不可多得的好资料. 源码包里有需要的库, 可直接使用vs编译,调试. 目前试过VS2013, 可以编译,调试. 其他版本没试过
ffmpeg源码,用于学习ffmpeg的实现架构和代码分析。
ffmpeg源码
当今所知的最完整的FFMPEG教程。 看完可以完全掌握FFMPEG。 当今所知的最完整的FFMPEG教程。 看完可以完全掌握FFMPEG。
ffmpeg 源码 流媒体 ,自己看。。
作者裁剪了一下ffplay, 只留下AVI格式解码播放, 详细分析了代码结构, 是学习ffmpeg不可多得的好资料
ffmpeg源码和编译版都在包里
ffmpeg源码优化解码和输出,支持移动端、电脑端各种设备的编译脚本。
ffmpeg源代码及编译方法,在windows下编译通过并结合媒体栈投入使用,运行稳定
文档很详细的对ffmpeg源码进行了讲解,由浅入深,很适合刚接触ffmpeg源码,又不知如何下手的人员
作者共享好资料。点赞!别人要10分,我这儿不要分。免费下。本书深入FFMPEG,详细介绍了FFPLAY播放的流程,以AVI文件为例,从解复用到解码,最后到SDL显示的整个过程,非常有参考价值。
FFMpeg 源码,解决Github访问问题
FFmpeg源码,是一个很不错的Android代码,有兴趣的伙伴们抽时间可以看一下把。
FFMpeg之FFMplay源码分析.rar
ffmpeg源码windows上编译的工具打包,有些工具比较难找,因此在此打包处理
ffmpeg源码下载 ffmpeg-2.0.2.tar.bz2
FFmpeg是音视频领域的最重要的开源软件,养活了一大票国产播放器(暴风,QQPlayer)
ffmpeg源码和相关工具文档ffmpeg源码和相关工具文档ffmpeg源码和相关工具文档ffmpeg源码和相关工具文档ffmpeg源码和相关工具文档