在 ffmpeg.exe 里面打开输入输出文件,是由 open_files() 函数来完成的,如下:

open_files() 函数的定义如下:

static int open_files(OptionGroupList *l, const char *inout,int (*open_file)(OptionsContext*, const char*))

参数解析如下:

1,OptionGroupList *l,输入/输出文件信息列表,

octx.groups[GROUP_INFILE]octx.groups[GROUP_OUTFILE]OptionParseContext octx 是在 split_commandline() 里面提前处理好的。

2,const char *inout,标记字符,用来输出日志的。

3,int (*open_file)(OptionsContext*, const char*),函数指针,指向真正的打开输入文件函数,或者是打开输出文件的函数。


open_files() 函数的流程如下:

open_files() 函数的逻辑就是 for 循环遍历 OptionGroupList 列表,OptionGroupList->nb_groups 就是文件的数量,如果命令行指定有 3 个输入文件,OptionGroupList->nb_groups 就等于 3

init_options() 函数是 初始化 OptionsContext o 的,设置一些字段的默认值。

然后调 parse_optfroup() 函数把 OptionGroup g 里面的参数解析到 OptionsContext o 结构,然后用 open_file() 函数打开文件。

open_file 是一个函数指针,可能指向 open_input_file() ,也可能指向 open_output_file()


我们需要先学习一下 struct OptionsContext 的结构,OptionsContext 保存的是单个输入或者输出文件的信息。

据本人统计,OptionsContext 里面的字段有50个以上,非常庞大,如下:

typedef struct OptionsContext {OptionGroup *g;/* input/output options */int64_t start_time;int64_t start_time_eof;int seek_timestamp;const char *format;SpecifierOpt *codec_names;int        nb_codec_names;SpecifierOpt *audio_channels;int        nb_audio_channels;...省略...
}

这 50 多个字段可以分为 3 种类型。

1,OptionGroup *gOptionsContext 本身就是从 OptionGroup 里面的 Option 解析过来,这里又保存一下 OptionGroup ,是因为要用到里面的 codec_opts ,format_opts,这些组件库相关的参数,是没有解析到 OptionsContext 里面的。

2,int64_t start_time ,int seek_timestampconst char *format,这些,我把他们称为单字段,都是普通类型。

3,SpecifierOpt数组字段,这是最重要,也是比较复杂的字段。数组字段是一个数组,可以有多个的。


parse_optgroup() 函数把 OptionGroup 转成 OptionsContext 的流程如下:

可以看到,Option 里面原本都是 key - value 的字符串结构,经过 parse_optgroup() 处理之后,就变成了,右边那种 int64 ,intSpecifierOpt 的结构。

至于 AVDictionary 相关的结构是没有进行转换的,因为 AVDictionary 可以直接传给 FFmpeg 的 API 函数,所以 open_files() 会把 OptionGroup 直接赋值给 OptionsContext 的 g 字段,通过 OptionsContext->g->codec_opts 的方式,即可提取命令行指定的编解码参数。


下面来具体分析一下 parse_optgroup() 函数是如何把 OptionGroup 里面的 key - value 转成 OptionsContext 特定的字段的。

不过与其说是 parse_optgroup() 做的转换,不如说是 write_option() 函数做的转换。

parse_optgroup() 的逻辑比较简单,只是在循环里面调了一下 write_option() ,如下:


下面来分析一下 write_option() 是如何做转换的,定义如下:

static int write_option(void *optctx, const OptionDef *po, const char *opt,const char *arg)

write_option() 的第一步是确定目标位置,这个 key-value 要保存到哪里,如下:

可以看到,是通过 flags 标记来判断要保存到全局变量,还是保存到 OptionsContext 结构,全局变量的解析流程之前已经讲过了,本文主要关注 解析到 OptionsContext 结构的流程。

可以看到通过 optctx + po->u.off 就能定位到 OptionsContext 里面的的字段,这个 u.off 是通过一个宏计算出来的,如下:

#define OFFSET(x) offsetof(OptionsContext, x)

OptionsContext 里面的 intint64double,等等字段的更新都比较简单,就是通过偏移定位到字段,然后赋值就行了。

但是 OptionsContext 里面 SpecifierOpt 类型的字段赋值比较复杂。SpecifierOpt 结构体的定义如下:

typedef struct SpecifierOpt {char *specifier;    /**< stream/chapter/program/... specifier */union {uint8_t *str;int        i;int64_t  i64;uint64_t ui64;float      f;double   dbl;} u;
} SpecifierOpt;

可以看到 SpecifierOpt 只有两个字段,

1,char *specifier,会是 a 或者 v,区分这是音频还是视频的选项,或者留空字符,代表不区分音视频。

2,union {...} u,一个联合体,用来适应各种各样的数据类型。uint8_t 实际上是字符串,因为一个 char 也是占 8 位。


OptionsContext 里面使用 SpecifierOpt 的时候,是以数组的方式使用的,后面会跟一个数组的长度,如下:

SpecifierOpt *codec_names; //指针就是数组
int        nb_codec_names; //数组长度

codec_names 是指编解码器的名称,所以 ffmpeg.exe 可以指定多个解码器来解码文件,如下:

ffmpeg.exe -c:v h264_cuvid -c:v h264_mf -i juren.mp4 juren.flv

这样会优先使用 h264_cuvid 硬件解码器解码,如果 ffmpeg.exe 没有编译硬件解码器,就会使用 h264_mf


下面讲解一下 SpecifierOpt 的解析代码,如下:

变量 so 直接指向 OptionsContext 里面 SpecifierOpt 类型的某个字段了。

下面的代码,实际是为了提取出 a 或者 v 字符, str 等于 a 或者 v,代表这是音频还是视频的选项。

char *p = strchr(opt, ':');
char *str;
...省略..
str = av_strdup(p ? p + 1 : "");

例如 -c:v ,-c:a,可以指定音频或者视频的编码器。上面的代码这样操作之后,就可以把 str 指向 a 或者 v。


变量 so 一开始其实是一个 NULL 指针,需要用 grow_array() 函数来动态扩容。

最后把 dst 指向 SpecifierOpt 里面的 u 联合体字段,这个其实比较通用,

SpecifierOpt 的 u 跟,OptionDef 的 u 其实是有点类似,可以通用后面的赋值逻辑

typedef struct SpecifierOpt {...省略...union {uint8_t *str;int        i;int64_t  i64;uint64_t ui64;float      f;double   dbl;} u;
} SpecifierOpt;
typedef struct OptionDef {...省略...union {void *dst_ptr;int (*func_arg)(void *, const char *, const char *);size_t off;} u;...省略...
} OptionDef;

至此,parse_option() 把 OptionGroup 转成 OptionsContext 的逻辑分析完毕了。

得到 OptionsContext 之后,就会调指针函数 open_file() 去打开输入或者输出文件。

open_file 是一个指针函数,可能指向 open_input_file() ,也可能指向 open_output_file()。


推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:

Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

open_files打开输入输出文件相关推荐

  1. ca76a_c++_流文件打开输入输出文件模式

    /*ca76a_c++_流文件打开输入输出文件模式 利用文件流打开文件进行输入与输出时的选项 in.out.app(附加模式).ate((end)文件打开后,定于文件结尾).trunc(裁剪).bin ...

  2. mysql 打开文件数_MySQL打开的文件描述符限制

    如果遇到如下错误: Can't open file: '.\test\mytable.frm' (errno: 24) shell> perror 24 OS error code 24: To ...

  3. linux怎么打开.o文件,Linux下文件I/O操作的相关知识

    Linux文件I/O主要指的是文件的输入输出,很多初学者对文件的I/O不是很了解,Linux文件I/O的操作较多,下面小编就给大家详细介绍下Linux文件I/O. linux 文件I/O教程(1) 一 ...

  4. c语言打开指定文件,C语言文件的打开和关闭

    文件代表一系列的字节.C语言提供了标准库函数用于文件的打开和关闭. 1.文件的打开 打开文件的操作通过标准库函数 fopen 完成,该函数定义如下:FILE *fopen( const char * ...

  5. MATLAB 输入输出文件

    输入输出方式,即从数据文件读取数据或将结果写入数据文件.MATLAB提供了一系列低层输入输出函数,专门用于文件操作. 1.文件的打开与关闭 1)打开文件 在读写文件之前,必须先用fopen函数打开或创 ...

  6. vscode老编译之前的文件_vscode 打开新文件不替换旧文件

    设置 "workbench.editor.enablePreview": false- 主要是模仿robocopy的部分功能 (robocopy /L 参数可以列出本地目录和备份目 ...

  7. android打开wav格式,FileNotFoundException从Android资产中打开wav文件

    在我们的android应用程序中,我们打开位于assets/config/notification.wav中的wav文件.要打开和播放声音,我们使用下面的代码:FileNotFoundExceptio ...

  8. MATLAB可以打开gms文件吗,GMS文件扩展名 - 什么是.gms以及如何打开? - ReviverSoft...

    你在这里因为你有,有一个文件扩展名结尾的​​文件 .gms. 文件与文件扩展名 .gms 只能通过特定的应用程序推出.这有可能是 .gms 文件是数据文件,而不是文件或媒体,这意味着他们并不是在所有观 ...

  9. vs2008/2010安装无法打开数据文件解决方案

    本人在安装VS2008或2010时,在开始的第一个页面(进度条大约加载到75%左右),提示"无法打开数据文件 'C:/Documents and Settings/Administrator ...

最新文章

  1. andorid 启动模式面试题
  2. 用C++的类重载高精度加法,乘法和等于符号
  3. WebApi用户登录验证及服务器端用户状态存取
  4. Gartner:云安全的未来,是安全访问服务边缘架构
  5. mysql sql组合_详解mysql 组合查询
  6. 《2019年数据及存储发展研究报告》十大洞察
  7. 数据库学习笔记【自学教程】—— 如何建立数据库
  8. 数据结构课程设计(二)---算术表达式求值
  9. 常见边缘检测对比(Roberts算子、Prewitt算子、Sobel算子、Laplacian算子、Canny算子)
  10. 基于JavaWeb的12306网络购票系统设计与实现 文档+项目源码+脚本文件
  11. 软件工程基础知识--软件项目管理
  12. XPI 文件安装方法
  13. 计算机网络重置点命令,重置网络命令 重装系统如何重置网络命令
  14. 2022前端面试系列——Vue面试题
  15. while语句没有花括号用法
  16. 基于STM32的MLX90614人体红外测温枪
  17. matlab模拟硅中的点缺陷,硅中的杂质和缺陷.pdf
  18. Dbg2Excel_Word
  19. 易經大意(21) 三和 韓長庚 著
  20. (十四)c#Winform自定义控件-键盘(一)

热门文章

  1. Camera 主观测试经验 --- Good
  2. vista正版序列号下载
  3. 使用layui把数据以Excel格式导出
  4. hive中出现rg.apache.hadoop.hive.ql.exec.mr.MapRedTask错误
  5. C#,索尼偏光相机(Polarization Camera)传感器IMX250和专用SDK简介
  6. 搜你所想(去除最新版本电驴搜索限制方法共享)
  7. 数据里副(负)业现实;扫地机器人发展到哪步了;疫情后要不要重返办公室;淘宝元宇宙直播间;GitHub今日热榜 | ShowMeAI资讯日报
  8. 解决Windows11/10本地账户改用Microsoft账户登录显示“发生了错误”的问题
  9. edge浏览器启动页面修改后依然跳转到其他网页
  10. 重新认识莫比乌斯函数