ffmpeg命令分析-output_ts_offset - 弦外之音

/ 0评 / 0

本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8

本系列主要分析各种 ffmpeg 命令参数 在代码里是如何实现的。a.mp4下载链接:百度网盘,提取码:nl0s 。


命令如下:ffmpeg -i a.mp4 -t 5 -output_ts_offset 5 a2.flv

-output_ts_offset 5 ,设置输出文件的所有流的第一帧位5秒,后续帧从5秒开始递增。


命令行参数 -output_ts_offset 定义如下:

options_table.h 92行
{"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E}

注意,本次的命令行参数不在 ffmpeg_opt.c 里面定义了,而是在 libavformat 目录下的 options_table.h 文件里面。

在前面文章《FFmpeg源码分析-命令行》,分析过 命令行参数查找 option 的逻辑,如下:

1,在 cmdutil.c 第 803 行,调用 find_option(options, opt),优选查找 ffmepg_opt.c 里面定义的 options 选项。

2,在 ffmepg_opt.c 定义的 options 里找不到 output_ts_offset ,就会调 opt_default(NULL, opt, argv[optindex]),从AVClass 里面查找,也就是 从 options_table.h 的选项里查找。



查找过程源码如下图所示:

从 opt_default() 函数可以看出,*fc = avformat_get_class() ,AVFormat 对应的 AVClass 能找到 output_ts_offset 这个参数的定义。代码如下:

cmdutil.c 578行
if ((o = opt_find(&fc, opt, NULL, 0,
                     AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
    av_dict_set(&format_opts, opt, arg, FLAGS); //注意这里
    if (consumed)
        av_log(NULL, AV_LOG_VERBOSE, "Routing option %s to both codec and muxer layer\n", opt);
    consumed = 1;
}

上面的重点代码是 av_dict_set(&format_opts, opt, arg, FLAGS),把 -output_ts_offset 60 的值设置进去 format_opts 这个全局变量里面了。

接下来再看看 format_opts 这个全局变量在哪里使用了,

因为 -output_ts_offset 60 是作用于输出文件的参数,所以基本可以猜到,就在 open_output_file() 函数里面用了 format_opts ,打开输出文件的时候,把format_opts 传递进去。

果然,在 ffmpeg_opt.c 2146行,open_output_file() 函数里面有这样一行代码。

ffmpeg_opt.c 2146行
av_dict_copy(&of->opts, o->g->format_opts, 0);

把 o->g->format_opts 赋值 给 of->opts。

读者可能会疑惑, 全局变量 format_opts 是怎么转移到 o->g->format_opts 的,请看之前的文章 《FFmpeg源码分析-命令行》,这里不再讲述。

再接着找 of->opts 用在哪里。

ffmpeg_opt.c 2559行
/* open the file */
if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, &oc->interrupt_callback, &of->opts)) < 0) { //注意这里
        print_error(filename, err);
        exit_program(1);
}

可以看到 of->opts 作为一个参数,丢进去 avio_open2() api函数里面了。

总结:

-output_ts_offset 是AVFormat 库里面的一个公共参数,所以在 调用 avio_open2() 的时候,把 output_ts_offset 作为一个 AVDictionary 传递给 avio_open2() 即可。

本文命令生成的 a2.flv ,如果用迅雷播放器播放,可以播10s,当时用ffpaly播放只能播5s,视频本身是5s的,只是第一帧pts 设置成了 5s。

个人思考:

记得我刚开始学 FFmpeg 的时候,看到AVFrame有个 pts,我就在想第一帧的pts 是不是都是0呢?因为你打开视频肯定要立即播放,但是这个第一帧的pts 又是可以手动改的,因为视频文件在你的手里,改数据很容易。

那时候我就在想,如果一个视频文件,他的第一帧的pts是 60 s,是不是说明这个视频打开之后,要过 60s 才能播放第一帧,60s 之前播放器要一直黑屏。标准实现是不是应该这样子。

今天看到 ffmpeg 的命令参数 output_ ts_ offset 可以指定 第一帧pts,实际测试了下。其实原来根本就没有什么标准的播放器实现,一定要如何如何实现。

在迅雷播放器下,我那个第一帧是60s的视频,前面60s确实是黑屏的。我的视频只有10s,所以迅雷播放器进度条是 60+ 10s,一共70s。

但是我用 ffplay 播放,直接开始播放了,根本没有黑屏。

所以虽然有 pts 这个一个东西,但他的值是可以随便改,而且对于播放器而言,播放器想怎么解释就怎么解释。没什么标准不标准。


版权所属:知识星球:弦外之音,QQ:2338195090。 由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注