RTSP协议的处理--ANNOUNCE

 一、ABLMediaServer的过程

1.ProcessNetData处理数据的开始。

2.FillHttpHeadToStruct解析各个字段。

3.InputRtspData处理具体的方法

4.GetMediaURLFromRtspSDP函数中

szCurRtspURL为:rtsp://192.168.169.131:554/live/Camera_00001

5.GetRtspPathCount传入的szCurRtspURL

(1)返回的为有/的个数,即有app和stream才合法。

(2)填充这个结构 的“app[128];//添加流的应用名”和  “char stream[128];//添加流的id ”

//代理拉流转发参数结构
struct addStreamProxyStruct
{
    char  secret[256];//api操作密码 
    char  vhost[64];//添加流的虚拟主机
    char  app[128];//添加流的应用名
    char  stream[128];//添加流的id 
    char  url[512];//拉流地址 ,支持 rtsp\rtmp\http-flv \ hls 
    char  isRtspRecordURL[128];//是否rtsp录像回放 
    char  enable_mp4[64];//是否录像

    addStreamProxyStruct()
    {
        memset(secret, 0x00, sizeof(secret));
        memset(vhost, 0x00, sizeof(vhost));
        memset(app, 0x00, sizeof(app));
        memset(stream, 0x00, sizeof(stream));
        memset(url, 0x00, sizeof(url));
        memset(isRtspRecordURL, 0x00, sizeof(isRtspRecordURL));
        memset(enable_mp4, 0x00, sizeof(enable_mp4));
     }
};

 6.GetMediaStreamSource

(1)从xh_ABLMediaStreamSourceMap查找是否有这个流。

(2)xh_ABLMediaStreamSourceMap这个变量的定义。

typedef boost::shared_ptr<CMediaStreamSource> CMediaStreamSource_ptr;
typedef boost::unordered_map<string, CMediaStreamSource_ptr> CMediaStreamSource_ptrMap;
CMediaStreamSource_ptrMap xh_ABLMediaStreamSourceMap;

7.创建原始媒体流 pMediaSource = CreateMediaStreamSource(szMediaSourceURL,nClient, MediaSourceType_LiveMedia, 0);

(1)szMediaSourceURL参数如/live/Camera_00001,即app+stream。

(2)在CreateMediaStreamSource主要调用创建一个流,

pXHClient = boost::make_shared<CMediaStreamSource>(szURL,nClient, nSourceType, nDuration);

(3)CMediaStreamSource的构造函数对流进行初始化,对传入的参数szURL(szMediaSourceURL),解析为app、stream。

(4)创建的pXHClient插入到xh_ABLMediaStreamSourceMap。

xh_ABLMediaStreamSourceMap.insert(std::make_pair(strURL, pXHClient));

8.在对ANNOUNCE进行回重以后,开始解析sdp.

9.GetMediaInfoFromRtspSDP从sdp信息中获取视频、音频格式信息 ,根据这些信息创建rtp解包、rtp打包 

(1)没有解析会话描述。

(2)通过标识m=video和m=audio来区分视频和音频的描述 。

(3)视频和音频描述的分析通过CABLSipParse类。

(4)获取视频编码名称

  • 通过解析器,得到a=rtpmap的value,如a=rtpmap:96 H264/90000
  • 通过空格得到动态负载类型nVideoPayload和nAudioPayload即96和97
  • 通过/得到编码名称szVideoName和szAudioName即H264和MPEG4-GENERIC
  • 在音频描述中,通过/得到通过号及时钟频率及通道号。

10.CABLSipParse把SDP头全部装入map里面,尽可能找出详细的项数据

(1)重要的数据结构

 1 struct SipFieldStruct
 2 {
 3     char szKey[384];
 4     char szValue[1024*16];
 5     SipFieldStruct()
 6     {
 7         memset(szKey, 0x00, sizeof(szKey));
 8         memset(szValue, 0x00, sizeof(szValue));
 9     }
10 };

 

(2)重要的变量

typedef  map<string, SipFieldStruct*, less<string> > SipFieldStructMap;

(3)首先根据“\r\n”取出每行的数据。

(4)第一行,如“m=video 0 RTP/AVP 96”,通过空格,将key和value分开。

(5)其他行,如“b=AS:2457”,通过“:”,将key和value分开。

(6)插入mpa时,结构体的key为key, 结构体为value.

(7)把方法作为一个关键字,KEY,存储下来。这时key为“Method”,原结构的value变为key,然后插入map.

(8)如value有更多的信息,将作为了键插入到map中.

(9)子键通过“;”和“,”分隔,如:a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z00AKY2NQDwBE/LCAAAOEAACvyAI,aO44gA==; profile-level-id=4D0029\r\n

(10)子键的key和value通过"="分隔。

 11.GetSPSPPSFromDescribeSDP()从 SDP中获取  SPS,PPS 信息

(1)并没有从解析的类中得到,而是直接从SDP中得到。通过“sprop-parameter-sets=”及“;”得到。

(2)通过解压函数从得到解压过后的sps和pps.共33个字符:

{0x0, 0x0, 0x0, 0x1, 0x67, 0x4d, 0x0, 0x29, 0x8d, 0x8d, 0x40, 0x3c, 0x1, 0x13,
0xf2, 0xc2, 0x0, 0x0, 0xe, 0x10, 0x0, 0x2, 0xbf, 0x20, 0x8, 0x0, 0x0, 0x0, 0x1, 0x68, 0xee, 0x38, 0x80}

(3)SPS即Sequence Paramater Set,又称作序列参数集。SPS中保存了一组编码视频序列(Coded video sequence)的全局参数。

(4)PPS即Picturle Parameter Set图像参数集。

12.SDP的信息主要存在CNetRtspServer类中的下列三个变量中

(1) RtspProtect      RtspProtectArray[MaxRtspProtectCount];

(2)CABLSipParse sipParseV, sipParseA; //sdp 信息分析

  二、ZLMediaKit的过程

1.大的流程

(1)RtspSplitter::onRecvHeader(const char *data, size_t len) 

(2)RtspSplitter::onRecvContent(const char *data, size_t len)

(3)  onWholeRtspPacket(_parser);

(4) RtspSession::handleReq_ANNOUNCE(const Parser &parser)

2.RtspSplitter::onRecvHeader调用_parser.Parse(data)解析RTSP部分

(1)Parser//rtsp/http/sip解析类Parser的主要变量

std::string _strMethod;

std::string _strUrl;
std::string _strTail;
std::string _strContent;
std::string _strNull;
std::string _params;
mutable StrCaseMap _mapHeaders;
mutable StrCaseMap _mapUrlArgs;

(2)_parser.Parse(data)赋值的变量

  • _strMethod
  • _strUrl
  • _strTail
  • _mapHeaders
  • _strContent

各部分的具体值不再详述。

 3.onWholeRtspPacket中对_media_info赋值

(1)是对“rtsp://192.168.169.131:554/live/Camera_00001”解析

(2)MediaInfo的变量

std::string _full_url;//rtsp://192.168.169.131:554/live/Camera_00001
std::string _schema;//rtsp
std::string _host;//192.168.169.131
uint16_t _port = 0;//554
std::string _vhost;//虚拟主机(采用默认的__defaultVhost__)
std::string _app;//live
std::string _streamid;//Camera_00001
std::string _param_strs;//?以后的解析

(3) _media_info._schema = RTSP_SCHEMA;这个代码似乎没有用。

 4.RtspSession::handleReq_ANNOUNCE函数的大致过程

(1)定义了lambda函数auto onRes和Broadcast::PublishAuthInvoker invoker 。

(2)真正开始执行NoticeCenter::Instance().emitEvent代码。

(3)先执行invoker 再执行onRes。

5.SdpParser类

      解析SDP的类。

(1)void SdpParser::load(const string &sdp)函数是主要的函数。

(2)m=video 0 RTP/AVP 96 的解析

 if (4 == sscanf(opt_val.data(), " %15[^ ] %d %15[^ ] %d", type, &port, rtp, &pt))

  • []在正则表达式代表范围
  • ^在正则表达式代表非

(3)执行完load函数后, std::vector<SdpTrack::Ptr> _track_vec有三个track

  • 会话track

  •  视频track

  •   音频track

 (4)SDP的字符:

v=0
o=- 0 0 IN IP4 0.0.0.0
s=Streamed by ZLMediaKit(git hash:099845b,branch:master,build time:Nov 19 2022 12:35:24)
c=IN IP4 0.0.0.0
t=0 0
a=range:npt=now-
a=control:*
m=video 0 RTP/AVP 96
b=AS:2457
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; profile-level-id=4D0029; sprop-parameter-sets=Z00AKY2NQDwBE/LCAAAOEAACvyAI,aO44gA==
a=control:trackID=0
m=audio 0 RTP/AVP 98
a=rtpmap:98 mpeg4-generic/8000/1
a=fmtp:98 streamtype=5;

(5)Times(必选)-- _track_vec[0]

t=0 0

格式:t=<start time> <stop time>

描述:t字段描述了会话的开始时间和结束时间,<start time> <stop time>为NTP时间,单位是秒;如果<stop time>为0表示过了<start time>之后,会话一直持续;当<start time> 和<stop time>都为0的时候,表示持久会话;建议两个值不设为0,如果设为0,不知道开始时间和结束时间,增大了调度的难度.

(6)v=0-- _track_vec[0]

// v = 0 “v =”字段给出SDP的版本,默认为0

(7)o = (所有者/创建者和会话标识符)

o=- 0 0 IN IP4 0.0.0.0

// o = <username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
// <username> 是用户在始发主机上的登录名
// <sess-id> <sess-id>是一个数字字符串,使得<username><sess-id>nettype><addrtype>和<unicast-address>的元组形成会话的全局唯一标识符。
// <sess-version>是此会话描述的版本号
// <nettype>是一个给出网络类型的文本字符串。 “IN”被定义为具有“Internet”的含义
// <addrtype>是一个文本字符串,给出了后面的地址类型 定义了“IP4”和“IP6”
// <unicast-address>是创建会话的计算机的地址。
o=mozilla...THIS_IS_SDPARTA-67.0.4 7082731800936198286 0 IN IP4 0.0.0.0

(8) s = <会话名称> 

s =”字段是文本会话名称。每个会话描述必须有一个且只有一个“s =”字段。“s =”字段不能为空

s=Streamed by ZLMediaKit(git hash:099845b,branch:master,build time:Nov 19 2022 12:35:24)

(9)c=IN IP4 0.0.0.0

 // c=<nettype> <addrtype> <connection-address>

// 会话描述必须包含 每个媒体描述中的至少一个“c =”字段或会话级别的单个“c =”字段

// <nettype> “IN”被定义为具有“Internet”的含义

(10)a=range:npt=now-

 //用来表示媒体流的长度

a=range:npt=0-72.080000

   int ret = sscanf(it->second.data(), "%15[^=]=%15[^-]-%15s", name, start, end);
            if (3 == ret || 2 == ret) {
                if (strcmp(start, "now") == 0) {
                    strcpy(start, "0");
                }
                track._start = (float) atof(start);
                track._end = (float) atof(end);
                track._duration = track._end - track._start;
            }

 (11)a=(*)

格式 :a=<*>
描述:表示一个会话级别或媒体级别下的0个或多个属性

(12)m=video 0 RTP/AVP 96

   m=audio 0 RTP/AVP 98

media information(必选)

格式:m=<media> <port> <transport type> <fmt list>

描述:

<media>表示媒体类型

有"audio","video","application","data"(不向用户显示的数据),"control"(描述额外的控制通道);

<port>表示媒体流发往传输层的端口,对于RTP,偶数端口用来传输数据,奇数端口用来RTSP和RTCP;

<transport>表示传输协议,与"c="一行相关联,一般用RTP/AVP表示,即 Realtime Transport Protocol using the Audio/Video profile over udp,即我们常说的RTP over udp;

<fmt list>表示媒体格式,分为静态绑定和动态绑定

静态绑定:媒体编码方式与RTP负载类型有确定的一一对应关系,如: m=audio 0 RTP/AVP 8

动态绑定:媒体编码方式没有完全确定,需要使用rtpmap进行进一步的说明: 如:m=video 0 RTP/AVP 96

(13)a=rtpmap:96 H264/90000

          a=rtpmap:98 mpeg4-generic/8000/1

rtpmap(可选)

格式:a=rtpmap:<payload typee> <encoding name>/<clock rate>/<channel>

描述:

payload type表示动态负载类型,如 98表示h264

encoding name表示编码名称,如H.264

clock rate表示时钟频率,如90000

channel表示通过号

  for (it = track._attr.find("rtpmap"); it != track._attr.end() && it->first == "rtpmap";) {
            auto &rtpmap = it->second;
            int pt, samplerate, channel;
            char codec[16] = {0};

            sscanf(rtpmap.data(), "%d", &pt);
            if (track._pt != pt && track._pt != 0xff) {
                //pt不匹配
                it = track._attr.erase(it);
                continue;
            }
            if (4 == sscanf(rtpmap.data(), "%d %15[^/]/%d/%d", &pt, codec, &samplerate, &channel)) {
                track._codec = codec;
                track._samplerate = samplerate;
                track._channel = channel;
            } else if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
                track._pt = pt;
                track._codec = codec;
                track._samplerate = samplerate;
            }
            if (!track._samplerate && track._type == TrackVideo) {
                //未设置视频采样率时,赋值为90000
                track._samplerate = 90000;
            }
            ++it;
        }

(14)a=control:*

         a=control:trackID=0

         a=control:trackID=1

通过媒体流0、1来发送音频或视频

(15)a=fmtp:96 packetization-mode=1; profile-level-id=4D0029; sprop-parameter-sets=Z00AKY2NQDwBE/LCAAAOEAACvyAI,aO44gA==

         a=fmtp:98 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1588

 

        for (it = track._attr.find("fmtp"); it != track._attr.end() && it->first == "fmtp"; ) {
            auto &fmtp = it->second;
            int pt;
            sscanf(fmtp.data(), "%d", &pt);// 如96
            if (track._pt != pt && track._pt != 0xff) {
                //pt不匹配
                it = track._attr.erase(it);
                continue;
            }
            track._fmtp = FindField(fmtp.data(), " ", nullptr);//如96后面的字符
            +

 

a=fmtp:<payload type> <format specific parameters>

fmtp,格式参数,即 format parameters;

<payload type>:负载类型,同样对应 RTP 包中的音视频数据负载类型;

< format specific parameters>:指具体参数,或者说对音视频编码信息的一次处理。该信息从编码器得到,比如视频的SPS\PPS等,用于解码端的播放器初始化。
解释:这里面包含一些RTP封包模式,视频质量等级,视频的SPS、PPS等信息。

表示该路会话的的audio是通过RTP来格式传送的,其payload值为97。

当 packetization-mode 的值为 1 时,RTP打包H.264的NALU单元必须使用非交错(non-interleaved)封包模式.
当 profile-level-id的值为 42C01E 时, 第一个字节0x42表示 H.264 的 profile_idc类型Baseline profile , 第二字节代表profile_iop,各个Bit代表视频序列遵循的条款,第三个字节表示 H.264 的 Profile 级别,0x1E即30代表了levle_idc为3,即30/3,具体信息参考H.264的SPS PPS即可。

另一个解释这3个字节都是16进制表示。第一个字节表示profile_idc(0x42, 0x4d, 0x64),第二个字节表示profile-iop,第三个字节表示level。其中第一个字节和第三个字节比较好理解,

sprop-parameter-sets是SPS和PPS的的Base64之后的字符串,中间以逗号分割。

(16)b=AS:2457

          b=AS:32

 Bandwidth(可选)

格式: b=<modifier>:<bandwidth-value>

描述:该选项描述了建议的带宽,单位 kbs/s,可选,modifier包括两种类型,CT和AS,CT表示总带宽,AS表示单个媒体带宽的最大值;bandwidth-value表示具体的带宽。

posted @ 2022-11-09 17:01  泽良_小涛  阅读(802)  评论(0编辑  收藏  举报