FFplay源码分析-nobuffer

《FFmpeg原理》的社群来了,想加入社群的朋友请购买 VIP 版,VIP 版有更高级的内容与答疑服务。


本系列 以 ffmpeg4.4 源码为准,主要讲解 ffplay 的 RTMP 协议解析,播放。本文使用的命令如下:

ffplay -fflags nobuffer -i rtmp://192.168.0.122/live/livestream

在使用 FFplay 播放 RTMP 流的时候,如果 不开启 nobuffer 选项,画面延迟会高达 7 秒左右,开启了,局域网延迟可降低到100毫秒左右。

因此本文主要研究 nobuffer 的具体实现,以及播放端 缓存 7 秒的数据有何作用。

fflags 的定义在 libavformat/options_table.h,如下图代码,这是一个通用选项,所有的 解复用器都有这个选项。

也就是说,这个命令行参数,是在调 avformat_open_input() 函数的时候丢进去的,我为什么知道是在这个地方丢进去的?请看之前的专栏《FFplay源码分析》,所有的解复用参数,也叫格式参数,都是在 avformat_open_input() 丢进去的。可以看下图证明一下:

记得改 Clion 的调试参数,把 -fflags nobuffer 加上去。


在 avformat_open_input() 函数内部,会把 fflags 这个 AVOption 丢给 AVClass,如下图所示,AVClass 里面存储了好几个 AVOption ,fflags 这个 AVOption 的下标是 5 ,前面的是 默认的选项,自动加进去的。

注意 ,av_opt_set_dict() 这个函数会改变 tmp 的值,把能用 选项应用之后剔除。


nobuffer 这个参数 传递过程中的函数调用有点长,推荐看之前的《FFmpeg源码分析-参数解析篇》,原理类似,我知道这个 参数最后会赋值到 struct AVFormatContext 的 flags 字段里面,如下图:

如上图所示,所以 avformat_open_input() 函数执行完之后 AVFormatContext::flags 的第7位应该会被置为1,因为 0x40 的二进制是 1000000。请看下图:

从上图可以看出, ic->flags 直接就是 64 ,也就是 16 进制的 0x40。所以上面的分析没错。 avformat_open_input() 函数只是把 命令行参数解析 到这个 flags 字段,但是真正使用这个字段 是在 avformat_find_stream_info() 里面。直接搜 AVFMT_FLAG_NOBUFFER 就能找到使用的位置。

avformat_find_stream_info() 函数的内部逻辑实际上非常复杂,我直接讲重点代码,如下:

if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) {
    ret = avpriv_packet_list_put(&ic->internal->packet_buffer,
                                        &ic->internal->packet_buffer_end,
    pkt1, NULL, 0);
    if (ret < 0)
        goto unref_then_goto_end;
​
    pkt = &ic->internal->packet_buffer_end->pkt;
} else {
    pkt = pkt1;
}

AVFMT_FLAG_NOBUFFER 标记 如果没设置,就会导致 探测的数据包丢进去队列,我们知道 avformat_find_stream_info() 会先读一段数据包分析出流是什么编码器之类的,为了重用这个 探测的数据包,这里就会丢进去队列,播放的时候,就从这些数据包开始,但是整个探测过程,长达 5秒,也就是 ffplay 大概会读 5秒的数据,来分析输入流的情况。如果开启 nobuffer,就不会重用这些探测数据,ffpaly 探测完输入流之后,就会重新读取新的数据包来播放。不用缓存的,所以延迟就低了。

如下图,我在 ffpaly.c 的 avformat_find_stream_info() 前后输出了个时间,正好相差5秒。

double start_time3 = av_gettime_relative() / 1000000.0;
if (find_stream_info) {
    ...省略代码...
    err = avformat_find_stream_info(ic, opts);
    ...省略代码...
}
double end_time3 = av_gettime_relative() / 1000000.0;
printf("start is %f , end is %f \r\n",start_time3,end_time3);

所以实际上 ffplay 在 实时的场景下,缓存是个鸡肋,本来这个 buffer 功能是为了分析本地文件,避免重复读取,但是影响到了实时的场景。实时场景必须把 buffer 关掉。

补充,因为我是虚拟机做服务器,所以都是本机通信,不启用 buffer 也能很流畅,但是如果我把 SRS 部署在局域网另一台机器,不开启 buffer ,视频有卡顿,估计是还没来得及解码丢进去队列,所以 ffplay 不断丢弃视频帧。因为视频比音频慢了,得丢弃。

相关阅读

1,《FFmpeg 框架简读-Demux 部分》


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Loken2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值