• live555 基本框架


     

    从程序的结构来看,live项目包括了四个基本库、程序入口类(在mediaServer中)和一些测试代码(在testProgs中)。四个基本库是UsageEnvironment,BasicUsageEnvironment、groupsock和liveMedia。

    UsageEnvironment包括抽象类UsageEnvironment和抽象类TaskScheduler,这两个类用于事件调度,其中包括实现了对事件的异步读取、对事件句柄的设置及对错误信息的输出等;UsageEnvironment代表了整个系统运行的环境,它提供了错误记录和错误报告的功能,无论哪一个类要输出错误,就需要保存UsageEnvironment的指针.而TaskScheduler则提供了任务调度功能.整个程序的运行发动机就是它,它调度任务,执行任务(任务就是一个函数).TaskScheduler由于在全局中只有一个,所以保存在了UsageEnvironment中.而所有的类又都保存了UsageEnvironment的指针,所以谁想把自己的任务加入调度中,那是很容易的.在此还看到一个结论:整个live555(服务端)只有一个线程.

    1.  
      class UsageEnvironment {
    2.  
      public:
    3.  
      void reclaim();
    4.  
       
    5.  
      // task scheduler:
    6.  
      TaskScheduler& taskScheduler() const {return fScheduler;}
    7.  
       
    8.  
      // result message handling:
    9.  
      typedef char const* MsgString;
    10.  
      virtual MsgString getResultMsg() const = 0;
    11.  
       
    12.  
      virtual void setResultMsg(MsgString msg) = 0;
    13.  
      virtual void setResultMsg(MsgString msg1, MsgString msg2) = 0;
    14.  
      virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) = 0;
    15.  
      virtual void setResultErrMsg(MsgString msg, int err = 0) = 0;
    16.  
      // like setResultMsg(), except that an 'errno' message is appended. (If "err == 0", the "getErrno()" code is used instead.)
    17.  
       
    18.  
      virtual void appendToResultMsg(MsgString msg) = 0;
    19.  
       
    20.  
      virtual void reportBackgroundError() = 0;
    21.  
      // used to report a (previously set) error message within
    22.  
      // a background event
    23.  
       
    24.  
      virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.
    25.  
       
    26.  
      // 'errno'
    27.  
      virtual int getErrno() const = 0;
    28.  
       
    29.  
      // 'console' output:
    30.  
      virtual UsageEnvironment& operator<<(char const* str) = 0;
    31.  
      virtual UsageEnvironment& operator<<(int i) = 0;
    32.  
      virtual UsageEnvironment& operator<<(unsigned u) = 0;
    33.  
      virtual UsageEnvironment& operator<<(double d) = 0;
    34.  
      virtual UsageEnvironment& operator<<(void* p) = 0;
    35.  
       
    36.  
      // a pointer to additional, optional, client-specific state
    37.  
      void* liveMediaPriv;
    38.  
      void* groupsockPriv;
    39.  
       
    40.  
      protected:
    41.  
      UsageEnvironment(TaskScheduler& scheduler); // abstract base class
    42.  
      virtual ~UsageEnvironment(); // we are deleted only by reclaim()
    43.  
       
    44.  
      private:
    45.  
      TaskScheduler& fScheduler;
    46.  

    该库中还有一个HashTable,这是一个通用的HashTable,在整个项目中都可以使用它,当然该HashTable也是一个抽象类。

    BasicUsageEnvironment中的类主要是对UsageEnvironment中对应类的实现。

    groupsock,顾名思义,用于数据包的接收和发送,其同时支持多播和单播。groupsock库中包括了GroupEId、Groupsock、GroupsockHelper、NetAddress、NetInterface等类,其中Groupsock类有两个构造函数,一个是“for a source-independent multicast group”,另一个是“for a source-specific multicast group”;而GroupsockHelper类主要用于读写Socket。Groupsock的构造函数有一个参数是struct in_addr const& groupAddr,在构造函数中首先会调用父类构造函数创建socket对象,然后判断这个地址,若是多播地址,则加入多播组。Groupsock的两个成员变量destRecord* fDests和DirectedNetInterfaceSet fMembers都表示目的地址集和,但我始终看不出DirectedNetInterfaceSet fMembers有什么用,且DirectedNetInterfaceSet是一个没有被继承的虚类,看起来fMembers没有什么用。仅fDesk也够用了,在addDestination()和removeDestination()函数中就是操作fDesk,添加或删除目的地址。

    Groupsock::changeDestinationParameters()函数

    1.  
      //改变目的地址的参数
    2.  
      //newDestAddr是新的目的地址
    3.  
      //newDestPort是新的目的端口
    4.  
      //newDestTTL是新的TTL
    5.  
      void Groupsock::changeDestinationParameters(
    6.  
      struct in_addr const& newDestAddr,
    7.  
      Port newDestPort,
    8.  
      int newDestTTL)
    9.  
      {
    10.  
      if (fDests == NULL)
    11.  
      return;
    12.  
       
    13.  
      //获取第一个目的地址(此处不是很明白:fDest是一个单向链表,每次添加一个目的地址,
    14.  
      //都会把它插入到最前目,难道这个函数仅改变最后一个添加的目的地址?)
    15.  
      struct in_addr destAddr = fDests->fGroupEId.groupAddress();
    16.  
      if (newDestAddr.s_addr != 0) {
    17.  
      if (newDestAddr.s_addr != destAddr.s_addr
    18.  
      && IsMulticastAddress(newDestAddr.s_addr))
    19.  
      {
    20.  
      //如果目的地址是一个多播地址,则离开老的多播组,加入新的多播组。
    21.  
      socketLeaveGroup(env(), socketNum(), destAddr.s_addr);
    22.  
      socketJoinGroup(env(), socketNum(), newDestAddr.s_addr);
    23.  
      }
    24.  
      destAddr.s_addr = newDestAddr.s_addr;
    25.  
      }
    26.  
       
    27.  
      portNumBits destPortNum = fDests->fGroupEId.portNum();
    28.  
      if (newDestPort.num() != 0) {
    29.  
      if (newDestPort.num() != destPortNum &&
    30.  
      IsMulticastAddress(destAddr.s_addr))
    31.  
      {
    32.  
      //如果端口也不一样,则先更改本身socket的端口
    33.  
      //(其实是关掉原先的socket的,再以新端口打开一个socket)。
    34.  
      changePort(newDestPort);
    35.  
      //然后把新的socket加入到新的多播组。
    36.  
      // And rejoin the multicast group:
    37.  
      socketJoinGroup(env(), socketNum(), destAddr.s_addr);
    38.  
      }
    39.  
      destPortNum = newDestPort.num();
    40.  
      fDests->fPort = newDestPort;
    41.  
      }
    42.  
       
    43.  
      u_int8_t destTTL = ttl();
    44.  
      if (newDestTTL != ~0)
    45.  
      destTTL = (u_int8_t) newDestTTL;
    46.  
       
    47.  
      //目标地址的所有信息都在fGroupEId中,所以改变成员fGroupEId。
    48.  
      fDests->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
    49.  
       
    50.  
      //(看起来这个函数好像只用于改变多播时的地址参数,
    51.  
      //以上分析是否合理,肯请高人指点)
    52.  
      }


    liveMedia是很重要的一个库,其不仅包含了实现RTSP Server的类,还包含了针对不同流媒体类型(如TS流、PS流等)编码的类。在该库中,基类是Medium,层次关系非常清晰。在该库中,有几个很重要的类,如RTSPServer、ServerMediaSession、RTPSink、RTPInterface、FramedSource等。

    http://www.live555.com上的相关文档中提到穿透防火墙的问题,方法是开启一个HTTP的tunnel,然后我们可以在liveMedia库中找到一个RTSPOverHTTPServer的类,该类解决了这样的问题。

    mediaServer下的live555MediaServer提供了main函数,DynamicRTSPServer继承了RTSPServer并重写了虚函数lookupServerMediaSession。

    鉴于UsageEnvironment库、BasicUsageEnvironment库和groupsock库中的类较少,就暂且不作分析了。这里主要针对liveMedia库中的主要类结构进行分析。通过查看类关系图,可以从整体把握,但是苦于类太多,用类关系图看起来也不方便,于是就自己重新整理了一下,下面是 liveMedia库的主要类结构。(注:其他单类及结构体等不在此列出)

    l  Medium

    n  RTSPServer

    n  RTSPOverHTTPServer

    n  MediaSession

    n  ServerMediaSession

    n  ServerMediaSubsession

    u  OnDemandServerMediaSubsession

    l  FileServerMediaSubsession

    n  ADTSAudioFileServerMediaSubsession

    n  AMRAudioFileServerMediaSubsession

    n  H263plusVideoFileServerMediaSubsession

    n  MP3AudioFileServerMediaSubsession

    n  MPEG1or2VideoFileServerMediaSubsession

    n  MPEG2TransportFileServerMediaSubsession

    n  MPEG4VideoFileServerMediaSubsession

    n  WAVAudioFileServerMediaSubsession

    l  MPEG1or2DemuxedServerMediaSubsession

    u  PassiveServerMediaSubsession

    n  MediaSource

    u  FramedSource

    l  FramedFileSource

    n  ByteStreamFileSource

    n  ADTSAudioFileSource

    n  MP3FileSource

    u  MP3HTTPSource

    l  BasicUDPSource

    l  RTPSource

    n  MultiFramedRTPSource

    u  RawQCELPRTPSource

    u  AC3AudioRTPSource

    u  MPEG4GenericRTPSource

    u  RawAMRRTPSource

    u  H261VideoRTPSource

    u  H263plusVideoRTPSource

    u  H264VideoRTPSource

    u  JPEGVideoRTPSource

    u  MP3ADURTPSource

    u  MPEG1or2AudioRTPSource

    u  MPEG1or2VideoRTPSource

    u  MPEG4ESVideoRTPSource

    u  MPEG4GenericRTPSource

    u  MPEG4LATMAudioRTPSource

    u  DVVideoRTPSource

    u  QuickTimeGenericRTPSource

    u  SimpleRTPSource

    l  AMRAudioSource

    n  AMRDeinterleaver

    n  AMRAudioFileSource

    l  ByteStreamMultiFileSource

    l  DeviceSource

    l  JPEGVideoSource

    l  MPEG1or2DemuxedElementaryStream

    l  MPEG2TransportStreamMultiplexor

    n  MPEG2TransportStreamFromESSource

    n  MPEG2TransportStreamFromPESSource

    l  AudioInputDevice

    n  WAVAudioFileSource

    l  FramedFilter

    n  H264FUAFragmenter

    n  QCELPDeinterleaver

    n  AC3AudioStreamFramer

    n  ADUFromMP3Source

    n  uLawFromPCMAudioSource

    n  H264VideoStreamFramer

    n  MP3FromADUSource

    u  MP3Transcoder

    n  PCMFromuLawAudioSource

    n  MPEG2IFrameIndexFromTransportStream

    n  NetworkFromHostOrder16

    n  HostFromNetworkOrder16

    n  MP3ADUinterleaverBase

    u  MP3ADUinterleaver

    u  MP3ADUdeinterleaver

    n  MPEG2TransportStreamFramer

    n  EndianSwap16

    n  H263plusVideoStreamFramer

    n  MPEGVideoStreamFramer

    u  MPEG1or2VideoStreamFramer

    l  MPEG1or2VideoStreamDiscreteFramer

    u  MPEG4VideoStreamFramer

    l  MPEG4VideoStreamDiscreteFramer

    n  MPEG1or2AudioStreamFramer

    n  DVVideoStreamFramer

    n  MP3ADUTranscoder

    n  MPEG2TransportStreamTrickModeFilter

    n  MediaSink

    u  DummySink

    u  BasicUDPSink

    u  RTPSink

    l  MultiFramedRTPSink

    n  MPEG4GenericRTPSink

    n  VideoRTPSink

    u  H264VideoRTPSink

    u  MPEG1or2VideoRTPSink

    u  H263plusVideoRTPSink

    u  JPEGVideoRTPSink

    u  DVVideoRTPSink

    u  MPEG4ESVideoRTPSink

    n  AudioRTPSink

    u  AC3AudioRTPSink

    u  MPEG4LATMAudioRTPSink

    u  GSMAudioRTPSink

    u  MPEG1or2AudioRTPSink

    u  AMRAudioRTPSink

    u  MP3ADURTPSink

    n  SimpleRTPSink

    u  HTTPSink

    l  MPEG1or2VideoHTTPSink

    u  FileSink

    l  AMRAudioFileSink

    l  H264VideoFileSink

    n  RTCPInstance

    n  RTSPClient

    n  SIPClient

    n  DarwinInjector

    n  QuickTimeFileSink

    n  MPEG1or2Demux

    n  MPEG2TransportStreamIndexFile

    n  MPEG1or2FileServerDemux

    n  AVIFileSink

    l  BufferedPacketFactory

    n  QCELPBufferedPacketFactory

    n  AMRBufferedPacketFactory

    n  MPEG4GenericBufferedPacketFactory

    n  ADUBufferedPacketFactory

    n  QTGenericBufferedPacketFactory

    n  LATMBufferedPacketFactory

    n  H264BufferedPacketFactory

    n  JPEGBufferedPacketFactory

    l  BufferedPacket

    n  QCELPBufferedPacket

    n  AMRBufferedPacket

    n  MPEG4GenericBufferedPacket

    n  ADUBufferedPacket

    n  QTGenericBufferedPacket

    n  LATMBufferedPacket

    n  H264BufferedPacket

    n  JPEGBufferedPacket

    l  StreamParser

    n  AC3AudioStreamParser

    n  MPEGVideoStreamParser

    u  MPEG1or2VideoStreamParser

    u  MPEG4VideoStreamParser

    n  MPEG1or2AudioStreamParser

    n  H263plusVideoStreamParser

    n  MPEGProgramStreamParser

    从上面这个主要的类结构可以看出,liveMedia库中的基类为Medium,其下又有几个非常重要的部分,一个是×××Subsession,除Medium父类外,所有的×××Subsession类都继承于ServerMediaSubsession;一个是×××Source,MediaSource的frameSource下主要包含FramedFileSource、RTPSource、FramedFilter等几个主要的部分;一个是MediaSink,以继承于RTPSink的类居多。

    1.  
      class H264VideoFileServerMediaSubsession: public FileServerMediaSubsession {
    2.  
      public:
    3.  
      static H264VideoFileServerMediaSubsession*
    4.  
      createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource);
    5.  
       
    6.  
      // Used to implement "getAuxSDPLine()":
    7.  
      void checkForAuxSDPLine1();
    8.  
      void afterPlayingDummy1();
    9.  
       
    10.  
      protected:
    11.  
      H264VideoFileServerMediaSubsession(UsageEnvironment& env,
    12.  
      char const* fileName, Boolean reuseFirstSource);
    13.  
      // called only by createNew();
    14.  
      virtual ~H264VideoFileServerMediaSubsession();
    15.  
       
    16.  
      void setDoneFlag() { fDoneFlag = ~0; }
    17.  
       
    18.  
      protected: // redefined virtual functions
    19.  
      virtual char const* getAuxSDPLine(RTPSink* rtpSink,
    20.  
      FramedSource* inputSource);
    21.  
      virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
    22.  
      unsigned& estBitrate);
    23.  
      virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
    24.  
      unsigned char rtpPayloadTypeIfDynamic,
    25.  
      FramedSource* inputSource);
    26.  
       
    27.  
      private:
    28.  
      char* fAuxSDPLine;
    29.  
      char fDoneFlag; // used when setting up "fAuxSDPLine"
    30.  
      RTPSink* fDummyRTPSink; // ditto
    31.  
      };
    32.  
       
    33.  
      #endif


    此外,还包含了用于处理packet的BufferedPacketFactory和BufferedPacket及其相关子类,用于处理流分析的StreamParser及其子类。

    基本上,整个liveMedia库的主要类结构就是这样。不过,类太多了,分析起来还是有较大的困难。于是乎,采取去掉枝叶保留主干的做法,将整个服务器精简成支持一种格式的服务器,如MP3流服务器。经过分离,一个基于MP3的测试服务器的主要类结构如下所示。(注:其他单类及结构体等不在此列出)

    l  Medium

    n  RTSPServer

    n  MediaSession

    n  ServerMediaSession

    n  ServerMediaSubsession

    u  OnDemandServerMediaSubsession

    l  FileServerMediaSubsession

    n  MP3AudioFileServerMediaSubsession

    n  MediaSource

    u  FramedSource

    l  FramedFileSource

    n  MP3FileSource

    u  MP3HTTPSource

    l  BasicUDPSource

    l  RTPSource

    n  MultiFramedRTPSource

    u  MP3ADURTPSource

    u  MPEG1or2AudioRTPSource

    u  SimpleRTPSource

    l  FramedFilter

    n  ADUFromMP3Source

    n  MP3FromADUSource

    u  MP3Transcoder

    n  MP3ADUinterleaverBase

    u  MP3ADUinterleaver

    u  MP3ADUdeinterleaver

    n  MP3ADUTranscoder

    n  MediaSink

    u  BasicUDPSink

    u  RTPSink

    l  MultiFramedRTPSink

    n  AudioRTPSink

    u  MPEG1or2AudioRTPSink

    u  MP3ADURTPSink

    n  RTCPInstance

    l  BufferedPacketFactory

    n  ADUBufferedPacketFactory

    l  BufferedPacket

    n  ADUBufferedPacket

    根据上面这种相对简单的类结构,分析起来就方便多了。于是,开始进入具体的分析细节了,这是一个相对漫长的过程,再加上本人的C++水平有限,这又将是一个相对艰苦的过程,一切慢慢来吧……

    末了,讲讲启动服务器的过程:

    LIVE555是一个纯粹的RTSP服务器,其服务器主类为liveMedia库下的RTSPServer;mediaServer下的live555MediaServer为主程序的入口类,DynamicRTSPServer是RTSPServer的实现类。

    从live555MediaServer类的入口函数main中可以非常清晰地分析出服务器的启动过程。

    首先是createNew一个TaskSchedulers对象和一个UsageEnvironment对象,这是初始工作。

    之后是一段访问控制的代码。然后开始进入创建RTSP服务器的代码段,服务器指定了一个默认端口554和一个可供替代的端口8554。

    接下来,createNew一个DynamicRTSPServer,这里建立了Socket(ourSocket)在TCP的554端口(默认端口)进行监听,然后把连接处理函数句柄和socket句柄传给任务调度器(即taskScheduler),既是RTSPServer类中的这句代码:env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket,        (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandler,this)。紧接着就是对socket句柄和incomingConnectionHandler句柄的处理,主要是进行关联等。

    最后,进入主循环(即env->taskScheduler().doEventLoop();),等待客户端连接。服务器启动完毕。

        文章的最后,需要说明的是,在编译运行的过程中,我是使用VLC播放器来进行测试的,同时通过使用Ethereal的网络分析工具抓包分析其建立到传输的过程,虽然在live555源代码中关于RTSP建立及收发数据包的过程已经用代码写得非常清楚(这个好好分析一下源码就可以了),但我想,学习使用一下一些网络分析工具对自身也是颇为有益的。

  • 相关阅读:
    javascript js date ios手机浏览器出现 NAN的问题解决方法
    DWG TrueView 2022
    FloatingActionButton(悬浮按钮)使用学习<一>
    Android多线程编程<一>Android中启动子线程的方法
    Android多线程编程<二>Handler异步消息处理机制之Message
    Java:类的构造函数
    Java:类与对象概念
    APP免邀请码安装
    .NET4安装总进度一直不动的解决办法
    bitmap缩放时抗锯齿
  • 原文地址:https://www.cnblogs.com/bigfi/p/9402091.html
Copyright © 2020-2023  润新知