live555 基本框架

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

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

class UsageEnvironment {
public:
  void reclaim();

  // task scheduler:
  TaskScheduler& taskScheduler() const {return fScheduler;}

  // result message handling:
  typedef char const* MsgString;
  virtual MsgString getResultMsg() const = 0;

  virtual void setResultMsg(MsgString msg) = 0;
  virtual void setResultMsg(MsgString msg1, MsgString msg2) = 0;
  virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) = 0;
  virtual void setResultErrMsg(MsgString msg, int err = 0) = 0;
	// like setResultMsg(), except that an 'errno' message is appended.  (If "err == 0", the "getErrno()" code is used instead.)

  virtual void appendToResultMsg(MsgString msg) = 0;

  virtual void reportBackgroundError() = 0;
	// used to report a (previously set) error message within
	// a background event

  virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.

  // 'errno'
  virtual int getErrno() const = 0;

  // 'console' output:
  virtual UsageEnvironment& operator<<(char const* str) = 0;
  virtual UsageEnvironment& operator<<(int i) = 0;
  virtual UsageEnvironment& operator<<(unsigned u) = 0;
  virtual UsageEnvironment& operator<<(double d) = 0;
  virtual UsageEnvironment& operator<<(void* p) = 0;

  // a pointer to additional, optional, client-specific state
  void* liveMediaPriv;
  void* groupsockPriv;

protected:
  UsageEnvironment(TaskScheduler& scheduler); // abstract base class
  virtual ~UsageEnvironment(); // we are deleted only by reclaim()

private:
  TaskScheduler& fScheduler;

该库中还有一个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()函数

//改变目的地址的参数
//newDestAddr是新的目的地址
//newDestPort是新的目的端口
//newDestTTL是新的TTL
void Groupsock::changeDestinationParameters(
		struct in_addr const& newDestAddr,
		Port newDestPort,
		int newDestTTL)
{
	if (fDests == NULL)
		return;

	//获取第一个目的地址(此处不是很明白:fDest是一个单向链表,每次添加一个目的地址,
	//都会把它插入到最前目,难道这个函数仅改变最后一个添加的目的地址?)
	struct in_addr destAddr = fDests->fGroupEId.groupAddress();
	if (newDestAddr.s_addr != 0) {
		if (newDestAddr.s_addr != destAddr.s_addr
				&& IsMulticastAddress(newDestAddr.s_addr))
		{
			//如果目的地址是一个多播地址,则离开老的多播组,加入新的多播组。
			socketLeaveGroup(env(), socketNum(), destAddr.s_addr);
			socketJoinGroup(env(), socketNum(), newDestAddr.s_addr);
		}
		destAddr.s_addr = newDestAddr.s_addr;
	}

	portNumBits destPortNum = fDests->fGroupEId.portNum();
	if (newDestPort.num() != 0) {
		if (newDestPort.num() != destPortNum &&
				IsMulticastAddress(destAddr.s_addr))
		{
			//如果端口也不一样,则先更改本身socket的端口
			//(其实是关掉原先的socket的,再以新端口打开一个socket)。
			changePort(newDestPort);
			//然后把新的socket加入到新的多播组。
			// And rejoin the multicast group:
			socketJoinGroup(env(), socketNum(), destAddr.s_addr);
		}
		destPortNum = newDestPort.num();
		fDests->fPort = newDestPort;
	}

	u_int8_t destTTL = ttl();
	if (newDestTTL != ~0)
		destTTL = (u_int8_t) newDestTTL;

	//目标地址的所有信息都在fGroupEId中,所以改变成员fGroupEId。
	fDests->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
	
	//(看起来这个函数好像只用于改变多播时的地址参数,
	//以上分析是否合理,肯请高人指点)
}


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库的主要类结构。(注:其他单类及结构体等不在此列出)

Medium

RTSPServer

RTSPOverHTTPServer

MediaSession

ServerMediaSession

ServerMediaSubsession

OnDemandServerMediaSubsession

FileServerMediaSubsession

ADTSAudioFileServerMediaSubsession

AMRAudioFileServerMediaSubsession

H263plusVideoFileServerMediaSubsession

MP3AudioFileServerMediaSubsession

MPEG1or2VideoFileServerMediaSubsession

MPEG2TransportFileServerMediaSubsession

MPEG4VideoFileServerMediaSubsession

WAVAudioFileServerMediaSubsession

MPEG1or2DemuxedServerMediaSubsession

PassiveServerMediaSubsession

MediaSource

FramedSource

FramedFileSource

ByteStreamFileSource

ADTSAudioFileSource

MP3FileSource

MP3HTTPSource

BasicUDPSource

RTPSource

MultiFramedRTPSource

RawQCELPRTPSource

AC3AudioRTPSource

MPEG4GenericRTPSource

RawAMRRTPSource

H261VideoRTPSource

H263plusVideoRTPSource

H264VideoRTPSource

JPEGVideoRTPSource

MP3ADURTPSource

MPEG1or2AudioRTPSource

MPEG1or2VideoRTPSource

MPEG4ESVideoRTPSource

MPEG4GenericRTPSource

MPEG4LATMAudioRTPSource

DVVideoRTPSource

QuickTimeGenericRTPSource

SimpleRTPSource

AMRAudioSource

AMRDeinterleaver

AMRAudioFileSource

ByteStreamMultiFileSource

DeviceSource

JPEGVideoSource

MPEG1or2DemuxedElementaryStream

MPEG2TransportStreamMultiplexor

MPEG2TransportStreamFromESSource

MPEG2TransportStreamFromPESSource

AudioInputDevice

WAVAudioFileSource

FramedFilter

H264FUAFragmenter

QCELPDeinterleaver

AC3AudioStreamFramer

ADUFromMP3Source

uLawFromPCMAudioSource

H264VideoStreamFramer

MP3FromADUSource

MP3Transcoder

PCMFromuLawAudioSource

MPEG2IFrameIndexFromTransportStream

NetworkFromHostOrder16

HostFromNetworkOrder16

MP3ADUinterleaverBase

MP3ADUinterleaver

MP3ADUdeinterleaver

MPEG2TransportStreamFramer

EndianSwap16

H263plusVideoStreamFramer

MPEGVideoStreamFramer

MPEG1or2VideoStreamFramer

MPEG1or2VideoStreamDiscreteFramer

MPEG4VideoStreamFramer

MPEG4VideoStreamDiscreteFramer

MPEG1or2AudioStreamFramer

DVVideoStreamFramer

MP3ADUTranscoder

MPEG2TransportStreamTrickModeFilter

MediaSink

DummySink

BasicUDPSink

RTPSink

MultiFramedRTPSink

MPEG4GenericRTPSink

VideoRTPSink

H264VideoRTPSink

MPEG1or2VideoRTPSink

H263plusVideoRTPSink

JPEGVideoRTPSink

DVVideoRTPSink

MPEG4ESVideoRTPSink

AudioRTPSink

AC3AudioRTPSink

MPEG4LATMAudioRTPSink

GSMAudioRTPSink

MPEG1or2AudioRTPSink

AMRAudioRTPSink

MP3ADURTPSink

SimpleRTPSink

HTTPSink

MPEG1or2VideoHTTPSink

FileSink

AMRAudioFileSink

H264VideoFileSink

RTCPInstance

RTSPClient

SIPClient

DarwinInjector

QuickTimeFileSink

MPEG1or2Demux

MPEG2TransportStreamIndexFile

MPEG1or2FileServerDemux

AVIFileSink

 

BufferedPacketFactory

QCELPBufferedPacketFactory

AMRBufferedPacketFactory

MPEG4GenericBufferedPacketFactory

ADUBufferedPacketFactory

QTGenericBufferedPacketFactory

LATMBufferedPacketFactory

H264BufferedPacketFactory

JPEGBufferedPacketFactory

 

BufferedPacket

QCELPBufferedPacket

AMRBufferedPacket

MPEG4GenericBufferedPacket

ADUBufferedPacket

QTGenericBufferedPacket

LATMBufferedPacket

H264BufferedPacket

JPEGBufferedPacket

 

StreamParser

AC3AudioStreamParser

MPEGVideoStreamParser

MPEG1or2VideoStreamParser

MPEG4VideoStreamParser

MPEG1or2AudioStreamParser

H263plusVideoStreamParser

MPEGProgramStreamParser

 

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

class H264VideoFileServerMediaSubsession: public FileServerMediaSubsession {
public:
  static H264VideoFileServerMediaSubsession*
  createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource);

  // Used to implement "getAuxSDPLine()":
  void checkForAuxSDPLine1();
  void afterPlayingDummy1();

protected:
  H264VideoFileServerMediaSubsession(UsageEnvironment& env,
				      char const* fileName, Boolean reuseFirstSource);
      // called only by createNew();
  virtual ~H264VideoFileServerMediaSubsession();

  void setDoneFlag() { fDoneFlag = ~0; }

protected: // redefined virtual functions
  virtual char const* getAuxSDPLine(RTPSink* rtpSink,
				    FramedSource* inputSource);
  virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
					      unsigned& estBitrate);
  virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
                                    unsigned char rtpPayloadTypeIfDynamic,
				    FramedSource* inputSource);

private:
  char* fAuxSDPLine;
  char fDoneFlag; // used when setting up "fAuxSDPLine"
  RTPSink* fDummyRTPSink; // ditto
};

#endif


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

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

Medium

RTSPServer

MediaSession

ServerMediaSession

ServerMediaSubsession

OnDemandServerMediaSubsession

FileServerMediaSubsession

MP3AudioFileServerMediaSubsession

MediaSource

FramedSource

FramedFileSource

MP3FileSource

MP3HTTPSource

BasicUDPSource

RTPSource

MultiFramedRTPSource

MP3ADURTPSource

MPEG1or2AudioRTPSource

SimpleRTPSource

FramedFilter

ADUFromMP3Source

MP3FromADUSource

MP3Transcoder

MP3ADUinterleaverBase

MP3ADUinterleaver

MP3ADUdeinterleaver

MP3ADUTranscoder

MediaSink

BasicUDPSink

RTPSink

MultiFramedRTPSink

AudioRTPSink

MPEG1or2AudioRTPSink

MP3ADURTPSink

RTCPInstance

 

BufferedPacketFactory

ADUBufferedPacketFactory

 

BufferedPacket

ADUBufferedPacket

 

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

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

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

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

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

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

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

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

 

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



你可能感兴趣的:(live555)