原因:由于webRtc媒体信息交互需要使用SDP数据格式,而且使用ffplay播放rtp数据流时需要首先获取SDP文件信息,故通过对比SDP信息进行分析.
概述:SDP( Session Description Protocol)与其说是一个协议不如确切的说是一种文本封装格式,关于SDP的传输需要使用到其他协议传输,比如Http,RTSP等,SDP文本中主要包含会话信息和媒体信息,按照规定的格式进行填写和扩展。https://www.rfc-editor.org/rfc/rfc2327.txt
首先使用ffmpeg进行rtp推流以及ffplay进行rtp播放命令行如下:通过命令行可以看出ffplay播放rtp需要首先使用sdp文件进行获取媒体信息。
ffmpeg -re -i gop.264 -vcodec copy -f rtp rtp://127.0.0.1:1234 > test_rtp_h264.sdp
ffplay -protocol_whitelist "file,udp,rtp" -i test_rtp_h264.sdp
ffplay -protocol_whitelist "file,udp,rtp" -i rtp://127.0.0.1:1234 (失败)
rtp推流生成的sdp文件信息如下:可以看出包括会话信息和媒体信息。接下来具体分析SDP数据个关键字含义
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.72.101
m=video 1234 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgCJ+XARAAAD6QAAu4APGDGWA==,aOvjyyLA; profile-level-id=640028
v = :协议版本,目前为0.
o = :<会话名><会话标识><版本><网络类型><地址类型>,主要表述会话源,IN:标识internet地址类型为文本类型.
s =: <会话名>,每个会话描述必须只有一个会话名
c = :<网络类型><地址类型><连接地址>,主要用于包含连接数据.
t = :<开始时间><结束时间>,会话的实效性,如果结束为0则表示该会话不受限制,如果开始也为0则表示视为永久性。
m = :<媒体><端口><传输层><格式列表>,媒体信息描述,每个媒体描述都是通过m字段开头。媒体类型包括:video,audio,application,data,control,传输层:对应的传输协议,格式列表:对应的负债类型。
a =<属性>:<值>,用于SDP扩展,可以分为会话级属性和媒体级属性.
a = rtpmap:<负载类型><编码类型><时钟频率><编码参数>,对于音频流,编码参数为声道数量,视频无该参数.
a = tool:<工具名><版本号>。
接下来为webRtc交互的SDP数据如下:
{
"sdp" :
"v=0\r\n
o=- 449280690799912824 2 IN IP4 127.0.0.1\r\n
s=-\r\n
t=0 0\r\n
a=group:BUNDLE audio video\r\n
a=msid-semantic: WMS stream_label\r\n
m=audio 9 UDP/TLS/RTP/SAVPF 103 104 0 8 106 105 13 112 113 126\r\n
c=IN IP4 0.0.0.0\r\n
a=rtcp:9 IN IP4 0.0.0.0\r\n
a=ice-ufrag:cF8F\r\n
a=ice-pwd:/mLl0OIRuzAUE0gXyCJ+Z+P4\r\n
a=fingerprint:sha-25679:B2:E3:E0:8A:0C:CB:DA:D2:12:D6:50:FB:FD:B5:E8:18:CA:C6:F6:07:26:0E:F7:CF:53:7F:80:E4:76:73:3B\r\n
a=setup:actpass\r\n
a=mid:audio\r\n
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n
a=sendrecv\r\n
a=rtcp-mux\r\n
a=rtpmap:103 ISAC/16000\r\n
a=rtpmap:104 ISAC/32000\r\n
a=rtpmap:0 PCMU/8000\r\n
a=rtpmap:8 PCMA/8000\r\n
a=rtpmap:106 CN/32000\r\n
a=rtpmap:105 CN/16000\r\n
a=rtpmap:13 CN/8000\r\n
a=rtpmap:112 telephone-event/32000\r\n
a=rtpmap:113 telephone-event/16000\r\n
a=rtpmap:126 telephone-event/8000\r\n
a=ssrc:2929087609 cname:O025nGVLc6C+RI1k\r\n
a=ssrc:2929087609 msid:stream_label audio_label\r\n
a=ssrc:2929087609 mslabel:stream_label\r\n
a=ssrc:2929087609 label:audio_label\r\n
m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\r\n
c=IN IP4 0.0.0.0\r\n
a=rtcp:9 IN IP4 0.0.0.0\r\n
a=ice-ufrag:cF8F\r\n
a=ice-pwd:/mLl0OIRuzAUE0gXyCJ+Z+P4\r\n
a=fingerprint:sha-256 79:B2:E3:E0:8A:0C:CB:DA:D2:12:D6:50:FB:FD:B5:E8:18:CA:C6:F6:07:26:0E:F7:CF:53:7F:80:E4:76:73:3B\r\n
a=setup:actpass\r\n
a=mid:video\r\n
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n
a=extmap:4 urn:3gpp:video-orientation\r\n
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\n
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\r\n
a=sendrecv\r\n
a=rtcp-mux\r\n
a=rtcp-rsize\r\n
a=rtpmap:96 VP8/90000\r\n
a=rtcp-fb:96 ccm fir\r\n
a=rtcp-fb:96 nack\r\n
a=rtcp-fb:96 nack pli\r\n
a=rtcp-fb:96 goog-remb\r\n
a=rtcp-fb:96 transport-cc\r\n
a=rtpmap:98 VP9/90000\r\n
a=rtcp-fb:98 ccm fir\r\n
a=rtcp-fb:98 nack\r\n
a=rtcp-fb:98 nack pli\r\n
a=rtcp-fb:98 goog-remb\r\n
a=rtcp-fb:98 transport-cc\r\n
a=rtpmap:100 H264/90000\r\n
a=rtcp-fb:100 ccm fir\r\n
a=rtcp-fb:100 nack\r\n
a=rtcp-fb:100 nack pli\r\n
a=rtcp-fb:100 goog-remb\r\n
a=rtcp-fb:100 transport-cc\r\n
a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n
a=rtpmap:102 red/90000\r\n
a=rtpmap:127 ulpfec/90000\r\n
a=rtpmap:97 rtx/90000\r\n
a=fmtp:97 apt=96\r\n
a=rtpmap:99 rtx/90000\r\n
a=fmtp:99 apt=98\r\n
a=rtpmap:101 rtx/90000\r\n
a=fmtp:101 apt=100\r\n
a=rtpmap:125 rtx/90000\r\n
a=fmtp:125 apt=102\r\n
a=ssrc-group:FID 4198811712 3825804760\r\n
a=ssrc:4198811712 cname:O025nGVLc6C+RI1k\r\n
a=ssrc:4198811712 msid:stream_label video_label\r\n
a=ssrc:4198811712 mslabel:stream_label\r\n
a=ssrc:4198811712 label:video_label\r\n
a=ssrc:3825804760 cname:O025nGVLc6C+RI1k\r\n
a=ssrc:3825804760 msid:stream_label video_label\r\n
a=ssrc:3825804760 mslabel:stream_label\r\n
a=ssrc:3825804760 label:video_label\r\n",
"type" : "offer"
}
SDP主要用于进行媒体交互,通过m字段包含媒体类型,通过a字段进行媒体类型的细化说明。利用FFmpeg进行SDP数据解析代码如下:可以看出主要使用到AVIOContext,而具体的SDP解析并没有深入分析。
int main(int argc, char *argv[])
{
//
av_register_all();
av_log_set_level(AV_LOG_DEBUG);
FILE *pSDP = nullptr;
errno_t er = fopen_s(&pSDP, "h264.sdp", "rb+");
if (er != 0)
{
printf("fopen_s failed.\n");
goto end;
}
fseek(pSDP, 0, SEEK_END);
int iSize = ftell(pSDP);
fseek(pSDP, 0, SEEK_SET);
unsigned char* pBuffer = (unsigned char *)av_malloc(iSize);
int iLen = fread(pBuffer, 1, iSize, pSDP);
if (iLen != iSize)
{
printf("iSize=%d,iLen=%d",iSize,iLen);
goto end;
}
AVIOContext *pIoCtx = nullptr;
pIoCtx = avio_alloc_context(pBuffer, iSize, 0, NULL, NULL, NULL, NULL);
AVFormatContext *pForCtx = nullptr;
pForCtx = avformat_alloc_context();
pForCtx->pb = pIoCtx;
pForCtx->iformat = av_find_input_format("sdp");
int ir = avformat_open_input(&pForCtx, NULL, nullptr , nullptr);
if (ir != 0)
{
printf("avformat_open_input Failed\n");
goto end;
}
ir = avformat_find_stream_info(pForCtx, nullptr);
if (ir < 0)
{
printf("avformat_find_stream_info Failed.\n");
goto end;
}
int iIndex = -1;
for (int i = 0; i < pForCtx->nb_streams; i++)
{
if (pForCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
iIndex = i;
break;
}
}
if (iIndex < 0)
{
printf("get video stream failed\n");
goto end;
}
AVCodec *pCodec = nullptr;
AVCodecContext *pCodCtx = nullptr;
pCodec = avcodec_find_decoder(pForCtx->streams[iIndex]->codecpar->codec_id);
pCodCtx = avcodec_alloc_context3(pCodec);
ir = avcodec_parameters_to_context(pCodCtx, pForCtx->streams[iIndex]->codecpar);
if (ir < 0)
{
printf("avcodec_parameters_to_context failed\n");
goto end;
}
ir = avcodec_open2(pCodCtx, pCodec,nullptr);
if (ir != 0)
{
printf("avcodec_open2 failed\n");
goto end;
}
AVPacket pk;
while (av_read_frame(pForCtx, &pk) == 0)
{
if (pk.stream_index == iIndex)
{
printf("Recv pts=%lld,dts=%lld,size=%d\n",pk.pts,pk.dts,pk.size);
}
av_packet_unref(&pk);
}
end:
fclose(pSDP);
av_free(pBuffer);
avformat_free_context(pForCtx);
for (;;);
return 0;
}
总结:以上为sdp的简单分析,通过分析可知sdp只是用来进行媒体交互的一种文本格式,且在媒体交互中该文本格式主要包含了媒体信息,而通过m字段可以表明传输的媒体类型,通过a字段对媒体类型进行详细描述。主要包括:音视频对应的负载类型,编码类型,传输协议,时钟频率等。