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

live555源码分析系列

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

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

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

live555源码分析(四)RTSPServer分析

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

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

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

live555源码分析(八)多播

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

文章目录

  • live555源码分析(三)基本组件下
    • 一、Groupsock
      • 1.1 Groupsock的简单应用
      • 1.2 Groupsock源码分析
    • 二、RTPInterface
    • 三、RTCPInstance
    • 四、RTSPServer
    • 五、RTSPClientConnection
    • 六、RTSPClientSession
    • 七、ServerMediaSession
    • 八、ServerMediaSubsession
    • 九、FramedSource
    • 十、MediaSink

本文衔接上文,继续分析live555的主要类

一、Groupsock

Groupsock实现了UDP的单播和多播功能

1.1 Groupsock的简单应用

使用Groupsock单播

/* 创建Groupsock */
const unsigned char ttl = 255;
Groupsock groupsock(*env, "0.0.0.0", 0, ttl);

/* 清除所有的目的 */
groupsock.removeAllDestinations();

/* 添加目的地址 */
groupsock.addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId);
...

/* 向所有的目的发送数据 */
groupsock.output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize);

使用Groupsock多播

/* 获得多播地址 */
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);

/* 多播端口 */
const unsigned short portNum = 18888;
const Port port(portNum);

const unsigned char ttl = 255;

/* 创建Groupsock */
Groupsock rtpGroupsock(*env, destinationAddress, port, ttl);

/* 向多播地址发送数据 */
groupsock.output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize);

Groupsock维护着一个目的链表,当使用单播时,链表存放所有目的地址,当使用多播时,链表只有一项,存放目的多播地址

1.2 Groupsock源码分析

Groupsock的继承关系如下

live555源码分析(三)基本组件下_第1张图片

Socket的构造函数会创建一个UDP套接字

Groupsock的构造函数会将指定的目的地址添加到目的链表中

Socket::Socket(UsageEnvironment& env, Port port) {
    fSocketNum = setupDatagramSocket(fEnv, port);   
}
Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
                     Port port, u_int8_t ttl)
	: OutputSocket(env, port),
	fDests(new destRecord(groupAddr, port, ttl, 0, NULL)) //将目的地址添加到链表中
{
    
}

fDestsGroupsock维护的一个链表,如下

class Groupsock: public OutputSocket {
    ...
protected:
    destRecord* fDests;
};

看一看destRecord元素的定义

class destRecord {
    ...
public:
    destRecord* fNext; //用于维护链表
    GroupEId fGroupEId; //包含目的地址和目的端口
    unsigned fSessionId; //会话ID
};

下面看一看在单播应用的情况下,Groupsock如何添加目的地址

void Groupsock::addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId {
    /* 遍历链表 */
	for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
    	if()
            return;
    }

    /* 将目的地址插入链表中 */
    fDests = createNewDestRecord(addr, port, 255, sessionId, fDests);
}

首先判断要添加的目的地址是否存在于链表中,如果存在则返回,不存在则将其插入链表中

下面再看一看Groupsock的发送函数

Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
			  DirectedNetInterface* interfaceNotToFwdBackTo) {
	/* 遍历目的链表,对链表中的每个目的发送数据 */
    for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
    	write(dests->fGroupEId.groupAddress().s_addr, dests->fGroupEId.portNum(),
              dests->fGroupEId.ttl(),buffer, bufferSize);
    }
}

二、RTPInterface

RTPInterface是RTP接口,通过包含Groupsock实现了UDP单播和多播,另再实现了TCP单播

class RTPInterface {
	Groupsock* gs() const { return fGS; }
    
    /* 添加TCP目的 */
    void addStreamSocket(int sockNum, unsigned char streamChannelId);
    
private:
    Groupsock* fGS;
    tcpStreamRecord* fTCPStreams; //维护TCP目的发送对象链表
};

在UDP情况下添加目的发送对象,会通过获取RTPInterface中的Groupsock来操作

在TCP情况下,通过addStreamSocket来添加目的发送对象,RTPInterface为TCP目的发送对象维护一个链表

看一下TCP目的对象的定义

class tcpStreamRecord {
	...
public:
  tcpStreamRecord* fNext; //用于维护链表
  int fStreamSocketNum; //客户端套接字
  unsigned char fStreamChannelId; //会话ID
};

下面再看一看如何addStreamSocket的定义

void RTPInterface::addStreamSocket(int sockNum,
				   unsigned char streamChannelId) {
    /* 添加到链表中 */
	fTCPStreams = new tcpStreamRecord(sockNum, streamChannelId, fTCPStreams);
}

接下俩看RTPInterface的发送函数

Boolean RTPInterface::sendPacket(unsigned char* packet, unsigned packetSize) {
	/* 优先使用Groupsock发送 */
    if (!fGS->output(envir(), packet, packetSize)) success = False;

    /* 遍历TCP目的链表 */
    tcpStreamRecord* nextStream;
    for (tcpStreamRecord* stream = fTCPStreams; stream != NULL; stream = nextStream) {
		sendRTPorRTCPPacketOverTCP(packet, packetSize,
                                   stream->fStreamSocketNum, stream->fStreamChannelId)
    }
}

首先调用Groupsock发送,这是UDP的情况

然后会遍历TCP目的链表发送

这两种情况只有一种是有效的

三、RTCPInstance

RTCPInstance是RTCP实例,内含RTPInterface作为其传输接口,此外还定义了RTCP相关的一些内容

class RTCPInstance: public Medium {
public:
    ...
    void setSRHandler(TaskFunc* handlerTask, void* clientData);
    void setRRHandler(TaskFunc* handlerTask, void* clientData);
    
    void addStreamSocket(int sockNum, unsigned char streamChannelId);

    void incomingReportHandler1();
    void processIncomingReport(...);
	...
private:
    ...
	RTPInterface fRTCPInterface;
	...
};

四、RTSPServer

RTSPServer是RTSP服务器,负责处理客户端连接

RtspServer的继承关系如下

live555源码分析(三)基本组件下_第2张图片

RTSPServer会创建一个TCP套接字,交给GenericMediaServerGenericMediaServer会监听它

GenericMediaServer定义了创建客户端连接和创建客户端会话的虚函数,RTSPServer重新实现它们

RTSPServer接到一个客户端连接的时候,就会调用createNewClientConnection创建一个客户连接

五、RTSPClientConnection

RTSPClientConnection表示客户连接,定义了一系列的请求处理函数

class RTSPClientConnection: public GenericMediaServer::ClientConnection {
	...
protected:
	virtual void handleCmd_OPTIONS();
    virtual void handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
    ...
};

六、RTSPClientSession

RTSPClientSession表示客户端连接会话,在客户端发起SETUP请求时,就会建立一个RTSPClientSessionRTSPClientSession定义了一系列的请求处理函数

class RTSPClientSession: public GenericMediaServer::ClientSession {
protected:
	virtual void handleCmd_SETUP(RTSPClientConnection* ourClientConnection,
				 char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr);
    virtual void handleCmd_PLAY(RTSPClientConnection* ourClientConnection,
				ServerMediaSubsession* subsession, char const* fullRequestStr);
    
    ...
};

七、ServerMediaSession

ServerMediaSession表示服务器会话,在rtsp://ip:port/session里的session字段就是表示请求的一个会话

ServerMediaSession中维护一个ServerMediaSubsession链表管理子会话

ServerMediaSession的定义如下

class ServerMediaSession: public Medium {
public:
    ...
    /* 获取sdp信息 */
    char* generateSDPDescription();
    
    /* 添加子会话 */
    Boolean addSubsession(ServerMediaSubsession* subsession);
    ...
private:
    /* 维护一个ServerMediaSubsession链表 */
	ServerMediaSubsession* fSubsessionsHead;
    ServerMediaSubsession* fSubsessionsTail;
};

其中的generateSDPDescription函数是用于获取sdp信息,它将调用它每个子会话获取sdp媒体级信息

char* ServerMediaSession::generateSDPDescription() {
	char const* const sdpPrefixFmt =
      "v=0\r\n"
      "o=- %ld%06ld %d IN IP4 %s\r\n"
      "s=%s\r\n"
      "i=%s\r\n"

	...
        
    /* 遍历所有子会话获取sdp信息 */
    char* mediaSDP = sdp;
    for (subsession = fSubsessionsHead; subsession != NULL;
         subsession = subsession->fNext) {
        ...
        char const* sdpLines = subsession->sdpLines();
        ...
    }
}

addSubsession将子会话添加到链表中

ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
	fSubsessionsTail->fNext = subsession;
	fSubsessionsTail = subsession;
}

八、ServerMediaSubsession

ServerMediaSubsession表示子会话,其内含RTPSink,表示一路音频或者视频流

ServerMediaSubsession其实是一个虚基类,定义了一系列的虚函数,由派生类实现

ServerMediaSubsession的定义如下

class ServerMediaSubsession: public Medium {
    /* 获取媒体级的sdp描述 */
    virtual char const* sdpLines() = 0;
    
    virtual void getStreamParameters(...)=0; //SETUP阶段会调用
    virtual void startStream(...)=0; //PLAY阶段会调用
};

派生于ServerMediaSubsession的类由非常多

其中两个比较基本的是OnDemandServerMediaSubsessionPassiveServerMediaSubsession

OnDemandServerMediaSubsession实现了一个单播的子会话

PassiveServerMediaSubsession实现了一个多播的子会话

然后又有许多类派生于OnDemandServerMediaSubsession,如MPEG4VideoFileServerMediaSubsessionH264VideoFileServerMediaSubsession

live555源码分析(三)基本组件下_第3张图片

九、FramedSource

FramedSource是数据源,用于产生音视频数据

FramedSource是一个虚基类,定义了一个虚函数用于获取一帧数据,由派生类实现

class FramedSource: public MediaSource {
public:
    ...
	virtual void doGetNextFrame() = 0;
	...
};

派生于FramedSource的类由许多ByteStreamFileSourceH264VideoStreamFramer等等

FramedSource的派生类广泛应用了装饰者模式

举一个例子

ByteStreamFileSource* fileSource
	= ByteStreamFileSource::createNew(*env, inputFileName);

H264VideoStreamFramer* videoSource
	= H264VideoStreamFramer::createNew(*env, videoES);

其中ByteStreamFileSource的作用是以字节流的形式读取文件,将fileSource传递给H264VideoStreamFramer之后,videoSource的作用是解析H.264文件,获取一帧一帧的NALU

十、MediaSink

MediaSink是数据的消费者,它会从source获取音视频数据,然后进行处理,发送给客户端

MediaSink是一个虚基类,它定义了一系列的虚函数,由派生类实现

如何RTPSinkMediaSink的派生类,它定义了一些跟RTP相关的信息

MultiFramedRTPSink继承RTPSink,它处理了RTP的基本打包格式

H264VideoRTPSinkMPEG4ESVideoRTPSink等继承MultiFramedRTPSink,实现了某种特定格式的音视频数据的RTP打包

live555源码分析(三)基本组件下_第4张图片

MultiFramedRTPSink实现了RTP的头部打包,并且提供一个虚函数给派生类实现自己特定的RTP打包

class MultiFramedRTPSink: public RTPSink {
public:
    virtual void doSpecialFrameHandling(...); //派生类重新实现
    
private:
	void buildAndSendPacket(Boolean isFirstPacket); //此函数实现了RTP打包
}
void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {
	unsigned rtpHdr = 0x80000000;
    rtpHdr |= (fRTPPayloadType<<16);
    rtpHdr |= fSeqNo;
    fOutBuf->enqueueWord(rtpHdr);
    ...
}

关于live555的主要类就介绍到这里,后面的文章再来讨论细节

你可能感兴趣的:(live555源码分析与应用)