SDP信息格式分析

原因:由于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字段对媒体类型进行详细描述。主要包括:音视频对应的负载类型,编码类型,传输协议,时钟频率等。

你可能感兴趣的:(FFmpeg)