FFServer源码分析
来源:互联网 发布:网络言情作者排行榜 编辑:程序博客网 时间:2024/06/06 04:41
FFServer源码分析
@author FlyFire
@copyleft
在本章将浏览ffserver的源代码,理解其设计的思路。重点研究ffserver对rtp rtcp的支持,研究ffserver管理多个连接的方法。
为使用rtsp管理多播,进行rtp rtcp的流媒体传输做准备。
在研究ffserver源码之前,我们需要理解ffserver的配置文件ffserver.conf。在ffserver.conf中透露了管理ffserver的蛛丝马迹。
ffmpeg\tests\目录下的ffserver.conf
MaxBandwidth指每个连接的最大带宽
Feed和Stream配置了该ffserver的输入和ffserver的输出。Feed是一个ffserver获得流的地方。可以是从一个ffmpeg的encoder或者另一个ffserver或者是一个编码好的文件。每个Feed中包含一个video和/或一个audio。
定义每个输出的流。流的格式 帧率 来源 GOP 等。
现在分析ffserver.c
1. main()
首先解析了配置文件,打开指定的文件流
然后创建子进程,并在子进程中执行http_server
2. http_server
a.打开ffserver的监听端口
b.打开rtsp的监听端口
c start_multicast
.为各个需要多播的流创建相应的multicast ip 和 multicast port。
并初始到各个multicast组的rtp连接的context。
所谓初始相应的rtp连接的Context是指 分配HttpContext
在HttpContext中指明相应连接的protocol state sessionId。
//这里注意当HttpContext中使用的是本地avi文件,它的域是怎样初始化的
注意在这里,只要是组播就一定使用rtp rtcp协议。所以在start_multicast()中,初始到各个multicast组的rtp连接的context。
如果当前的流是multicast,
C0创建该流的HttpContext
rtp_new_connection()
初始化该HttpContext对应的from_addr session_id 和 protocol
将该HttpContext加到由first_http_ctx引导的链表中
C1.对该流的HttpContext执行open_input_stream
C10. av_open_input_file 打开相应的输入文件
在av_open_input_file中,对相应的输入文件的格式进行Probe。并将Probe的结果,用以初始化HttpContext中的AVInputFormat。
当打开一个本地文件时,常常是根据后缀名进行Probe的。可以看到,在av_open_input_file中将AVProbeData中的buf域初始化为空。
在进行Probe时,先通过执行各个AVInputFormat中的read_probe函数,若没法进行read_probe,则采用后缀名的比较方法。
AVProbeData中的buf域何时会被初始化呢?
在跟踪时发现,av_read_packet中有对AVProbeData中的buf进行初始化的代码。
在av_open_input_file中,先没有对AVProbeData做任何初始化就开始Probe。意味着仅仅通过filename的后缀名进行Probe。如果通过后缀名Probe失败,在av_open_input_file中先url_fopen然后再次Probe。
Probe完成后调用av_open_input_stream(),使用Probe到的AVInputFormat打开相应的文件流
在av_open_input_stream中调用相应的AVInputFormat的read_header()获得特定文件的基础信息,放在AVFormatContext的pri_data中,给后面解码时使用。
也就是说,在av_open_input_file中完成了对AVFormatContext的初始化和AVInputFormat的初始化。AVFormatContext中已经包括了打开文件的头部信息,供解码时使用。而AVInputFormat则在Probe时完成了初始化。
从而完成了对HttpContext中的fmt_in和stream->ifmt的初始化。
C2.
对HttpContext执行rtp_new_av_stream()
C20
rtp_new_av_stream()
在其中分配一个rtp连接所需的上下文。分配一个RtpContext并赋给URLContext。其中包括该rtp连接使用的两个端口 ttl等信息。HttpContext中的rtp_handles[]即在此初始。每个rtp stream对应一个rtp_handle。该rtp_handle也就是URLContext中。
对HttpContext中的每个Stream,每个Stream中的更小的stream分配管理该小stream的rtp_handle。
C3.
将该HttpContext的状态设为HTTPSTATE_SEND_DATA。
d.对server_fd和rtsp_fd进行poll,看是否有数据可读。如果有,则新建相应的HttpContext。.
并加入到first_http_ctx中。设置HttpContext时,如果是rtsp端口的连接,设为RTSPSTATE_WAIT_REQUEST;如果是serverfd的连接,设为HTTPSTATE_WAIT_REQUEST
e.处于WAIT_REQUEST状态下的HttpContext使用相应的poll_entry来进行管理。
然后依次对每个HttpContext进行handle_connection。
handle_connection()根据每个HttpContext的State以及相应的poll_entry进行处理。
注意:实际的状态变迁数据读取都是在handle_connection中完成的。
在进行rtsp的SETUP时,调用rtp_new_connection创建了rtp连接的HttpContext。且注意rtp连接的HttpContext的初始状态为HTTPSTATE_READY。并将该HttpContext加入到first_http_ctx的链表当中。
重点理解rtsp是怎样管理多个rtp的connection。
f.在rtsp_cmd_setup中,有个地方让人疑惑:检查传入的RTSPMessageHeader中的session_id。按我的理解,session_id应该是在SETUP时才产生的,在此次的rtsp_cmd_setup中,应该无需检查,直接分配才对。令人奇怪的是,竟然根据该session_id去查找相应的rtp session。对于第一次SETUP而言,应该是没有相应的rtp session的。为何还要做这样的检查?
最不能理解的是在rtsp的wiki上,While HTTP is stateless, RTSP is a stateful protocol. A session identifier is used to keep track of sessions when needed; thus, no permanent TCP connection is required。是不是意味着rtsp的tcp连接能随时断开?断开后再通过服务端的session_id进行恢复?
g.在执行完rtsp_parse_request之后,该HttpContext状态变成了RTSPSTATE_SEND_REPLY。
这里可能需要注意,在rtsp的监听端口上,当来了一个新的客户端连接时,则新建一个针对该连接的HTTPContext。这样每个客户端由一个RTSP的HTTPContext控制状态。它控制多个RTP流?还是说只控制一个RTP流?像AVI文件的传输,有video流audio流和text流。它们是使用一个RTSP控制还是三个RTSP控制?显然一个AVI文件三个流是相关的,如果要快进,三个流都要快进;如果要暂停,三个流都要暂停。所以使用一个RTSP连接管理三个RTP流。只进行一次SETUP,但建立三个RTP connection?????
这个问题现在相当不清楚。。。。。。
好像是这样的,在ffmpeg的rtsp_cmd_setup中,貌似是每个流进行了一次SETUP。也就是说,如果是个avi文件,需要进行三次SETUP,分别建立音频流视频流和字幕流。再第一次SETUP时分配相应的RTP的HTTPContext,以后两个SETUP,共用已经建立的该HTTPContext。在rtsp_cmd_setup中,在第一次SETUP命令时,执行rtp_new_connection(),以后两次执行rtp_new_av_stream()。
执行完相应的rtsp_cmd_xxx之后,通过url_close_dyn_buf完成了解析后产生的应答的传送。
//先研究RTSP对多个RTP RTCP的管理。
//之后研究RTCP对RTP的管理
h.这里不得不提到,在流媒体服务器端,对ffserver而言,它仅解析对每个文件中的某个流的请求,并不解析对整个文件的请求。即在ffserver配置文件中,针对每个具体单一的audio/video流配置。每个FFStream包含多个相关的单一的流audio/video/text
由客户端将这些流合并起来。
一个rtsp的session可以管理多个相关的流。
Rfc2326中的内容:
Note that a session identifier identifies a RTSP session across
transport sessions or connections. Control messages for more than one
RTSP URL may be sent within a single RTSP session. Hence, it is
possible that clients use the same session for controlling many
streams constituting a presentation, as long as all the streams come
from the same server. (See example in Section 14). However, multiple
"user" sessions for the same URL from the same client MUST use
different session identifiers.
The session identifier is needed to distinguish several delivery
requests for the same URL coming from the same client.
Rfc2326的例子(Section 14),
Client C requests a presentation from media server M . The movie is
stored in a container file. The client has obtained an RTSP URL to
the container file.
C->M: DESCRIBE rtsp://foo/twister RTSP/1.0
CSeq: 1
M->C: RTSP/1.0 200 OK
CSeq: 1
Content-Type: application/sdp
Content-Length: 164
v=0
o=- 2890844256 2890842807 IN IP4 172.16.2.93
s=RTSP Session
i=An Example of RTSP Session Usage
a=control:rtsp://foo/twister
t=0 0
m=audio 0 RTP/AVP 0
@author FlyFire
@copyleft
在本章将浏览ffserver的源代码,理解其设计的思路。重点研究ffserver对rtp rtcp的支持,研究ffserver管理多个连接的方法。
为使用rtsp管理多播,进行rtp rtcp的流媒体传输做准备。
在研究ffserver源码之前,我们需要理解ffserver的配置文件ffserver.conf。在ffserver.conf中透露了管理ffserver的蛛丝马迹。
ffmpeg\tests\目录下的ffserver.conf
MaxBandwidth指每个连接的最大带宽
Feed和Stream配置了该ffserver的输入和ffserver的输出。Feed是一个ffserver获得流的地方。可以是从一个ffmpeg的encoder或者另一个ffserver或者是一个编码好的文件。每个Feed中包含一个video和/或一个audio。
定义每个输出的流。流的格式 帧率 来源 GOP 等。
现在分析ffserver.c
1. main()
首先解析了配置文件,打开指定的文件流
然后创建子进程,并在子进程中执行http_server
2. http_server
a.打开ffserver的监听端口
b.打开rtsp的监听端口
c start_multicast
C10. av_open_input_file 打开相应的输入文件
在av_open_input_file中,对相应的输入文件的格式进行Probe。并将Probe的结果,用以初始化HttpContext中的AVInputFormat。
当打开一个本地文件时,常常是根据后缀名进行Probe的。可以看到,在av_open_input_file中将AVProbeData中的buf域初始化为空。
在进行Probe时,先通过执行各个AVInputFormat中的read_probe函数,若没法进行read_probe,则采用后缀名的比较方法。
AVProbeData中的buf域何时会被初始化呢?
在跟踪时发现,av_read_packet中有对AVProbeData中的buf进行初始化的代码。
在av_open_input_file中,先没有对AVProbeData做任何初始化就开始Probe。意味着仅仅通过filename的后缀名进行Probe。如果通过后缀名Probe失败,在av_open_input_file中先url_fopen然后再次Probe。
Probe完成后调用av_open_input_stream(),使用Probe到的AVInputFormat打开相应的文件流
在av_open_input_stream中调用相应的AVInputFormat的read_header()获得特定文件的基础信息,放在AVFormatContext的pri_data中,给后面解码时使用。
也就是说,在av_open_input_file中完成了对AVFormatContext的初始化和AVInputFormat的初始化。AVFormatContext中已经包括了打开文件的头部信息,供解码时使用。而AVInputFormat则在Probe时完成了初始化。
在其中分配一个rtp连接所需的上下文。分配一个RtpContext并赋给URLContext。其中包括该rtp连接使用的两个端口 ttl等信息。HttpContext中的rtp_handles[]即在此初始。每个rtp stream对应一个rtp_handle。该rtp_handle也就是URLContext中。
对HttpContext中的每个Stream,每个Stream中的更小的stream分配管理该小stream的rtp_handle。
d.对server_fd和rtsp_fd进行poll,看是否有数据可读。如果有,则新建相应的HttpContext。.
e.处于WAIT_REQUEST状态下的HttpContext使用相应的poll_entry来进行管理。
然后依次对每个HttpContext进行handle_connection。
g.在执行完rtsp_parse_request之后,该HttpContext状态变成了RTSPSTATE_SEND_REPLY。
执行完相应的rtsp_cmd_xxx之后,通过url_close_dyn_buf完成了解析后产生的应答的传送。
//先研究RTSP对多个RTP RTCP的管理。
//之后研究RTCP对RTP的管理
h.这里不得不提到,在流媒体服务器端,对ffserver而言,它仅解析对每个文件中的某个流的请求,并不解析对整个文件的请求。即在ffserver配置文件中,针对每个具体单一的audio/video流配置。每个FFStream包含多个相关的单一的流audio/video/text
Rfc2326的例子(Section 14),
Client C requests a presentation from media server M . The movie is