`

FFMPEG源码分析:avformat_open_input()(媒体打开函数)

 
阅读更多

本文分析了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);
}

 

 

avio_open

 

//打开一个地址指向的媒体
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;
}



 
 
[cpp]view plaincopy
 

 

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;
}

 

原文地址:http://wodamazi.iteye.com/blog/1293994

 

分享到:
评论

相关推荐

    avformat_open_input详解

    我自己写的avformat_open_input函数的解析,需要了解ffmpeg源码结构的可以看一下

    ffmpeg录制视频(需要安装x11grab)--C++编程

    4. **打开输入设备**:调用`avformat_open_input`以打开`x11grab`设备。如果成功,你可以获取到输入流并准备开始捕获屏幕内容。 5. **创建输出流**:根据需要的视频编码格式(如H.264)创建一个新的`AVStream`,...

    FFmpeg-QT实现摄像头rtsp实时显示

    3. **解析RTSP流**:使用FFmpeg的`avformat_open_input`函数打开RTSP流,并通过`avformat_find_stream_info`获取流的信息。这些信息包括视频和音频的编码格式、分辨率等。 4. **解码流**:使用`avcodec_find_...

    MFC 播放FFMPEG SDL视频所需库

    使用`avformat_open_input`函数打开RTSP视频流,并使用`avformat_find_stream_info`获取流信息: ```cpp AVFormatContext *iformat_ctx = NULL; if (avformat_open_input(&iformat_ctx, "rtsp://your_rtsp_url", ...

    FFmpeg-master.zip_FFmpeg-master_ffmpeg_ffmpeg 播放

    2. **打开输入文件**:使用 `avformat_open_input()` 函数打开视频文件。你需要提供文件路径,FFmpeg 将自动检测其格式。成功后,`avformat_find_stream_info()` 用于获取媒体文件的流信息,包括音频和视频轨道。 3...

    QT C++ ffmpeg 调用usb 摄像头显示 并录制 H264录制视频

    在该线程中,你可以使用FFmpeg的`avdevice_open_input()`函数打开USB摄像头,然后使用`avformat_find_stream_info()`获取流信息。 ```cpp AVFormatContext *fmtCtx = nullptr; if (avformat_open_input(&fmtCtx, ...

    ffmpeg.zip_FFmpeg 内存_JMdecode_ffmpeg open_opencv_ffmpeg_视频帧

    2. **打开和读取视频流**:通过 avformat_open_input() 打开视频文件,然后用 av_find_stream_info() 获取流信息。 3. **解码视频帧**:使用 av_read_frame() 读取包,avcodec_decode_video2() 或 avcodec_decode_...

    ffmpeg教程 C++ MFC VC

    - avformat_open_input()打开输入文件,avformat_find_stream_info()获取流信息,avio_open()创建IO上下文,av_read_frame()读取一帧。 6. **音视频过滤** - FFmpeg的libavfilter模块支持各种视觉效果和处理,如...

    ffmpeg探测网络流格式方法源码

    3. 使用FFmpeg的`avformat_open_input`或`avio_open`打开UDP流。 4. 调用`avformat_find_stream_info`来探测流的格式信息,此过程会分析接收到的数据包以识别其类型和编码格式。 5. 设置解码器,开始解码和播放流。 ...

    qt整合ffmpeg实现点击按钮调用ffmpeg解码视频转成一张张的图片

    首先,需要使用 FFmpeg 的 avformat_open_input 函数打开视频文件,接着调用 avformat_find_stream_info 获取流信息。在这个过程中,我们可能需要处理各种不同的视频编码格式,因此需要使用 avcodec_find_decoder ...

    qt_ffmpeg_rtsp_rtsp取流_qtffmpeg流媒体_qt+ffmpeg_QT_qt_ffmpeg_rtsp

    2. **打开RTSP流**: 使用`avformat_open_input()`函数指定RTSP URL,以打开连接到RTSP服务器的流。这需要一个`AVFormatContext`结构体,它是FFmpeg中的核心数据结构,包含了媒体流的所有信息。 3. **读取流信息**: ...

    QT + ffmpeg 播放 rtsp,rtmp,udp视频流

    3. **打开流**:根据不同的流类型,如`avformat_open_input()`函数打开RTSP、RTMP或UDP流。对于RTSP,需要设置相应的URL;RTMP则可能需要服务器地址和流名;UDP流需要指定接收的IP地址和端口。 4. **获取流信息**:...

    ffmpeg_dsl_camera.rar_55_ffmpeg_ffmpeg 64_ffmpeg camera_ffmpeg 库

    3. 打开设备:使用 `avformat_open_input()` 函数打开选定的摄像头设备,并通过 `avformat_find_stream_info()` 获取设备的流信息。 4. 分析流:对每个视频流,获取相应的编解码器上下文(`AVCodecContext`),然后...

    simplest_ffmpeg_player.zip_ffmpeg_ffmpeg player C++_ffmpeg 播放器_f

    2. **打开视频文件**:使用 `avformat_open_input()` 打开视频文件。这个函数会根据文件的扩展名和内部元数据选择合适的解封装器。接着,调用 `avformat_find_stream_info()` 获取视频的流信息,如流的数量、类型、...

    simplest_ffmpeg_player_2.zip_ffmpeg sdl_ffmpeg vc_ffmpeg 播放_ffmp

    4. **打开和解码 H264 文件**:使用 FFmpeg 的 avformat_open_input 函数打开视频文件,然后调用 avformat_find_stream_info 解析流信息。找到 H264 视频流后,分配并初始化解码器上下文,然后用 avcodec_decode_...

    Qt + FFmpeg RTSP视频流解码,延迟0.2s以内!

    2. **打开RTSP流**:使用`avformat_open_input()`函数连接到RTSP服务器并打开流。需要提供URL和相应的选项,如用户名和密码。 3. **查找流信息**:调用`avformat_find_stream_info()`来获取流的详细信息,包括编码...

    simplest_ffmpeg_decoder.zip_FFmpeg解码_ffmpeg_ffmpeg visual c_ffmp

    在“simplest_ffmpeg_decoder”中,开发者可能首先会使用`avformat_open_input`打开输入视频文件,然后调用`avformat_find_stream_info`获取文件的流信息,包括流的数量、类型和编码格式。接着,通过`av_find_best_...

    FFmpeg_录屏_编码_转码

    开发者可以使用 `avformat_open_input` 和 `avformat_find_stream_info` 函数来打开和获取屏幕设备的信息,然后通过 `av_read_frame` 获取每一帧图像,最后用编码器编码成 H.264 或其他格式的视频流。 3. **编码**...

    simplest_ffmpeg_transcoder.rar_ffmpeg C++builder

    2. 打开输入文件:使用`avformat_open_input()`打开输入视频文件,并通过`avformat_find_stream_info()`获取文件的流信息。 3. 分析流:`avformat_find_stream_info()`会分析文件,找到所有的音视频流,分配对应的...

    sunwukong.rar_ffmpeg_ffmpeg例子_ffmpeg转码_转码

    2. **打开输入文件**:使用`avformat_open_input`函数打开待转码的多媒体文件。这个函数会识别文件的格式并加载相应的解封装器。 3. **读取流信息**:`avformat_find_stream_info`函数用于获取输入文件的流信息,...

Global site tag (gtag.js) - Google Analytics