ShiftMediaProject项目先容—ffplay工具RTSP播放源码剖析

打印 上一主题 下一主题

主题 1840|帖子 1840|积分 5530

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
RTSP解复用器

     libavformat/rtspdec.c
                                   登录后复制                        
  1. const FFInputFormat ff_rtsp_demuxer = {
  2.     .p.name         = "rtsp",
  3.     .p.long_name    = NULL_IF_CONFIG_SMALL("RTSP input"),
  4.     .p.flags        = AVFMT_NOFILE,
  5.     .p.priv_class   = &rtsp_demuxer_class,
  6.     .priv_data_size = sizeof(RTSPState),
  7.     .read_probe     = rtsp_probe,
  8.     .read_header    = rtsp_read_header,
  9.     .read_packet    = rtsp_read_packet,
  10.     .read_close     = rtsp_read_close,
  11.     .read_seek      = rtsp_read_seek,
  12.     .read_play      = rtsp_read_play,
  13.     .read_pause     = rtsp_read_pause,
  14. };
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
                       探测输入格式根据URL起始字符进行匹配

                                   登录后复制                        
  1. static int rtsp_probe(const AVProbeData *p)
  2. {
  3.     if (
  4. #if CONFIG_TLS_PROTOCOL
  5.         av_strstart(p->filename, "rtsps:", NULL) ||
  6. #endif
  7.         av_strstart(p->filename, "satip:", NULL) ||
  8.         av_strstart(p->filename, "rtsp:", NULL))
  9.         return AVPROBE_SCORE_MAX;
  10.     return 0;
  11. }
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
                       sdp_parse_line实现SDP分析

     libavformat/rtsp.c实现RTSP协议的分析
                                   登录后复制                        
  1. >        ffplayd.exe!ff_sdp_parse(AVFormatContext * s, const char * content) 行 721        C
  2.         ffplayd.exe!ff_rtsp_setup_input_streams(AVFormatContext * s, RTSPMessageHeader * reply) 行 643        C
  3.         ffplayd.exe!ff_rtsp_connect(AVFormatContext * s) 行 1967        C
  4.         ffplayd.exe!rtsp_read_header(AVFormatContext * s) 行 757        C
  5.         ffplayd.exe!avformat_open_input(AVFormatContext * * ps, const char * filename, const AVInputFormat * fmt, AVDictionary * * options) 行 305        C
  6.         ffplayd.exe!read_thread(void * arg) 行 2858        C
  7.         ffplayd.exe!SDL_RunThread(void * data) 行 283        C
  8.         ffplayd.exe!RunThread(void * data) 行 91        C
  9.         ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106        C
  10.         ucrtbased.dll!00007ff8de7a4fb8()        未知
  11.         ucrtbased.dll!00007ff8de7a4bf1()        未知
  12.         kernel32.dll!00007ff92b737c24()        未知
  13.         ntdll.dll!00007ff92c9ad721()        未知
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
                       ff_rtsp_setup_input_streams创建rtsp交互毗连

     ff_rtsp_send_cmd优先发送DESCRIBE指令,海康和大华RTSP指令交互的区别在于,大华在OPTIONS阶段就会哀求认证信息RTSP/1.0 401 Unauthorized
     int ff_sdp_parse(AVFormatContext *s, const char *content) 解析SDP中的内容,实际上这里就已经完全知道码流的数据格式以及所有的流信息,根本不必要探测码流格式
     sdp_parse_rtpmap函数分析出h264码流格式,保存在AVFormatContext中的stream流中
                                   登录后复制                        
  1. /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other params>] */
  2. static int sdp_parse_rtpmap(AVFormatContext *s,
  3.                             AVStream *st, RTSPStream *rtsp_st,
  4.                             int payload_type, const char *p)
  5. {
  6. //*p = H264/90000
  7. //最终st中保存探测的码流格式
  8.     AVCodecParameters *par = st->codecpar;
  9.     char buf[256];
  10.     int i;
  11.     const AVCodecDescriptor *desc;
  12.     const char *c_name;    /* See if we can handle this kind of payload.
  13.      * The space should normally not be there but some Real streams or
  14.      * particular servers ("RealServer Version 6.1.3.970", see issue 1658)
  15.      * have a trailing space. */
  16. //关键,buf获取到H264
  17.     get_word_sep(buf, sizeof(buf), "/ ", &p);
  18.     if (payload_type < RTP_PT_PRIVATE) {
  19.         /* We are in a standard case
  20.          * (from http://www.iana.org/assignments/rtp-parameters). */
  21.         par->codec_id = ff_rtp_codec_id(buf, par->codec_type);
  22.     }    if (par->codec_id == AV_CODEC_ID_NONE) {
  23.         const RTPDynamicProtocolHandler *handler =
  24.             ff_rtp_handler_find_by_name(buf, par->codec_type);
  25.         init_rtp_handler(handler, rtsp_st, st);
  26.         /* If no dynamic handler was found, check with the list of standard
  27.          * allocated types, if such a stream for some reason happens to
  28.          * use a private payload type. This isn't handled in rtpdec.c, since
  29.          * the format name from the rtpmap line never is passed into rtpdec. */
  30.         if (!rtsp_st->dynamic_handler)
  31.             par->codec_id = ff_rtp_codec_id(buf, par->codec_type);
  32.     }    desc = avcodec_descriptor_get(par->codec_id);
  33.     if (desc && desc->name)
  34.         c_name = desc->name;
  35.     else
  36.         c_name = "(null)";    get_word_sep(buf, sizeof(buf), "/", &p);
  37.     i = atoi(buf);
  38.     switch (par->codec_type) {
  39.     case AVMEDIA_TYPE_AUDIO:
  40.         av_log(s, AV_LOG_DEBUG, "audio codec set to: %s\n", c_name);
  41.         par->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE;
  42.         par->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS;
  43.         if (i > 0) {
  44.             par->sample_rate = i;
  45.             avpriv_set_pts_info(st, 32, 1, par->sample_rate);
  46.             get_word_sep(buf, sizeof(buf), "/", &p);
  47.             i = atoi(buf);
  48.             if (i > 0)
  49.                 par->channels = i;
  50.         }
  51.         av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n",
  52.                par->sample_rate);
  53.         av_log(s, AV_LOG_DEBUG, "audio channels set to: %i\n",
  54.                par->channels);
  55.         break;
  56.     case AVMEDIA_TYPE_VIDEO:
  57.         av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name);
  58.         if (i > 0)
  59.             avpriv_set_pts_info(st, 32, 1, i);
  60.         break;
  61.     default:
  62.         break;
  63.     }
  64.     finalize_rtp_handler_init(s, rtsp_st, st);
  65.     return 0;
  66. }
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
                       附上SDP数据
                                   登录后复制                        
  1. v=0
  2. o=- 1625669797472718 1625669797472718 IN IP4 192.168.18.204
  3. s=Media Presentation
  4. e=NONE
  5. b=AS:5050
  6. t=0 0
  7. a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/
  8. m=video 0 RTP/AVP 96
  9. b=AS:5000
  10. a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/trackID=1
  11. a=rtpmap:96 H264/90000
  12. a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z0IAKpY1QPAET8s3AQEBAg==,aM48gA==
  13. a=Media_header:MEDIAINFO=494D4B48010100000400000100000000000000000000000000000000000000000000000000000000;
  14. a=appversion:1.0
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
                       毗连代码堆栈

                                   登录后复制                        
  1. ffplayd.exe!ff_poll_interrupt(pollfd * p, unsigned long nfds, int timeout, AVIOInterruptCB * cb) 行 166        C
  2.         ffplayd.exe!ff_connect_parallel(addrinfo * addrs, int timeout_ms_per_address, int parallel, URLContext * h, int * fd, void(*)(void *, int) customize_fd, void * customize_ctx) 行 461        C
  3.         ffplayd.exe!tcp_open(URLContext * h, const char * uri, int flags) 行 198        C
  4.         ffplayd.exe!ffurl_connect(URLContext * uc, AVDictionary * * options) 行 205        C
  5.         ffplayd.exe!ffurl_open_whitelist(URLContext * * puc, const char * filename, int flags, const AVIOInterruptCB * int_cb, AVDictionary * * options, const char * whitelist, const char * blacklist, URLContext * parent) 行 345        C
  6.         ffplayd.exe!ff_rtsp_connect(AVFormatContext * s) 行 1841        C
  7.         ffplayd.exe!rtsp_read_header(AVFormatContext * s) 行 726        C
  8. >        ffplayd.exe!avformat_open_input(AVFormatContext * * ps, const char * filename, AVInputFormat * fmt, AVDictionary * * options) 行 631        C
  9.         ffplayd.exe!read_thread(void * arg) 行 2780        C
  10.         ffplayd.exe!SDL_RunThread(void * data) 行 283        C
  11.         ffplayd.exe!RunThread(void * data) 行 91        C
  12.         ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106        C
  13.         ucrtbased.dll!00007ffbf3f14fb8()        未知
  14.         ucrtbased.dll!00007ffbf3f14bf1()        未知
  15.         kernel32.dll!00007ffc6c1e7c24()        未知
  16.         ntdll.dll!00007ffc6d74d721()        未知
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
                       接收代码堆栈

                                   登录后复制                        
  1. ffplayd.exe!ff_network_wait_fd_timeout(int fd, int write, __int64 timeout, AVIOInterruptCB * int_cb) 行 84        C
  2.         ffplayd.exe!tcp_read(URLContext * h, unsigned char * buf, int size) 行 240        C
  3.         ffplayd.exe!retry_transfer_wrapper(URLContext * h, unsigned char * buf, int size, int size_min, int(*)(URLContext *, unsigned char *, int) transfer_func) 行 376        C
  4.         ffplayd.exe!ffurl_read_complete(URLContext * h, unsigned char * buf, int size) 行 419        C
  5.         ffplayd.exe!ff_rtsp_tcp_read_packet(AVFormatContext * s, RTSPStream * * prtsp_st, unsigned char * buf, int buf_size) 行 771        C
  6.         ffplayd.exe!read_packet(AVFormatContext * s, RTSPStream * * rtsp_st, RTSPStream * first_queue_st, __int64 wait_end) 行 2111        C
  7.         ffplayd.exe!ff_rtsp_fetch_packet(AVFormatContext * s, AVPacket * pkt) 行 2202        C
  8.         ffplayd.exe!rtsp_read_packet(AVFormatContext * s, AVPacket * pkt) 行 879        C
  9.         ffplayd.exe!ff_read_packet(AVFormatContext * s, AVPacket * pkt) 行 856        C
  10.         ffplayd.exe!read_frame_internal(AVFormatContext * s, AVPacket * pkt) 行 1582        C
  11.         ffplayd.exe!av_read_frame(AVFormatContext * s, AVPacket * pkt) 行 1776        C
  12.         ffplayd.exe!read_thread(void * arg) 行 3009        C
  13.         ffplayd.exe!SDL_RunThread(void * data) 行 283        C
  14.         ffplayd.exe!RunThread(void * data) 行 91        C
  15.         ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106        C
  16.         ucrtbased.dll!00007fff78404fb8()        未知
  17.         ucrtbased.dll!00007fff78404bf1()        未知
  18.         kernel32.dll!00007fffc9f77c24()        未知
  19.         ntdll.dll!00007fffcacad721()        未知
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
                       FU-A分包处理逻辑

     重点分析函数ff_rtsp_fetch_packet,该函数调用read_packet获取到RTP数据,调用ff_rtp_parse_packet分析RTP数据,去掉RTP包头,添加起始码,然后封装成AVPacket,但是封装的AVPacket并不是完备的NAL单元的视频流,对于FU-A分包的数据,仍然必要对多个AVPacket进行重新组装
     对FU-A分包的RTP格式数据,会根据是否是第一个包添加起始码,,关键是(h264_handle_packet_fu_a)start_bit    = fu_header >> 7;
     ff_h264_handle_frag_packet函数根据start_bit,确定是否必要添加起始码
     av_read_frame分包代码剖析

     ff_h264_handle_frag_packet函数会为每一个RTP包携带的NALU分片申请一个AVPacket包保存,因此必要通过ff_combine_frame函数构建一个完备的NALU单元
                                   登录后复制                        
  1. ffplayd.exe!ff_h264_handle_frag_packet(AVPacket * pkt, const unsigned char * buf, int len, int start_bit, const unsigned char * nal_header, int nal_header_len) 行 270        C
  2.         ffplayd.exe!h264_handle_packet_fu_a(AVFormatContext * ctx, PayloadContext * data, AVPacket * pkt, const unsigned char * buf, int len, int * nal_counters, int nal_mask) 行 310        C
  3.         ffplayd.exe!h264_handle_packet(AVFormatContext * ctx, PayloadContext * data, AVStream * st, AVPacket * pkt, unsigned int * timestamp, const unsigned char * buf, int len, unsigned short seq, int flags) 行 360        C
  4.         ffplayd.exe!rtp_parse_packet_internal(RTPDemuxContext * s, AVPacket * pkt, const unsigned char * buf, int len) 行 763        C
  5.         ffplayd.exe!rtp_parse_one_packet(RTPDemuxContext * s, AVPacket * pkt, unsigned char * * bufptr, int len) 行 917        C
  6.         ffplayd.exe!ff_rtp_parse_packet(RTPDemuxContext * s, AVPacket * pkt, unsigned char * * bufptr, int len) 行 951        C
  7.         ffplayd.exe!ff_rtsp_fetch_packet(AVFormatContext * s, AVPacket * pkt) 行 2288        C
  8.         ffplayd.exe!rtsp_read_packet(AVFormatContext * s, AVPacket * pkt) 行 914        C
  9.         ffplayd.exe!ff_read_packet(AVFormatContext * s, AVPacket * pkt) 行 646        C
  10.         ffplayd.exe!read_frame_internal(AVFormatContext * s, AVPacket * pkt) 行 1335        C
  11. >        ffplayd.exe!av_read_frame(AVFormatContext * s, AVPacket * pkt) 行 1547        C
  12.         ffplayd.exe!read_thread(void * arg) 行 3087        C
  13.         ffplayd.exe!SDL_RunThread(void * data) 行 283        C
  14.         ffplayd.exe!RunThread(void * data) 行 91        C
  15.         ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106        C
  16.         ucrtbased.dll!00007ff8de734fb8()        未知
  17.         ucrtbased.dll!00007ff8de734bf1()        未知
  18.         kernel32.dll!00007ff92b737c24()        未知
  19.         ntdll.dll!00007ff92c9ad721()        未知
复制代码
      

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
                       HEVC码流探测剖析

     比方:"rtsp://admin:admin12345@192.168.28.136:554/h265/ch1/main/av_stream"
     哀求海康摄像机H265码流
     SDP报文如下:
v=0
o=- 1566124110963848 1566124110963848 IN IP4 192.168.28.136
s=Media Presentation
e=NONE
b=AS:5100
t=0 0
a=control:rtsp://192.168.28.136:554/h265/ch1/main/av_stream/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://192.168.28.136:554/h265/ch1/main/av_stream/trackID=1
a=rtpmap:96 H265/90000
a=fmtp:96 sprop-sps=QgEBAWAAAAMAsAAAAwAAAwB7oAPAgBDlja5JMvTcBAQEAg==; sprop-pps=RAHA8vA8kAA=
m=audio 0 RTP/AVP 8
c=IN IP4 0.0.0.0
b=AS:50
a=recvonly
a=control:rtsp://192.168.28.136:554/h265/ch1/main/av_stream/trackID=2
a=rtpmap:8 PCMA/8000
a=Media_header:MEDIAINFO=494D4B48010200000400050011710110401F000000FA000000000000000000000000000000000000;
a=appversion:1.0
     static int sdp_parse_rtpmap(AVFormatContext *s,
                            AVStream *st, RTSPStream *rtsp_st,
                            int payload_type, const char *p)
     通过sdp_parse_rtpmap函数分析SDP中的a=rtpmap:96 H265/90000
在调用函数        const RTPDynamicProtocolHandler *handler =
            ff_rtp_handler_find_by_name(buf, par->codec_type);中 获取到h265的处理句柄
     rtsp 分包NALU
            if (codec_id == AV_CODEC_ID_HEVC)
            ret = hevc_parse_nal_header(nal, logctx);
        else
            ret = h264_parse_nal_header(nal, logctx);
     static void parse_fmtp(AVFormatContext *s, RTSPState *rt,
                       int payload_type, const char *line)
通过h265的句柄调用
static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index,
                                       PayloadContext *hevc_data, const char *line)
该函数将SPS/PPS的内容保存在(AVFormatContext结构体中的AVStream流中的extradata变量
     创建视频流和音频流
update_stream_avctx函数只是将avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
并没有做其他的操作

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

泉缘泉

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表