分享

【Live555】live555源码详解(九):ServerMediaSession、ServerMediaSubsession、live555MediaServer...

 waston 2021-10-20

【Live555】live555源码详解系列笔记

继承协作关系图

下面红色表示本博客将要介绍的三个类所在的位置:
ServerMediaSession、ServerMediaSubsession、DynamicRTSPServer
DynamicRTSPServer是live555MediaServer中实现的类,用来创建RTSP服务器
在这里插入图片描述

17、ServerMediaSession

ServerMediaSession 继承自 Medium,继承关系图:
在这里插入图片描述
协作图:
在这里插入图片描述

class ServerMediaSession: public Medium {
public:
/ 创建服务端媒体会话 ServerMediaSession
  static ServerMediaSession* createNew(UsageEnvironment& env,
       char const* streamName = NULL,
       char const* info = NULL,
       char const* description = NULL,
       Boolean isSSM = False,
       char const* miscSDPLines = NULL);

/ 通过名字获取服务端媒体会话 ServerMediaSession
  static Boolean lookupByName(UsageEnvironment& env,
                              char const* mediumName,
                              ServerMediaSession*& resultSession);

/ 生成会话描述,注意:调用者负责释放返回的字符串
  char* generateSDPDescription(); 

/ 返回流名称,例如媒体文件名
  char const* streamName() const { return fStreamName; }

/ 添加子会话
  Boolean addSubsession(ServerMediaSubsession* subsession);
  
/ 返回子会话数量
  unsigned numSubsessions() const { return fSubsessionCounter; }

/ 将“scale”设置为实际支持的比例:RTSP PLAY命令中的scale,通过调整这个数值,可以用来控制播放速度,也用来实现回放
  void testScaleFactor(float& scale);
  
/ 结果== 0表示:无限制会话(默认值)
/ 结果<  0表示:子会话持续时间不同;结果是-(最大的)/ 结果>  0表示:这是有界会话的持续时间
  float duration() const;

/ 当客户端访问此媒体时调用。
/ 默认实现什么都不做,但是子类可以重新定义它——例如,如果你想从服务器上删除长期未使用的“ServerMediaSession”。
  virtual void noteLiveness();

/ 返回引用数量,当引用数为0时,才可以删除
  unsigned referenceCount() const { return fReferenceCount; }
  
/ 增加引用
  void incrementReferenceCount() { ++fReferenceCount; }
  
/ 减少引用
  void decrementReferenceCount() { if (fReferenceCount > 0) --fReferenceCount; }
  
/ 判读是否可以删除
  Boolean& deleteWhenUnreferenced() { return fDeleteWhenUnreferenced; }

/ 删除所有由“addSubsession()”添加的子会话,返回到“空”状态
/ 注意:如果你已经添加了这个"ServerMediaSession""RTSPServer"/那么,在调用这个函数之前,你必须先通过调用"RTSPServer::closeAllClientSessionsForServerMediaSession()"来关闭使用它的客户端连接。
  void deleteAllSubsessions();

18、ServerMediaSubsession

ServerMediaSubsession 是一个纯虚类,继承自 Medium,继承关系图:
在这里插入图片描述
协作图:
在这里插入图片描述

class ServerMediaSubsession: public Medium {
public:
/ 返回子会话处理的是哪个轨道的数据(例如:音频、视频、字幕等)
  unsigned trackNumber() const { return fTrackNumber; }
/ 以字符串形式返回轨道信息:track%d
  char const* trackId();
  
/ 返回SDP媒体会话的所有描述内容
  virtual char const* sdpLines() = 0;
  
/ 获取流参数
  virtual void getStreamParameters(unsigned clientSessionId, // in
   netAddressBits clientAddress, // in
   Port const& clientRTPPort, // in
   Port const& clientRTCPPort, // in
   int tcpSocketNum, // in (-1 means use UDP, not TCP)
   unsigned char rtpChannelId, // in (used if TCP)
   unsigned char rtcpChannelId, // in (used if TCP)
   netAddressBits& destinationAddress, // in out
   u_int8_t& destinationTTL, // in out
   Boolean& isMulticast, // out
   Port& serverRTPPort, // out
   Port& serverRTCPPort, // out
   void*& streamToken // out
   ) = 0;
   
/ 启动流
  virtual void startStream(unsigned clientSessionId, void* streamToken,
   TaskFunc* rtcpRRHandler,
   void* rtcpRRHandlerClientData,
   unsigned short& rtpSeqNum,
   unsigned& rtpTimestamp,
   ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
   void* serverRequestAlternativeByteHandlerClientData) = 0;

/ 暂停流
  virtual void pauseStream(unsigned clientSessionId, void* streamToken);
  
/ 跳帧播放
  virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT,double streamDuration, u_int64_t& numBytes);
  virtual void seekStream(unsigned clientSessionId, void* streamToken, char*& absStart, char*& absEnd);

/"PLAY"命令没有指定的开始时间时调用???
  virtual void nullSeekStream(unsigned clientSessionId, void* streamToken, double streamEndTime, u_int64_t& numBytes);
  
/ 设置播放速度
  virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);
  
/获取当前正常播放时间NPT(Normal playing time)
  virtual float getCurrentNPT(void* streamToken);
  
/ 获取源
  virtual FramedSource* getStreamSource(void* streamToken);
  
/ 返回指向“streamToken”的“RTPSink”和“RTCPInstance”对象的指针。
/ 例如,如果您想要获得关联的“Groupsock”对象,这就很有用。
/ 您不能删除这些对象,或开始/停止播放它们;相反,这是使用“startStream()”和“deleteStream()”函数完成的。
  virtual void getRTPSinkandRTCP(void* streamToken, RTPSink const*& rtpSink, RTCPInstance const*& rtcp) = 0;
  virtual void deleteStream(unsigned clientSessionId, void*& streamToken);

/ 将“scale”设置为实际支持的比例:RTSP PLAY命令中的scale,通过调整这个数值,可以用来控制播放速度,也用来实现回放
  virtual void testScaleFactor(float& scale); // sets "scale" to the actual supported scale
  
/ 返回值 = 0 :持续播放
/ 返回值 > 0 :播放持续时间
  virtual float duration() const;

/ 子类可以通过“绝对”时间重新实现它们所支持的。
  virtual void getAbsoluteTimeRange(char*& absStartTime, char*& absEndTime) const;

/ (例如)SIP服务器可以调用以下命令,将SDP描述中的地址和端口号字段设置为非零:
  void setServerAddressAndPortForSDP(netAddressBits addressBits,  portNumBits portBits);

19、UserAuthenticationDatabase

用于可选用户/密码身份验证的数据结构,协作关系图:
在这里插入图片描述

class UserAuthenticationDatabase {
public:

/ 如果"passwordsAreMD5"为真,则存储到数据库或从数据库中删除的每个密码实际上都是md5计算的值(<username>:<realm>:<实际-密码>)UserAuthenticationDatabase(char const* realm = NULL, Boolean passwordsAreMD5 = False);
  virtual ~UserAuthenticationDatabase();

/ 添加用户记录
  virtual void addUserRecord(char const* username, char const* password);
  
/ 删除用户记录
  virtual void removeUserRecord(char const* username);

/ 获取密码,如果用户名不存在,则返回NULL
  virtual char const* lookupPassword(char const* username);

/ 默认值是"LIVE555 Streaming Media"。(不清楚什么作用)
  char const* realm() { return fRealm; }
  
/ 判读密码是否经过MD5加密
  Boolean passwordsAreMD5() { return fPasswordsAreMD5; }
}

live555MediaServer源码分析

1、主流程

1.1 创建任务调度器和环境

TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

1.2 创建 RTSPServer

UserAuthenticationDatabase* authDB = NULL;
portNumBits rtspServerPortNum = 554;
RTSPServer* rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);

1.3 尝试为 RTSP-over-HTTP 隧道创建一个HTTP服务器。

rtspServer->setUpTunnelingOverHTTP(80) // 尝试其它端口号:8000 8080

1.4 启动任务调度的事件循环

env->taskScheduler().doEventLoop(); 

2、DynamicRTSPServer 类分析

DynamicRTSPServer 继承自 RTSPServer
主要接口:

createNew:静态函数,调用 GenericMediaServer::setUpOurSocket ,然后创建 DynamicRTSPServer

重新实现的虚函数:

lookupServerMediaSession:获取 ServerMediaSession
live555MediaServer是将当前目录下的媒体文件通过RTSPServer发布出去。
lookupServerMediaSession 中首先根据名字streamName判读文件是否存在,如果存在,搜索 ServerMediaSession 是否已经创建,
结合其它条件,执行删除、创建、添加 ServerMediaSession 的操作

静态函数:

createNewSMS:创建服务会话
首先使用 ServerMediaSession::createNew 新建会话 ServerMediaSession *sms;
然后根据文件名的后缀创建不同的子会话,添加到会话sms中:sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(...))

对RTSP抓包,分析通讯流程

1、C–>S(客户端向服务端):OPTIONS命令查询服务器提供的方法
Request: OPTIONS rtsp://192.168.1.10:554/12 RTSP/1.0\r\n
CSeq: 2\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
\r\n

2、S–>C:回应OPTIONS
Response: RTSP/1.0 200 OK\r\n
CSeq: 2\r\n
Cache-Control: no-cache\r\n
Server: Hisilicon RTSP Streaming Media Server/1.0.0\r\n
Date: Thu, Jan 01 1970 00:02:47 GMT\r\n
Public: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN, SET_PARAMETER, GET_PARAMETER\r\n
\r\n

3、C–>S:DESCRIBE命令查询媒体文件的SDP(Session Description Protocol)会话描述信息
Request: DESCRIBE rtsp://192.168.1.10:554/12 RTSP/1.0\r\n
CSeq: 3\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
Accept: application/sdp\r\n //请求获得sdp信息
\r\n

4、S–>C (RTSP/SDP):回应DESCRIBE,并携带SDP信息
Real Time Streaming Protocol
Response: RTSP/1.0 200 OK\r\n
CSeq: 3\r\n
Content-type: application/sdp
Cache-Control: no-cache\r\n
Server: Hisilicon RTSP Streaming Media Server/1.0.0\r\n
Content-length: 380
Date: Thu, Jan 01 1970 00:02:47 GMT\r\n
\r\n

v=0 //协议版本
o=StreamingServer 3331435948 1116907222000 IN IP4 192.168.1.10 //所有者和会话标识符
s=12//会话名称
c=IN IP4 0.0.0.0
t=0 0
a=control:*
a=range:npt=0-
m=video 0 RTP/AVP 96//m:媒体行
a=framerate:30//a:属性行
a=transform:1,0,0;0,1,0;0,0,1
a=control:trackID=0
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=42002A;sprop-parameter-sets=Z0IAKp41wPAET8s3AQEBAg==,aM48gA==
a=x-dimensions:1920,1080

5、C–>S:SETUP命令告诉服务器 客户端用于接受媒体数据的端口号
Request: SETUP rtsp://192.168.1.10:554/12/trackID=0 RTSP/1.0\r\n
CSeq: 4\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
Transport: RTP/AVP;unicast;client_port=51374-51375 //unicast表示单播,用以区别组播;传输参数,RTP使用偶数,RTCP使用奇数
\r\n

6、S–>C:回应SETUP;至此流媒体连接建立完成
Response: RTSP/1.0 200 OK\r\n
CSeq: 4\r\n
Session: 106932986252082
Server: Hisilicon RTSP Streaming Media Server/1.0.0\r\n
Date: Thu, Jan 01 1970 00:02:47 GMT\r\n
Transport: RTP/AVP;unicast;client_port=51374-51375;server_port=5000-5001;ssrc=23bdcc95;mode=“PLAY”
\r\n

7、C–>S:PLAY命令,告诉服务端开始传输媒体流数据。
Request: PLAY rtsp://192.168.1.10:554/12 RTSP/1.0\r\n
CSeq: 5\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
Session: 106932986252082
Range: npt=0.000-\r\n
\r\n

8、S–>C:回应PLAY后,开始传输媒体数据流
Response: RTSP/1.0 200 OK\r\n
Server: Hisilicon RTSP Streaming Media Server/1.0.0\r\n
CSeq: 5\r\n
Session: 106932986252082
Date: Date: Thu, Jan 01 1970 00:02:49 GMT\r\n
Range: npt=0.000-\r\n
RTP-Info: url=rtsp://192.168.1.10:554/12/trackID=0;seq=0;rtptime=0,\r\n
\r\n

9、IPv4 RTP

x、C–>S
Request: GET_PARAMETER rtsp://192.168.1.10:554/12 RTSP/1.0\r\n
CSeq: 6\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
Session: 106932986252082
\r\n

n、IPv4 RTP

n+1、C–>S:TEARDOWN命令,终止媒体流传输
Request: TEARDOWN rtsp://192.168.1.10:554/12 RTSP/1.0\r\n
Method: TEARDOWN
URL: rtsp://192.168.1.10:554/12
CSeq: 7\r\n
User-Agent: LibVLC/2.2.8 (LIVE555 Streaming Media v2016.02.22)\r\n
Session: 378514285583215
\r\n

n+2、C–>S:RTCP Goodbye

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多