live555源码分析(八)多播

live555源码分析系列

live555源码分析(一)live555初体验

live555源码分析(二)基本组件上

live555源码分析(三)基本组件下

live555源码分析(四)RTSPServer分析

live555源码分析(五)DESCRIBE请求的处理

live555源码分析(六)SETUP和PLAY请求的处理

live555源码分析(七)播放过程

live555源码分析(八)多播

live555源码分析(八)多播

一、单播与多播实现区别

前面的例子实现的是单播的情况,其实多播也是差不多的,主要区别在于子会话的实现不同

在live555中,单播的子会话是继承于OnDemandServerMediaSubsession的,多播是使用PassiveServerMediaSubsession

单播与多播的主要区别在于开始播放后,会不会把客户端目的添加到发送目标中

单播会将每个客户端目的添加到发送目标中,而多播的发送目标只有多播地址

OnDemandServerMediaSubsession::startStream会将客户端目的添加到发送目标中

PassiveServerMediaSubsession::startStream并不会

二、多播示例注解

在live555中的testProgs\testH264VideoStreamer.cpp实现了一个多播H.264文件的例子

如果有仔细阅读前几篇文章,那么这个示例应该很容易看懂,本文将对这个示例进行注解,然后分析PassiveServerMediaSubsession

#include <liveMedia.hh>
#include <BasicUsageEnvironment.hh>
#include <GroupsockHelper.hh>

UsageEnvironment* env;
char const* inputFileName = "test.264";
H264VideoStreamFramer* videoSource;
RTPSink* videoSink;

void play();

int main(int argc, char** argv) {
  TaskScheduler* scheduler = BasicTaskScheduler::createNew(); // 调度器
  env = BasicUsageEnvironment::createNew(*scheduler); // 环境变量

  /* 随机产生多播地址 */
  struct in_addr destinationAddress;
  destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);

  /* 指定多播的RTP和RTCP端口 */
  const unsigned short rtpPortNum = 18888;
  const unsigned short rtcpPortNum = rtpPortNum+1;
  const unsigned char ttl = 255;
  const Port rtpPort(rtpPortNum);
  const Port rtcpPort(rtcpPortNum);

  Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
  rtpGroupsock.multicastSendOnly();
  Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
  rtcpGroupsock.multicastSendOnly();


  OutPacketBuffer::maxSize = 100000;

  /* 创建消费者,用于处理和发送H264的NALU */
  videoSink = H264VideoRTPSink::createNew(*env, &rtpGroupsock, 96);

  /* 创建RTCP */
  const unsigned estimatedSessionBandwidth = 500;
  const unsigned maxCNAMElen = 100;
  unsigned char CNAME[maxCNAMElen+1];
  gethostname((char*)CNAME, maxCNAMElen);
  CNAME[maxCNAMElen] = '\0';
  RTCPInstance* rtcp
  = RTCPInstance::createNew(*env, &rtcpGroupsock,
			    estimatedSessionBandwidth, CNAME,
			    videoSink, NULL,
			    True /* 多播 */);
  
  /* 创建RTSP服务器,指定端口8554 */
  RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  /* 创建一个会话 */
  ServerMediaSession* sms
    = ServerMediaSession::createNew(*env, "testStream", inputFileName,
		   "Session streamed by \"testH264VideoStreamer\"",
					   True /*SSM,此位表示是否为多播*/);
					   
  /* 添加子会话,子会话为H.264视频流 */
  sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
  rtspServer->addServerMediaSession(sms); // 将会话添加到会话中

  /* 打印会话的url */
  char* url = rtspServer->rtspURL(sms);
  *env << "Play this stream using the URL \"" << url << "\"\n";
  delete[] url;

  /* 开始播放 */
  *env << "Beginning streaming...\n";
  play();

  /* 开始事件循环 */
  env->taskScheduler().doEventLoop();

  return 0;
}

/* 清除资源,重新播放 */
void afterPlaying(void* /*clientData*/) {
  *env << "...done reading from file\n";
  videoSink->stopPlaying();
  Medium::close(videoSource);

  play();
}

void play() {
  /* 字节流读取文件 */
  ByteStreamFileSource* fileSource
    = ByteStreamFileSource::createNew(*env, inputFileName);
  if (fileSource == NULL) {
    *env << "Unable to open file \"" << inputFileName
         << "\" as a byte-stream file source\n";
    exit(1);
  }

  FramedSource* videoES = fileSource;

  /* 装饰者,读取NALU */
  videoSource = H264VideoStreamFramer::createNew(*env, videoES);

  *env << "Beginning to read from file...\n";
  /* 开始播放 */
  videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}

三、PassiveServerMediaSubsession分析

这里主要分析getStreamParameters函数还有startStream函数

getStreamParameters函数在DESCRIBE阶段被调用

startStream函数在PLAY阶段被调用

首先看getStreamParameters

void PassiveServerMediaSubsession
::getStreamParameters(unsigned clientSessionId,
		      netAddressBits clientAddress,
		      Port const& /*clientRTPPort*/,
		      Port const& clientRTCPPort,
		      int /*tcpSocketNum*/,
		      unsigned char /*rtpChannelId*/,
		      unsigned char /*rtcpChannelId*/,
		      netAddressBits& destinationAddress,
		      u_int8_t& destinationTTL,
		      Boolean& isMulticast,
		      Port& serverRTPPort,
		      Port& serverRTCPPort,
		      void*& streamToken) {
    /* 指定多播 */
    isMulticast = True;
 
    /* 服务端RTP和RTCP端口 */
    serverRTPPort = gs.port();
    serverRTCPPort = rtcpGS->port();
	...
        
    /* 将客户端的RTCP保存下来 */
	RTCPSourceRecord* source = new RTCPSourceRecord(clientAddress, clientRTCPPort);
    fClientRTCPSourceRecords->Add((char const*)clientSessionId, source);
}

可以看到会指定当前会话为多播,并且返回服务端的RTP和RTCP端口,但没有将客户端目的添加到发送目标中

这里将客户端的RTCP保存下来的原因是,虽然发送是多播,但是RTCP信息的接收还是采用单播,所以要独立处理每一个RTCP的接收

下面看一看startStream的实现

void PassiveServerMediaSubsession::startStream() {
    RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId));
    /* 指定RTCP的反射处理 */
    fRTCPInstance->setSpecificRRHandler(source->addr, source->port,
    						rtcpRRHandler, rtcpRRHandlerClientData);
}

它设置了客户端RTCP的反射处理

live555源码分析系列的文章到这里就结束了,希望对live555的初学者有所帮助

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值