(转)

从程序的结构来看,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建立及收发数据包的过程已经用代码写得非常清楚(这个好好分析一下源码就可以了),但我想,学习使用一下一些网络分析工具对自身也是颇为有益的。

live555 基本框架的更多相关文章

  1. live555流媒体框架介绍

    LIVE555 Streaming Media This code forms a set of C++ libraries for multimedia streaming, using open ...

  2. 用Darwin和live555实现的直播框架

    我们在开发视频直播或者监控类项目的时候,如场馆监控.学校监控.车载监控等等,往往首先希望的是形成一个项目的雏形,然后再在这个框架的基础上进行不断的完善和扩展工作,那么我们今天要给大家介绍的就是,如何形 ...

  3. Live555流媒体服务器编译(Windows下)

    最近在回顾之前做过的相关项目,其中包括live555流媒体服务器相关,今天先把live555开源框架在Windows下的编译方法记录一下. live555是一套使用使用开放的标准协议(RTP/RTCP ...

  4. Live555 实战之框架简单介绍

    作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 上一篇文章简要介绍了怎样以共享库的方式交叉编译Live555,今天再来介绍live源代码框架. 先 ...

  5. 值得推荐的C/C++框架和库

    值得推荐的C/C++框架和库 [本文系外部转贴,原文地址:http://coolshell.info/c/c++/2014/12/13/c-open-project.htm]留作存档 下次造轮子前先看 ...

  6. [转载]C/C++框架和库

    C/C++框架和库 装载自:http://blog.csdn.net/xiaoxiaoyeyaya/article/details/42541419 值得学习的C语言开源项目 Webbench Web ...

  7. 最全面的 C++ 资源、框架大全

    转载自   http://www.codeceo.com/article/cpp-resource-framework.html#0-tsina-1-99850-397232819ff9a47a7b7 ...

  8. 1.值得推荐的C/C++框架和库 (转)

    值得学习的C语言开源项目 - 1. Webbench Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的 ...

  9. 转:值得推荐的C/C++框架和库(真的很强大)

    目录(?)[+] 值得学习的C语言开源项目 - 1 Webbench - 2 Tinyhttpd - 3 cJSON - 4 CMockery - 5 Libev - 6 Memcached - 7 ...

随机推荐

  1. 【Activiti学习之七】BPMN子流程、顺序流、流程关口

    环境 JDK 1.8 MySQL 5.6 Tomcat 7 Eclipse-Luna activiti 6.0 一.子流程 1.嵌入子流程2.调用子流程3.事件子流程4.事务子流程 二.顺序流1.条件 ...

  2. mysql 基本操作 一

    1.mysql 管理语句 1)展示数据库列表 mysql> show databases; +--------------------+ | Database | +-------------- ...

  3. pod install 的历程

    公司项目拉下来执行有报错,建议执行pod install.这个是IOS的xcode第一次用的时候是需要的步骤 1.首先对于pod的命令你想执行,需要安装CocoaPods OS X 10.11以前,在 ...

  4. AKKA事件机制

    AKKA Event Bus 事件机制就用于当前运行环境,与集群环境不同,详细见AKKA 集群中的发布与订阅Distributed Publish Subscribe in Cluster 简单实现示 ...

  5. [转帖]【mount】Linux根目录空间不足

    [mount]Linux根目录空间不足 2019.04.15 21:30:47字数 1094阅读 107 一.问题背景 一台数据库服务器,突然监控告警,报根目录空间不足(no space left o ...

  6. 【Mac+Android+uiautomatorviewer】之报错:Unexpected error while obtaining UI hierarchy java.lang.reflect.InvocationTargetException

    安卓8.0以后uiautomatorviewer会报错,解决办法如下: 参考:<android 8.0 以后 uiautomator 无法直接使用的问题> 步骤一: 先执行命令:(注意:想 ...

  7. DDR3(2):初始化

    调取 DDR3 IP核后,是不能直接进行读写测试的,必须先进行初始化操作,对 IP 核进行校验.本篇采用 Modelsim 软件配合 DDR3 IP核生成的仿真模型,搭建出 IP核的初始化过程. 一. ...

  8. Oracle 加解密教程

    参考Oracle官方文档 在Oracle使用dbms_crypto包进行加解密 首先,授权当前用户使用加解密包 在sql中运行:connect sqlplus as sysdbagrant execu ...

  9. Java+Python+前端 学习资料大全 百度云盘

    Java架构师3大阶段 链接:https://pan.baidu.com/s/1DlXh33y5t4cZUmZH0cLvCw 提取码:5s76 2019前端架构阶段 链接:https://pan.ba ...

  10. swiper4基础

    这段时间在公司实习做前端,感觉前端没学习到很多前端框架,接口那些都是写好的,只需要调用就好,感觉没什么好记的,唯一觉得有必要记的就是swiper轮播了,在前端做网站的时候经常用到swiper做公告,图 ...