FFplay源码分析-rtmp_open - 弦外之音

/ 0评 / 0

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

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


本文主要讲解 rtmp_open() 函数的实现,RTMP 协议的代码都在 libavformat/rtmpproto.c 文件里面,请看下面截图:

RTMP 经常使用的函数,rtmp_open (建立链接),rtmp_read(读取数据),rtmp_writertmp_seek ,这些函数,都在这个文件 rtmpproto.c 里面实现。


rtmp_open() 函数的流程图如下:

上面的流程图有以下重点:

重点一:

上面的流程图是第二次执行 ffurl_open_whitelist() 函数,第一次是处理RTMP链接,第二次是处理 TCP链接。第二次的 parent 参数是有值的。

重点二:

ff_connect_parallel() 函数会尝试重试,参考 RFC 8305 的标准。大概应该是 200ms后 TCP 握手还没完成,就会立即新建第二个链接,最后哪一个成了就用哪一个TCP链接。

重点三:

rtmp_handshake()函数就是处理握手交互的,推荐阅读RTMP协议分析-handshake

重点四:

ff_url_join() 是 ffmpeg 的 URL utility functions,也就是 工具函数,可以很方便的 拼接 协议相关的字符串。


还有 get_packet() 是个非常重点的函数。里面主要干了3件事

1,调用 rtmp_parse_result() 处理各种RTMP相关的AM,INVOKE,跟交互。

2,调 handle_metadata() 函数 处理 flv 的 metadata

3,调 append_flv_data() 处理 RTMP 音视频包,加入队列。

在上面流程图, get_packet() 这个函数调用了两次,第一次是为了把 RTMP 链接的状态从 STATE_HANDSHAKED 转成 STATE_PLAYING,第二次会阻塞直到读取到任何一个音视频包,或者flv 的metadata。

append_flv_data() 这个函数需要详细讲解一下,因为里面是处理音视频包的。代码如下:

实际上这里面的操作,就是把 一个 RTMP 音视频包(RTMP 是直接传的 H264 裸流),转成 flv 的tag 格式,然后丢给 flv 的解复用器。这个其实跟读取本地文件的 flv 文件一样。

下图是 flv 的格式,可以参考着看上面代码,上面的变量 old_flv_size 有时候是 13 个字节,就是 flv header(9字节) + flv body 的 previous tagsize (4字节)。


总结,rtmp_open() 函数做了以下事情:

1,建立 RTMP 链接。

2,读取到第一个音视频包,丢进去 RTMPContext *rt 里面

struct RTMPContext 这个结构体非常重要,掌控了 rtmp 会话的整个周期。先看代码:

从上面可以看到 变量 RTMPContext *rt 是套在 URLContext 里面的。下面讲一下 rtmp_open() 执行完之后,变量 rt 主要有哪些字段被改变了。如下图:


最后提一点,FFplay 对播放 RTMP 做了很多兼容的处理,例如即使服务器推的RTMP 数据,flv 头部数据某些字段缺少或者不对,FFplay 内部也会纠正过来。估计是早期有些人不按标准实现 RTMP 服务器

扩展知识:

1,struct RTMPContext 有两个比较有趣的字段,has_audiohas_video,代表有没收到音频或视频的 RTMP 包。


由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,或者希望交流音视频技术的,可以加我微信 Loken1。

发表回复

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