目录
- 0.avformat_open_input的rtsp流程程纯净版
- 1.rtsp拉流流程图
- 2.rtsp拉流对象图
- 2.1 sdp解析出来的信息
本节探索下ffmpeg的rtsp拉流协议tcp的创建及rtsp协商过程。
0.avformat_open_input的rtsp流程程纯净版
ffmpeg拉流,从avformat_open_input接口开始,去除与rtsp拉流无关的代码后,如下:
int avformat_open_input(AVFormatContext **ps, const char *filename,
const AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
FFFormatContext *si;
AVDictionary *tmp = NULL;
int ret = 0;
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
si = ffformatcontext(s);
if (!s->av_class) {
av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
return AVERROR(EINVAL);
}
if (options)
av_dict_copy(&tmp, *options, 0);
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
if (!(s->url = av_strdup(filename ? filename : ""))) {
ret = AVERROR(ENOMEM);
goto fail;
}
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
s->probe_score = ret;
s->duration = s->start_time = AV_NOPTS_VALUE;
/* Allocate private data. */
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;
}
}
if (s->iformat->read_header)
{
if ((ret = s->iformat->read_header(s)) < 0) {
if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)
goto close;
goto fail;
}
}
si->raw_packet_buffer_size = 0;
update_stream_avctx(s);
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;
close:
if (s->iformat->read_close)
s->iformat->read_close(s);
fail:
av_dict_free(&tmp);
avformat_free_context(s);
*ps = NULL;
return ret;
}
其中留下了参数配置流程,因为一般会配置些参数,参数配置参见《ffmpeg面向对象——参数配置机制及其设计模式探索》。
输入格式匹配(看下面流程图会清晰点),参见《ffmpeg面向对象-rtsp拉流相关对象》的第2节。
协议匹配机制(看下面流程图会清晰点),参见《ffmpeg面向对象——拉流协议匹配机制探索》。
输入格式类与协议类什么关系参见《ffmpeg面向对象——AVInputFormat与URLProtocol啥关系》。
这些结合流层图与对象图看会清晰点。
1.rtsp拉流流程图
rtsp的tcp链接创建在图中最右边。
rtsp协商流程也比较清晰,可以看到ff_rtsp_send_cmd是各个协商方法最终都要调用的中心转发节点。其中SDP解析后创建了一堆解码信息对象并根据解析赋值。
这种统一到同一个接口上(ff_rtsp_send_cmd)的设计模式是什么?外观模式。其内部的实现,又无视是什么命令,都同样的流程——如ff_rtsp_read_reply,尤其ffurl_read接口对所有协议的封装(这也是抽象出一个URLContxt类原因所在吧)——这种模式抽象出了共性的代码,走同样的流程,这种是什么设计模式?模板模式。
可以看到在retry_transfer_wrapper中有睡眠机制,也就是说avformat_open_input是阻塞的调用。
在此,也可以发现avformat_open_input调用完成,那么rtsp协商也完成了。
另外可以看到底层tcp协议读写是如何对接的,结合下面对象图,就能深入理解输入格式类与协议类的关系,它们面向接口编程,各自分层迭代开发,互不影响。
最后是解码信息的拷贝——从RTSP协议的DESCRIBE的SDP里解析出来的,有对象承载这些信息,要拷贝到AVFormatContext的streams对应元素成员中。
流程图里应该标记下各个对象创建的时机,这样下面对象图就能对应上了,否则得看源码。
2.rtsp拉流对象图
可琢磨的比较多,其中,输入格式类与底层协议类型都是“可变的”,因为这两者都是代码运行中匹配出来的,所以它们都被更上一层的进行“托管”。参见《ffmpeg面向对象——priv_data设计原理探索》。
2.1 sdp解析出来的信息
因为rtsp协商有sdp信息,所以可以在rtsp协商阶段获取到该rtsp会话里有几路流,这样就早早创建了AVStream,RTSPStream等对象。
可以看到它们的关系:
第1个层次,
AVFormatContext管理了AVStream、rtsp的私有数据类RTSPState。
第2个层次,
AVStream管理了AVCodecParameters(里面包含了流格式——音视频字幕等类型,包含了编码格式——264/265/AAC等)。
RTSPState管理了RTSPStream等。