ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
来源:互联网 发布:dcs编程软件 编辑:程序博客网 时间:2024/06/05 11:54
在avformat_open_input中,有一个 init_input函数,它的作用是打开输入媒体,初始化所有与媒体读写有关的结构们,例如/AVIOContext,AVInputFormat等等。分析init_input函数,找出AVIOContext的初始化过程。以下对于init_input函数的分析代码摘自 http://blog.csdn.NET/nkmnkm/article/details/7043241,表示感谢!
补充一下:
[cpp] view plain copy
//参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,
//会返回一个AVFormatContext的实例.
//参数filename是媒体文件名或URL.
//参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以
//传入一个调用者定义的inputFormat,对应命令行中的 -f xxx段,如果指定了它,
//在打开文件中就不会探测文件的实际格式了,以它为准了.
//参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入
//特殊的操作参数而建的, 为了了解流程,完全可以无视它.
int avformat_open_input(AVFormatContext **ps,
const char *filename,
AVInputFormat *fmt,
AVDictionary **options)
{
AVFormatContext *s = *ps;
int ret = 0;
AVFormatParameters ap = { { 0 } };
AVDictionary *tmp = NULL;
//创建上下文结构 if (!s && !(s = avformat_alloc_context())) //可以在函数外创建 return AVERROR(ENOMEM); //如果用户指定了输入格式,直接使用它 if (fmt) s->iformat = fmt; //输入格式可通过AVProbeData(包含文件名/buffer)传入av_probe_input_format函数来获得。 //忽略 if (options) av_dict_copy(&tmp, *options, 0); if ((ret = av_opt_set_dict(s, &tmp)) < 0) goto fail; //打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如 //AVIOContext,AVInputFormat等等 if ((ret = init_input(s, filename)) < 0) //后面分析该函数。 goto fail; //执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它 //把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格 //式进行分析,也就是说pb在底层,iformat在上层. //很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式 //名为image2.此处还不是很了解具体细节,作不得准哦. /* check filename in case an image number is expected */ if (s->iformat->flags & AVFMT_NEEDNUMBER) { if (!av_filename_number_test(filename)) { ret = AVERROR(EINVAL); goto fail; } } s->duration = s->start_time = AV_NOPTS_VALUE; //上下文中保存下文件名 av_strlcpy(s->filename, filename, sizeof(s->filename)); /* allocate private data */ //为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构. //此结构的大小在定义AVInputFormat时已指定了. if (s->iformat->priv_data_size > 0) { if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) { ret = AVERROR(ENOMEM); goto fail; } //这个可以先不必管它 if (s->iformat->priv_class) { *(const AVClass**) 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) goto fail; } } /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */ //从mp3文件中读ID3数据并保存之. if (s->pb) ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC); //ID3v2是某些MP3文件的标签帧,包含歌曲的某些信息。是ID3v1(尾部128byte)的扩展。 //读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的 //私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等. //当mp3文件时,调用ff_mp3_demuxer的read_header() 。在里面检测媒体信息,如果没有则说明没有id3v2头,跳转到尾部,去读id3v1信息。 if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header) if ((ret = s->iformat->read_header(s, &ap)) < 0)//内部使用一系列函数,分析mp3头部信息 goto fail; //保存数据区开始的位置 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; //执行成功 return 0; //执行失败 fail: av_dict_free(&tmp); if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO)) avio_close(s->pb); avformat_free_context(s); *ps = NULL; return ret;
}
分析ff_mp3_demuxer的read_header()函数
ffmpeg先读取id3v2,读不到则跳转至距离文件尾部128字节处读取id3v1.
[cpp] view plain copy
static int mp3_read_header(AVFormatContext *s,
AVFormatParameters *ap)
{
AVStream *st;
int64_t off;
st = av_new_stream(s, 0); if (!st) return AVERROR(ENOMEM); st->codec->codec_type = AVMEDIA_TYPE_AUDIO; st->codec->codec_id = CODEC_ID_MP3; st->need_parsing = AVSTREAM_PARSE_FULL; st->start_time = 0; // lcm of all mp3 sample rates av_set_pts_info(st, 64, 1, 14112000); off = avio_tell(s->pb); //没有必须的媒体信息(无id3v2头或者损坏),则读id3v1信息 if (!av_dict_get(s->metadata, "", NULL, AV_DICT_IGNORE_SUFFIX)) ff_id3v1_read(s); if (mp3_parse_vbr_tags(s, st, off) < 0) //分析如果是vbr格式,计算时长。cbr和vbr的时长计算有相关文章。 avio_seek(s->pb, off, SEEK_SET); /* the parameters will be extracted from the compressed bitstream */ return 0;
}
[cpp] view plain copy
//打开输入媒体并填充其AVInputFormat结构
static int init_input(AVFormatContext *s, const char *filename)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
//当调用者已指定了pb(数据取得的方式)--一般不会这样.除非自定义AVIOContext,不使用file或者pipe if (s->pb) { s->flags |= AVFMT_FLAG_CUSTOM_IO; //自定义输入源,设置自定义标志 if (!s->iformat) //如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat. return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0); else if (s->iformat->flags & AVFMT_NOFILE) //如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了, //此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错. av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and " "will be ignored with AVFMT_NOFILE format.\n"); return 0; } //一般会执行到这里 if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) || (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))//根据AVProbeData的内容来获取格式。可以根据文件名或者 //buffer数据。内部首先匹配demuxer的AVInputformat全局变量,然后调用其read_probe成员。该函数在对应格式中初始化,读格式信息 //如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回 //如果没指定iformat,但是可以从文件名中猜出iformat,也成功. return 0; //如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件 if ((ret = avio_open(&s->pb, filename, AVIO_FLAG_READ)) < 0) return ret; if (s->iformat) return 0; //再探测之 return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0); }
通过以上分析,看出当用户不提供pb即AVIOContext,需要文件,并且根据文件无法猜出iformat的时候,就要打开文件进行探测。此处有一个疑问:当从文件名中猜出iformat时,此时pb是否也被初始化?因为有文件就肯定需要pb的。没有读源码,此时暂不需要。
依然回来寻找AVIOContext的初始化函数,找到avio_open函数,输入参数是s->pb,可能在里面对其初始化,分析之。
[cpp] view plain copy
//打开一个地址指向的媒体
int avio_open(AVIOContext **s, const char *filename, int flags)
{
//URLContext代表一个URL地址指向的媒体文件,本地路径也算一种.它封装了
//操作一个媒体文件的相关数据,最重要的是prot变量,是URLProtocol型的.
//prot代表一个特定的协义和协议操作函数们,URLContext包含不同的prot,
//就可以通过URLContext使用不同的协议读写媒体数据,比如tcp,http,本地
//文件用file协议.
URLContext *h;
int err;
//创建并初始化URLContext,其prot通过文件名确定.然后打开这个媒体文件 err = ffurl_open(&h, filename, flags); if (err < 0) return err; //其实文件已经在上边真正打开了.这里只是填充AVIOContext.使它记录下 //URLContext,以及填充读写数据的函数指针. err = ffio_fdopen(s, h); if (err < 0) { ffurl_close(h); return err; } return 0;
}
找到函数ffio_fdopen,第一个参数s。分析函数ffio_fdopen。
[cpp] view plain copy
int ffio_fdopen(AVIOContext **s, URLContext *h)
{
uint8_t *buffer; int buffer_size, max_packet_size; max_packet_size = h->max_packet_size; if (max_packet_size) { buffer_size = max_packet_size; /* no need to bufferize more than one packet */ } else { buffer_size = IO_BUFFER_SIZE; } //创建buffer缓冲 用来初始化s buffer = av_malloc(buffer_size); if (!buffer) return AVERROR(ENOMEM); //为s分配内存空间 *s = av_mallocz(sizeof(AVIOContext)); if(!*s) { av_free(buffer); return AVERROR(ENOMEM); } //初始化AVIOContext if (ffio_init_context(*s, buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, (void*)ffurl_read, (void*)ffurl_write, (void*)ffurl_seek) < 0) { av_free(buffer); av_freep(s); return AVERROR(EIO); }
if FF_API_OLD_AVIO
(*s)->is_streamed = h->is_streamed;
endif
(*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; (*s)->max_packet_size = max_packet_size; if(h->prot) { (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause; (*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek; } return 0;
}
找到初始化函数ffio_init_context,分析。
[cpp] view plain copy
nt ffio_init_context(AVIOContext *s,
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
s->buffer = buffer;
s->buffer_size = buffer_size;
s->buf_ptr = buffer;
s->opaque = opaque;
url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
s->write_packet = write_packet;
s->read_packet = read_packet;
s->seek = seek;
s->pos = 0;
s->must_flush = 0;
s->eof_reached = 0;
s->error = 0;
if FF_API_OLD_AVIO
s->is_streamed = 0;
endif
s->seekable = AVIO_SEEKABLE_NORMAL; s->max_packet_size = 0; s->update_checksum= NULL; if(!read_packet && !write_flag){ s->pos = buffer_size; s->buf_end = s->buffer + buffer_size; } s->read_pause = NULL; s->read_seek = NULL; return 0;
}
对AVIOContex的初始化,ffmpeg提供了另外一个api,
[cpp] view plain copy
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence))
{
AVIOContext *s = av_mallocz(sizeof(AVIOContext));
if (!s)
return NULL;
ffio_init_context(s, buffer, buffer_size, write_flag, opaque,
read_packet, write_packet, seek);
return s;
}
根据以上分析,知道创建并初始化AVIOContext的步骤:av_mallocz(sizeof(AVIOContext)) ——–> ffio_init_context.
可以自定义回调函数,buffer缓冲区来进行初始化。后面再记录完整测试过程
- ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
- ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
- ffmepg框架阅读之三:AVIOContext的标准初始化
- avio_open2初始化AVIOContext
- DirectFB源代码阅读(二)初始化 .
- SparkContext初始化源码阅读笔记
- FFmpeg 初始化
- 使用ffmpeg库的初始化过程
- nginx启动初始化过程(二)
- CI框架源码阅读---------系统初始化文件
- 学习笔记---对象初始化过程
- zhphpframework框架(二) 框架初始化 init.php
- vector源码阅读笔记(初始化)
- Spark源码阅读笔记:DriverProgram初始化
- ijkplayer阅读笔记01-初始化操作
- Nginx 源码阅读笔记5 初始化 cycle
- 初始化过程
- 初始化过程
- 网络配置
- Spring中@Transactional用法深度分析之一
- Nginx+Keepalived 主备高可用 安装与配置
- [Java]去掉字符串中空格的几种方法
- 架构图积累
- ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
- C#实现发送短信到手机
- 百度地图定位的几个问题
- SpringMVC笔记(二)
- 使用URL读取网页内容
- 创建redis集群报错 /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load suc
- 端口调研
- JS正则表达式使用验证账号、手机号、电话和邮箱
- SQL 约束 (Constraints)