FFmpeg RTSP拉流源码分析

原因:由于需要分析RTSP拉流细节,故在此通过FFmpeg进行源码分析。

概述:首先利用VLC搭建RTSP服务器,客户端首先根据服务端提供的ip和端口进行TCP创建和连接,通过tcp进行发送RTSP需要的关键字,比如OPTIONS,DESCRIBE,SETUP,PLAY等,具体的RTSP流程交互可以参考另一个博客(live555的rtsp协议交互),通过rtsp交互获取远端的rtp和rtcp端口,然后进行连通性测试,随后进行拉流播放。

伪代码实现如下:通过如下伪代码可以看出调用流程还是最基础的流程,只是针对不同协议的处理主要发生在avformat_open_input方法中,该方法主要实现了tcp创建,rtsp信息获取,rtp和rtcp的连通和play信息的发送等。

char *url = "rtsp://192.168.27.196:8554/1";

AVFormatContext *pFormatCtx = nullptr;

int ir = avformat_open_input(&pFormatCtx, url, nullptr, nullptr);

ir = avformat_find_stream_info(pFormatCtx, nullptr);

ir = avcodec_open2(pCodecCtx, pCodec,nullptr);

首先tcp协议的创建和连接伪代码实现如下:通过下面实现可以看出主要实现了tcp的创建和对远端服务器端的connect连接.

//rtspdec.c
static int rtsp_read_header(AVFormatContext *s)
{

  ret = ff_rtsp_connect(s);

}
int ff_rtsp_connect(AVFormatContext *s)

{

 if (!ff_network_init())
        return AVERROR(EIO);

 /* open the tcp connection */
        ff_url_join(tcpname, sizeof(tcpname), lower_rtsp_proto, NULL,
                    host, port,
                    "?timeout=%d", rt->stimeout);
        if ((ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE,
                       &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist, NULL)) < 0) {
            err = ret;
            goto fail;
        }

}
//avio.c
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
                         const AVIOInterruptCB *int_cb, AVDictionary **options,
                         const char *whitelist, const char* blacklist,
                         URLContext *parent)
{

 ret = ffurl_connect(*puc, options);

}
int ffurl_connect(URLContext *uc, AVDictionary **options)
{
    err =
        uc->prot->url_open2 ? uc->prot->url_open2(uc,
                                                  uc->filename,
                                                  uc->flags,
                                                  options) :
        uc->prot->url_open(uc, uc->filename, uc->flags);
}
//tcp.c
static int tcp_open(URLContext *h, const char *uri, int flags)
{
    fd = ff_socket(cur_ai->ai_family,
                   cur_ai->ai_socktype,
                   cur_ai->ai_protocol);
                   
     if ((ret = ff_listen_connect(fd, cur_ai->ai_addr, cur_ai->ai_addrlen,
                                     s->open_timeout / 1000, h, !!cur_ai->ai_next)) < 0) 

}

通过上面的tcp连接成功,则接下来将会和对端服务器通过RTSP协议进行信息交互,首先发送OPTIONS请求,获取服务器支持的方法。经Live555的RTSP流程分析可知服务器在收到OPTIONS后会返回支持的方法,但是通过调试可以得知服务器并没有返回支持的方法,而接下来的流程所使用的方法可知为RTSP默认支持。伪代码如下:

rtsp.c
int ff_rtsp_connect(AVFormatContext *s)
{
		ff_rtsp_send_cmd(s, "OPTIONS", rt->control_uri, cmd, reply, NULL);
        if (reply->status_code != RTSP_STATUS_OK) {
            err = ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA);
            goto fail;
        }
}

"OPTIONS rtsp://192.168.27.196:8554/1 RTSP/1.0\r\nCSeq: 1\r\nUser-Agent: Lavf57.72.101\r\n\r\n"

客户端发送DESCRIBE请求,用于获取真实的流信息,服务端以sdp形式进行反馈,然后本地进行sdp解析,通过解析参数用于填充AVFormatContext对象。伪代码实现如下:

//rtsp.c
int ff_rtsp_connect(AVFormatContext *s)
{
err = ff_rtsp_setup_input_streams(s, reply);
}

//rtspdec.c
int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply)
{
 ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content);
 
 ret = ff_sdp_parse(s, (const char *)content);
}

DESCRIBE rtsp://192.168.27.196:8554/1 RTSP/1.0\r\n

v=0
o=- 16229513470211784150 16229513470211784150 IN IP4 SKY-20181030CRR
s=Unnamed
i=N/A
c=IN IP4 0.0.0.0
t=0 0
a=tool:vlc 3.0.8
a=recvonly
a=type:broadcast
a=charset:UTF-8
a=control:rtsp://192.168.27.196:8554/1
m=video 0 RTP/AVP 96
b=RR:0
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=64000d;sprop-parameter-sets=Z2QADazZQUH6EAAAPpAADqYA8UKZYA==,aOvjyyLA;
a=control:rtsp://192.168.27.196:8554/1/trackID=0
m=audio 0 RTP/AVP 96
b=RR:0
a=rtpmap:96 mpeg4-generic/44100/2
a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=121056e500; SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;
a=control:rtsp://192.168.27.196:8554/1/trackID=1

接下来则进行SETUP信息发送,首先本地创建RTP和RTCP,此时可以指定rtp和rtcp的实现协议是udp还是tcp,指定后会根据不同的协议进行不同处理,如果是udp则本地创建udp对象包含rtp和rtcp,然后将本地创建的rtp和rtcp端口安装SETUP信息发送给对端,如果对端支持UDP,则会返回服务端对应的udp端口,如果是tcp则直接安装SETUP信息发送对端服务器,对端服务器则进行返回是否支持.伪代码如下:

//rtsp.c
int ff_rtsp_connect(AVFormatContext *s)
{
  err = ff_rtsp_make_setup_request(s, host, port, lower_transport,
                                 rt->server_type == RTSP_SERVER_REAL ?
                                     real_challenge : NULL);
}
int ff_rtsp_make_setup_request(AVFormatContext *s, const char *host, int port,
                              int lower_transport, const char *real_challenge)
{
	  /* we will use two ports per rtp stream (rtp and rtcp) */
                j += 2;
   err = ffurl_open_whitelist(&rtsp_st->rtp_handle, buf, AVIO_FLAG_READ_WRITE,
                                 &s->interrupt_callback, &opts, s->protocol_whitelist, s->protocol_blacklist, NULL);
                                 
	
   ff_rtsp_send_cmd(s, "SETUP", rtsp_st->control_url, cmd, reply, NULL);
}


"Transport: RTP/AVP/UDP;unicast;client_port=13696-13697\r\n"
"Transport: RTP/AVP/UDP;unicast;client_port=13698-13699\r\n"

RTP/AVP/TCP:unicast;interleaved=0-1

如上已经获取对端对应的rtp和rtcp端口,则接下来进行rtp和rtcp连通性探测和申请播放,伪代码如下:

//rtspdec.c

static int rtsp_read_play(AVFormatContext *s)
{
    if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP) {
        for (i = 0; i < rt->nb_rtsp_streams; i++) {
            RTSPStream *rtsp_st = rt->rtsp_streams[i];
            /* Try to initialize the connection state in a
             * potential NAT router by sending dummy packets.
             * RTP/RTCP dummy packets are used for RDT, too.
             */
            if (rtsp_st->rtp_handle &&
                !(rt->server_type == RTSP_SERVER_WMS && i > 1))
                ff_rtp_send_punch_packets(rtsp_st->rtp_handle);
        }
    }
    
 ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL);
}

//Play rtsp://192.168.27.196:8554/1 RTSP/1.0\r\n

总结:通过源码可以简单看出RTSP的信息控制传输主要通过TCP进行交互用于启动和终止播放,而真实的数据流信息则通过RTP和RTCP进行交互.通过名称可以看出RTP:real-time transport protocol(实时传输协议),RTCP:rtp control protocol(实时传输控制协议),RTSP:real-time streaming protocol(实时流协议),这也对应了rtp和rtcp为传输层协议,而rtsp则为应用层协议。

你可能感兴趣的:(FFmpeg)