马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
RTSP解复用器
libavformat/rtspdec.c
登录后复制 - const FFInputFormat ff_rtsp_demuxer = {
- .p.name = "rtsp",
- .p.long_name = NULL_IF_CONFIG_SMALL("RTSP input"),
- .p.flags = AVFMT_NOFILE,
- .p.priv_class = &rtsp_demuxer_class,
- .priv_data_size = sizeof(RTSPState),
- .read_probe = rtsp_probe,
- .read_header = rtsp_read_header,
- .read_packet = rtsp_read_packet,
- .read_close = rtsp_read_close,
- .read_seek = rtsp_read_seek,
- .read_play = rtsp_read_play,
- .read_pause = rtsp_read_pause,
- };
复制代码
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
探测输入格式根据URL起始字符进行匹配
登录后复制 - static int rtsp_probe(const AVProbeData *p)
- {
- if (
- #if CONFIG_TLS_PROTOCOL
- av_strstart(p->filename, "rtsps:", NULL) ||
- #endif
- av_strstart(p->filename, "satip:", NULL) ||
- av_strstart(p->filename, "rtsp:", NULL))
- return AVPROBE_SCORE_MAX;
- return 0;
- }
复制代码
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
sdp_parse_line实现SDP分析
libavformat/rtsp.c实现RTSP协议的分析
登录后复制 - > ffplayd.exe!ff_sdp_parse(AVFormatContext * s, const char * content) 行 721 C
- ffplayd.exe!ff_rtsp_setup_input_streams(AVFormatContext * s, RTSPMessageHeader * reply) 行 643 C
- ffplayd.exe!ff_rtsp_connect(AVFormatContext * s) 行 1967 C
- ffplayd.exe!rtsp_read_header(AVFormatContext * s) 行 757 C
- ffplayd.exe!avformat_open_input(AVFormatContext * * ps, const char * filename, const AVInputFormat * fmt, AVDictionary * * options) 行 305 C
- ffplayd.exe!read_thread(void * arg) 行 2858 C
- ffplayd.exe!SDL_RunThread(void * data) 行 283 C
- ffplayd.exe!RunThread(void * data) 行 91 C
- ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106 C
- ucrtbased.dll!00007ff8de7a4fb8() 未知
- ucrtbased.dll!00007ff8de7a4bf1() 未知
- kernel32.dll!00007ff92b737c24() 未知
- 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流中
登录后复制 - /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other params>] */
- static int sdp_parse_rtpmap(AVFormatContext *s,
- AVStream *st, RTSPStream *rtsp_st,
- int payload_type, const char *p)
- {
- //*p = H264/90000
- //最终st中保存探测的码流格式
- AVCodecParameters *par = st->codecpar;
- char buf[256];
- int i;
- const AVCodecDescriptor *desc;
- const char *c_name; /* See if we can handle this kind of payload.
- * The space should normally not be there but some Real streams or
- * particular servers ("RealServer Version 6.1.3.970", see issue 1658)
- * have a trailing space. */
- //关键,buf获取到H264
- get_word_sep(buf, sizeof(buf), "/ ", &p);
- if (payload_type < RTP_PT_PRIVATE) {
- /* We are in a standard case
- * (from http://www.iana.org/assignments/rtp-parameters). */
- par->codec_id = ff_rtp_codec_id(buf, par->codec_type);
- } if (par->codec_id == AV_CODEC_ID_NONE) {
- const RTPDynamicProtocolHandler *handler =
- ff_rtp_handler_find_by_name(buf, par->codec_type);
- init_rtp_handler(handler, rtsp_st, st);
- /* If no dynamic handler was found, check with the list of standard
- * allocated types, if such a stream for some reason happens to
- * use a private payload type. This isn't handled in rtpdec.c, since
- * the format name from the rtpmap line never is passed into rtpdec. */
- if (!rtsp_st->dynamic_handler)
- par->codec_id = ff_rtp_codec_id(buf, par->codec_type);
- } desc = avcodec_descriptor_get(par->codec_id);
- if (desc && desc->name)
- c_name = desc->name;
- else
- c_name = "(null)"; get_word_sep(buf, sizeof(buf), "/", &p);
- i = atoi(buf);
- switch (par->codec_type) {
- case AVMEDIA_TYPE_AUDIO:
- av_log(s, AV_LOG_DEBUG, "audio codec set to: %s\n", c_name);
- par->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE;
- par->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS;
- if (i > 0) {
- par->sample_rate = i;
- avpriv_set_pts_info(st, 32, 1, par->sample_rate);
- get_word_sep(buf, sizeof(buf), "/", &p);
- i = atoi(buf);
- if (i > 0)
- par->channels = i;
- }
- av_log(s, AV_LOG_DEBUG, "audio samplerate set to: %i\n",
- par->sample_rate);
- av_log(s, AV_LOG_DEBUG, "audio channels set to: %i\n",
- par->channels);
- break;
- case AVMEDIA_TYPE_VIDEO:
- av_log(s, AV_LOG_DEBUG, "video codec set to: %s\n", c_name);
- if (i > 0)
- avpriv_set_pts_info(st, 32, 1, i);
- break;
- default:
- break;
- }
- finalize_rtp_handler_init(s, rtsp_st, st);
- return 0;
- }
复制代码
- 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数据
登录后复制 - v=0
- o=- 1625669797472718 1625669797472718 IN IP4 192.168.18.204
- s=Media Presentation
- e=NONE
- b=AS:5050
- t=0 0
- a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/
- m=video 0 RTP/AVP 96
- b=AS:5000
- a=control:rtsp://192.168.18.204:554/h264/ch1/main/av_stream/trackID=1
- a=rtpmap:96 H264/90000
- a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z0IAKpY1QPAET8s3AQEBAg==,aM48gA==
- a=Media_header:MEDIAINFO=494D4B48010100000400000100000000000000000000000000000000000000000000000000000000;
- a=appversion:1.0
复制代码
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
毗连代码堆栈
登录后复制 - ffplayd.exe!ff_poll_interrupt(pollfd * p, unsigned long nfds, int timeout, AVIOInterruptCB * cb) 行 166 C
- 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
- ffplayd.exe!tcp_open(URLContext * h, const char * uri, int flags) 行 198 C
- ffplayd.exe!ffurl_connect(URLContext * uc, AVDictionary * * options) 行 205 C
- 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
- ffplayd.exe!ff_rtsp_connect(AVFormatContext * s) 行 1841 C
- ffplayd.exe!rtsp_read_header(AVFormatContext * s) 行 726 C
- > ffplayd.exe!avformat_open_input(AVFormatContext * * ps, const char * filename, AVInputFormat * fmt, AVDictionary * * options) 行 631 C
- ffplayd.exe!read_thread(void * arg) 行 2780 C
- ffplayd.exe!SDL_RunThread(void * data) 行 283 C
- ffplayd.exe!RunThread(void * data) 行 91 C
- ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106 C
- ucrtbased.dll!00007ffbf3f14fb8() 未知
- ucrtbased.dll!00007ffbf3f14bf1() 未知
- kernel32.dll!00007ffc6c1e7c24() 未知
- ntdll.dll!00007ffc6d74d721() 未知
复制代码
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
接收代码堆栈
登录后复制 - ffplayd.exe!ff_network_wait_fd_timeout(int fd, int write, __int64 timeout, AVIOInterruptCB * int_cb) 行 84 C
- ffplayd.exe!tcp_read(URLContext * h, unsigned char * buf, int size) 行 240 C
- ffplayd.exe!retry_transfer_wrapper(URLContext * h, unsigned char * buf, int size, int size_min, int(*)(URLContext *, unsigned char *, int) transfer_func) 行 376 C
- ffplayd.exe!ffurl_read_complete(URLContext * h, unsigned char * buf, int size) 行 419 C
- ffplayd.exe!ff_rtsp_tcp_read_packet(AVFormatContext * s, RTSPStream * * prtsp_st, unsigned char * buf, int buf_size) 行 771 C
- ffplayd.exe!read_packet(AVFormatContext * s, RTSPStream * * rtsp_st, RTSPStream * first_queue_st, __int64 wait_end) 行 2111 C
- ffplayd.exe!ff_rtsp_fetch_packet(AVFormatContext * s, AVPacket * pkt) 行 2202 C
- ffplayd.exe!rtsp_read_packet(AVFormatContext * s, AVPacket * pkt) 行 879 C
- ffplayd.exe!ff_read_packet(AVFormatContext * s, AVPacket * pkt) 行 856 C
- ffplayd.exe!read_frame_internal(AVFormatContext * s, AVPacket * pkt) 行 1582 C
- ffplayd.exe!av_read_frame(AVFormatContext * s, AVPacket * pkt) 行 1776 C
- ffplayd.exe!read_thread(void * arg) 行 3009 C
- ffplayd.exe!SDL_RunThread(void * data) 行 283 C
- ffplayd.exe!RunThread(void * data) 行 91 C
- ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106 C
- ucrtbased.dll!00007fff78404fb8() 未知
- ucrtbased.dll!00007fff78404bf1() 未知
- kernel32.dll!00007fffc9f77c24() 未知
- 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单元
登录后复制 - 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
- 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
- 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
- ffplayd.exe!rtp_parse_packet_internal(RTPDemuxContext * s, AVPacket * pkt, const unsigned char * buf, int len) 行 763 C
- ffplayd.exe!rtp_parse_one_packet(RTPDemuxContext * s, AVPacket * pkt, unsigned char * * bufptr, int len) 行 917 C
- ffplayd.exe!ff_rtp_parse_packet(RTPDemuxContext * s, AVPacket * pkt, unsigned char * * bufptr, int len) 行 951 C
- ffplayd.exe!ff_rtsp_fetch_packet(AVFormatContext * s, AVPacket * pkt) 行 2288 C
- ffplayd.exe!rtsp_read_packet(AVFormatContext * s, AVPacket * pkt) 行 914 C
- ffplayd.exe!ff_read_packet(AVFormatContext * s, AVPacket * pkt) 行 646 C
- ffplayd.exe!read_frame_internal(AVFormatContext * s, AVPacket * pkt) 行 1335 C
- > ffplayd.exe!av_read_frame(AVFormatContext * s, AVPacket * pkt) 行 1547 C
- ffplayd.exe!read_thread(void * arg) 行 3087 C
- ffplayd.exe!SDL_RunThread(void * data) 行 283 C
- ffplayd.exe!RunThread(void * data) 行 91 C
- ffplayd.exe!RunThreadViaBeginThreadEx(void * data) 行 106 C
- ucrtbased.dll!00007ff8de734fb8() 未知
- ucrtbased.dll!00007ff8de734bf1() 未知
- kernel32.dll!00007ff92b737c24() 未知
- 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企服之家,中国第一个企服评测及商务社交产业平台。 |