ffmpeg源码分析之媒体打开过程
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;
//忽略
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);
//读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的
//私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.
if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s, &ap)) < 0)
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;
}
下面分析init_input():
[cpp] view plaincopy?
//打开输入媒体并填充其AVInputFormat结构
static int init_input(AVFormatContext *s, const char *filename)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
//当调用者已指定了pb(数据取得的方式)--一般不会这样.
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))))
//如果已指定了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);
}
再看一下文件打开过程:
[cpp] view plaincopy?
//打开一个地址指向的媒体
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;
}
下面是探测函数
[cpp] view plaincopy?
int av_probe_input_buffer(AVIOContext *pb,
AVInputFormat **fmt,
const char *filename,
void *logctx,
unsigned int offset,
unsigned int max_probe_size)
{
AVProbeData pd = { filename ? filename : "", NULL, -offset };
unsigned char *buf = NULL;
int ret = 0, probe_size;
//计算最多探测数据的字节数
if (!max_probe_size) {
max_probe_size = PROBE_BUF_MAX;
} else if (max_probe_size > PROBE_BUF_MAX) {
max_probe_size = PROBE_BUF_MAX;
} else if (max_probe_size < PROBE_BUF_MIN) {
return AVERROR(EINVAL);
}
if (offset >= max_probe_size) {
return AVERROR(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))) {
int score = probe_size < max_probe_size ? AVPROBE_SCORE_MAX / 4 : 0;
int buf_offset = (probe_size == PROBE_BUF_MIN) ? 0 : probe_size >> 1;
void *buftmp;
if (probe_size < offset) {
continue;
}
/* read probe data */
//分配读取数据存放的缓冲
buftmp = av_realloc(buf, probe_size + AVPROBE_PADDING_SIZE);
if (!buftmp) {
av_free(buf);
return AVERROR(ENOMEM);
}
buf = buftmp;
//利用pb读数据到缓冲的剩余空间中
if ((ret = avio_read(pb, buf + buf_offset, probe_size - buf_offset))
< 0) {
/* fail if error was not end of file, otherwise, lower score */
if (ret != AVERROR_EOF) {
av_free(buf);
return ret;
}
score = 0;
ret = 0; /* error was end of file, nothing read */
}
pd.buf_size += ret;
pd.buf = &buf[offset];
//缓冲中没有数据的部分要清0
memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);
/* guess file format */
//从一个打开的文件只探测媒体格式
*fmt = av_probe_input_format2(&pd, 1, &score);
if (*fmt) {
if (score <= AVPROBE_SCORE_MAX / 4) { //this can only be true in the last iteration
av_log(
logctx,
AV_LOG_WARNING,
"Format %s detected only with low score of %d, misdetection possible!\n",
(*fmt)->name, score);
} else
av_log(logctx, AV_LOG_DEBUG,
"Format %s probed with size=%d and score=%d\n",
(*fmt)->name, probe_size, score);
}
//不成功,继续
}
if (!*fmt) {
av_free(buf);
return AVERROR_INVALIDDATA;
}
/* rewind. reuse probe buffer to avoid seeking */
//把探测时读入的数据保存到pb中,为的是真正读时直接利用之.
if ((ret = ffio_rewind_with_probe_data(pb, buf, pd.buf_size)) < 0)
av_free(buf);
return ret;
}
ffmpeg源码分析之媒体打开过程的更多相关文章
- 最新版ffmpeg源码分析
最新版ffmpeg源码分析一:框架 (ffmpeg v0.9) 框架 最新版的ffmpeg中发现了一个新的东西:avconv,而且ffmpeg.c与avconv.c一个模样,一研究才发现是libav下 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- Spring源码分析之`BeanFactoryPostProcessor`调用过程
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...
- Spring Ioc源码分析系列--Bean实例化过程(一)
Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...
- Spring Ioc源码分析系列--Bean实例化过程(二)
Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...
- 【Netty源码分析】发送数据过程
前面两篇博客[Netty源码分析]Netty服务端bind端口过程和[Netty源码分析]客户端connect服务端过程中我们分别介绍了服务端绑定端口和客户端连接到服务端的过程,接下来我们分析一下数据 ...
- 【Canal源码分析】parser工作过程
本文主要分析的部分是instance启动时,parser的一个启动和工作过程.主要关注的是AbstractEventParser的start()方法中的parseThread. 一.序列图 二.源码分 ...
- tomcat8 源码分析 | 组件及启动过程
tomcat 8 源码分析 ,本文主要讲解tomcat拥有哪些组件,容器,又是如何启动的 推荐访问我的个人网站,排版更好看呦: https://chenmingyu.top/tomcat-source ...
- MyBatis 源码分析 - SQL 的执行过程
* 本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析 ...
随机推荐
- layer快速点击会触发多次回调
场景还原 测试同学反馈点击了一次操作,为什么会有两条操作记录? 我:???? 排查思路 查看日志,看一下是不是发了两次请求,果不其然啊: 并发了,同一时间发送了两次请求,出现了脏写. 原因 系统的co ...
- mongodb 报错 not authorized on admin to execute command【 version 3.2.18 】
mongodb version 3.2.18 测试问题: 分析: 从报错内容上看是权限不够,但不明了为什么,因为已经使用的超级用户权限: { "_id" : "admin ...
- .net断点续传
IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头. 一. 两个必要响应头Accept-Ranges.ETag 客户端每次提交下载请求时,服务 ...
- Flutter 的url_launcher简介
url_launcher是用于在移动平台中启动URL的Flutter插件,适用于IOS和Android平台.他可以打开网页,发送邮件,还可以拨打电话. github地址:https://github. ...
- Flutter 底部的renderflex溢出
一开始直接使用Scaffold布局,body:new Column 然后模拟器会提示捕获异常: 然后百度了一下Flutter的溢出问题,发现解决办法是使用SingleChildScrollView包 ...
- useJDBC4ColumnNameAndLabelSemantics设置后无效,怎么办?
连接的是DB2数据库, 在查询语句中有SELECT COLUMNNAME AS ALIASNAME FROM TABLE这样的结构时, 会报如下错误: Caused by: com.ibm.db2.j ...
- C语言字符串之无重复字符的最长子串
题目描述 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 输入: "abcabcbb" 输出: 解释: 因为无重复字符的最长子串是 . 输入: " ...
- jdbc访问oracle超慢,但是PLSQL访问正常
oracle数据库连接非常慢,sqlplus很快,用客户端就很慢,十几秒才好.然后服务器内存一下就飙升到了90%,最开始以为是表空间占满了,数据库连接数占满了.折腾了半天,重启,还是很慢.应用连接数据 ...
- Python基础总结之第七天开始【总结字符串、列表、元组的常用方法】(新手可相互督促)
前面的笔记说,python中的一切数据类型都是对象 我们在细化下就是:对象可由两部分组成:对象数据和对象方法 针对不同类型的数据对象,有不同的操作对象的方法. 那么我们开始看下字符串对象的常用方法: ...
- UWP笔记-自定义Grid背景图片
之前写简单的UWP版本地音乐播放器,有自定义背景壁纸的功能,现在贴在这里回顾下. Page.xaml 页面,添加Grid <Grid x:Name="mainGrid"/&g ...