【rtsp client取海康IPC H264视频流】——sdp数据格式

        rtsp client发送DESCIBE的时候,server响应的数据为sdp数据格式,sdp包含了音视频数据的信息。客户端发送“DESCRIBE”,同时Accept头为“application/sdp”。

        server响应“DESCRIBE”请求,其中body的内容为sdp数据格式。

DESCRIBE rtsp://192.168.1.65:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 7
Authorization: Digest username="admin", realm="IP Camera(G8735)", nonce="9790c5947f6ad1b894f5d68edd4715b2", uri="rtsp://192.168.1.65:554/cam/realmonitor?channel=1&subtype=0", response="ee47399fd2cf6fefd01be4527f784c21"
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Accept: application/sdp

RTSP/1.0 200 OK
CSeq: 7
Content-Type: application/sdp
Content-Base: rtsp://192.168.1.65:554/cam/realmonitor/
Content-Length: 625

v=0
o=- 1656081731594734 1656081731594734 IN IP4 192.168.1.65
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://192.168.1.65:554/cam/realmonitor/?channel=1&subtype=0
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.1.65:554/cam/realmonitor/trackID=1?channel=1&subtype=0
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpY1QPAET8s3AQEBQAABwgAAV+QB,aO4xsg==
a=Media_header:MEDIAINFO=494D4B48010300000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0    

        sdp由多行”<type>=<value>”组成,其中<type>是一个字符串,<value>是一个字符串,type表示类型,value的格式视type而定,整个协议区分大小写,”=”两侧不允许有空格

        sdp会话描述包含一个会话级描述(session_level_description)和多个媒体级描述(media_level description)组成!会话级描述的作用域是整个会话,其位置从”v=”行开始到第一个媒体描述为止;媒体级描述是对单个的媒体流进行描述,如传输过程中的视频流信息,从”m=”开始到下一个媒体描述为止,如下图所示。

根据server返回的body数据,我们可以得知会话级描述包含如下内容

v=0
o=- 1656081731594734 1656081731594734 IN IP4 192.168.1.65
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://192.168.1.65:554/cam/realmonitor/?channel=1&subtype=0

其中

v=<version>

o=<username> <session id> <version> <network type> <address type> <address>

s=<session name>

e=<email address>

b=<modifier>:<bandwidth-value>

t=<start time> <stop time>

a=<attribute>:<value>

媒体描述的信息如下

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.1.65:554/cam/realmonitor/trackID=1?channel=1&subtype=0
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpY1QPAET8s3AQEBQAABwgAAV+QB,aO4xsg==
a=Media_header:MEDIAINFO=494D4B48010300000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0

其中各字段含义如下:

m=<media> <port> <transport> <fmt list>

c=<network type> <address type> <connection address>

b=<modifier>:<bandwidth-value>

a=<attribute>:<value>

我们可以看到在会话描述级中和媒体描述中均存在a=control:,分别为

        a=control:rtsp://192.168.1.65:554/cam/realmonitor/?channel=1&subtype=0

        a=control:rtsp://192.168.1.65:554/cam/realmonitor/trackID=1?channel=1&subtype=0

这里的两个URL分别主要用到RTSP协议中PLAY、TEARDOWN和SETUP方法请求中,也就是说,当请求为PLAY或者TEARDOWN的时候采用会话中的URL,也就是a=control:rtsp://192.168.1.65:554/cam/realmonitor/?channel=1&subtype=0

当请求方法为SETUP的时候,采用媒体描述中的URL,也就是a=control:rtsp://192.168.1.65:554/cam/realmonitor/trackID=1?channel=1&subtype=0

这也就是我们后面看到SETUP和PLAY的URL不一样的原因,主要是采用sdp中的字段属性决定。也就如下抓包的数据。

SETUP方法

SETUP rtsp://192.168.1.65:554/cam/realmonitor/trackID=1?channel=1&subtype=0 RTSP/1.0

CSeq: 8

Authorization: Digest username="admin", realm="IP Camera(G8735)", nonce="9790c5947f6ad1b894f5d68edd4715b2", uri="rtsp://192.168.1.65:554/cam/realmonitor/", response="d064e6136d51a63c8e77a9b9e61ea942"

User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)

Transport: RTP/AVP;unicast;client_port=55122-55123

PLAY方法

PLAY rtsp://192.168.1.65:554/cam/realmonitor/?channel=1&subtype=0 RTSP/1.0

CSeq: 9

Authorization: Digest username="admin", realm="IP Camera(G8735)", nonce="9790c5947f6ad1b894f5d68edd4715b2", uri="rtsp://192.168.1.65:554/cam/realmonitor/", response="1658da5ce39c8e3b07f470c809a171f9"

User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)

Session: 1236328428

Range: npt=0.000-

如下代码实现对sdp数据中的会话url和媒体url的解析。

int sdp_info_parse(char *sdp_body,SDP_INFO_ST *p_sdp_info)
{
    int session_or_media = 0;
    char *p = NULL;
    char sdp_key;
    char sdp_value[256];
    SDP_MEDIA_TYPE sdp_media_type = SDP_UNKOWN_MEDIA;
    if(NULL == sdp_body || NULL == p_sdp_info){
        return -1;
    }
    //根据\n进行分行
    printf("[%s:%d]\n",__FUNCTION__,__LINE__);
    p = strtok(sdp_body,"\n");
    while(p != NULL){
        memset(sdp_value,0x00,sizeof(sdp_value));
        printf("sdp key is %c\n",sdp_key);
        sscanf(p,"%c=%s",&sdp_key,sdp_value);
        if(sdp_key == 'm'){
            session_or_media = 1;
            //解析media 目前只解析video & audio模块
            if(strstr(sdp_value,"video") != NULL){
                sdp_media_type = SDP_VIDEO_MEDIA;
            }
            else if(strstr(sdp_value,"audio") != NULL){
                sdp_media_type = SDP_AUDIO_MEDIA;
            }
            else{
                sdp_media_type = SDP_OTHER_MEDIA;
            }
        }
        if(session_or_media == 1){
            //media info
            if(sdp_media_type == SDP_VIDEO_MEDIA){
                if(sdp_key == 'a'){
                        if(strncmp(sdp_value,"control:",strlen("control:")) == 0){
                                memcpy(p_sdp_info->sdp_media_info.sdp_control,sdp_value+strlen("control:"),strlen(sdp_value)-strlen("control:"));
                        }
                }
            }
        }else{
            //session info
            if(sdp_key == 'v'){
                memcpy(p_sdp_info->sdp_session_info.sdp_v,sdp_value,strlen(sdp_value));
            }
            if(sdp_key == 'a'){
                if(strncmp(sdp_value,"control:",strlen("control:")) == 0){
                    memcpy(p_sdp_info->sdp_session_info.sdp_control,sdp_value+strlen("control:"),strlen(sdp_value)-strlen("control:"));
 }
            }
        }
        p = strtok(NULL,"\n");
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weihan0208/article/details/125886856