证据一:向所有客户端发出数据:
Boolean RTPInterface::sendPacket(unsigned char* packet, unsigned packetSize) { Boolean success = True; // we'll return False instead if any of the sends fail // Normal case: Send as a UDP packet: if (!fGS->output(envir(), fGS->ttl(), packet, packetSize)) success = False; // Also, send over each of our TCP sockets: for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; streams = streams->fNext) { if (!sendRTPOverTCP(packet, packetSize, streams->fStreamSocketNum, streams->fStreamChannelId)) { success = False; } } return success; }很明显啊,先发送udp数据,一对多的问题在GroupSocket中解决。再发送tcp数据,一对多的问题本地解决。
void RTPInterface::setStreamSocket(int sockNum, unsigned char streamChannelId) { fGS->removeAllDestinations(); addStreamSocket(sockNum, streamChannelId); } void RTPInterface::addStreamSocket(int sockNum, unsigned char streamChannelId) { if (sockNum < 0) return; for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; streams = streams->fNext) { if (streams->fStreamSocketNum == sockNum && streams->fStreamChannelId == streamChannelId) { return; // we already have it } } fTCPStreams = new tcpStreamRecord(sockNum, streamChannelId, fTCPStreams); }setStreamSocket()没必要说了吧,看一下addStreamSocke()。从字面意思应能了解,添加一个流式Socket,也就是添加tcp
void RTPInterface::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) { // Normal case: Arrange to read UDP packets: envir().taskScheduler().turnOnBackgroundReadHandling(fGS->socketNum(),handlerProc, fOwner); // Also, receive RTP over TCP, on each of our TCP connections: fReadHandlerProc = handlerProc; for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; streams = streams->fNext) { // Get a socket descriptor for "streams->fStreamSocketNum": SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum); // Tell it about our subChannel: socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this); } }用UDP时很简单,直接把处理函数做为handler加入taskScheduler即可。而TCP时,需向所有的session的socket都注册自己。可以想像,socketDescriptor代表一个tcp socket,并且它有一个链表之类的东西,其中保存了所有的对这个socket感兴趣的RTPInterface,同时也记录了RTPInterface对应的channal id。只有向socketDescriptor注册了自己,socketDescriptor在读取数据时,才能跟据分析出的channel id找到对应的RTPInterface,才能调用RTPInterface中的数据处理handler,当然,这个函数也不是RTPInteface自己的,而是从startNetworkReading()这个函数接收到的调用者的。
void SocketDescriptor::registerRTPInterface( unsigned char streamChannelId, RTPInterface* rtpInterface) { Boolean isFirstRegistration = fSubChannelHashTable->IsEmpty(); fSubChannelHashTable->Add((char const*) (long) streamChannelId, rtpInterface); if (isFirstRegistration) { // Arrange to handle reads on this TCP socket: TaskScheduler::BackgroundHandlerProc* handler = (TaskScheduler::BackgroundHandlerProc*) &tcpReadHandler; fEnv.taskScheduler().turnOnBackgroundReadHandling(fOurSocketNum, handler, this); } }可见在注册第一个多路复用对象时启动reand handler。看一下函数主体:
void SocketDescriptor::tcpReadHandler1(int mask) { // We expect the following data over the TCP channel: // optional RTSP command or response bytes (before the first '$' character) // a '$' character // a 1-byte channel id // a 2-byte packet size (in network byte order) // the packet data. // However, because the socket is being read asynchronously, this data might arrive in pieces. u_int8_t c; struct sockaddr_in fromAddress; if (fTCPReadingState != AWAITING_PACKET_DATA) { int result = readSocket(fEnv, fOurSocketNum, &c, 1, fromAddress); if (result != 1) { // error reading TCP socket, or no more data available if (result < 0) { // error fEnv.taskScheduler().turnOffBackgroundReadHandling( fOurSocketNum); // stops further calls to us } return; } } switch (fTCPReadingState) { case AWAITING_DOLLAR: { if (c == '$') { fTCPReadingState = AWAITING_STREAM_CHANNEL_ID; } else { // This character is part of a RTSP request or command, which is handled separately: if (fServerRequestAlternativeByteHandler != NULL) { (*fServerRequestAlternativeByteHandler)( fServerRequestAlternativeByteHandlerClientData, c); } } break; } case AWAITING_STREAM_CHANNEL_ID: { // The byte that we read is the stream channel id. if (lookupRTPInterface(c) != NULL) { // sanity check fStreamChannelId = c; fTCPReadingState = AWAITING_SIZE1; } else { // This wasn't a stream channel id that we expected. We're (somehow) in a strange state. Try to recover: fTCPReadingState = AWAITING_DOLLAR; } break; } case AWAITING_SIZE1: { // The byte that we read is the first (high) byte of the 16-bit RTP or RTCP packet 'size'. fSizeByte1 = c; fTCPReadingState = AWAITING_SIZE2; break; } case AWAITING_SIZE2: { // The byte that we read is the second (low) byte of the 16-bit RTP or RTCP packet 'size'. unsigned short size = (fSizeByte1 << 8) | c; // Record the information about the packet data that will be read next: RTPInterface* rtpInterface = lookupRTPInterface(fStreamChannelId); if (rtpInterface != NULL) { rtpInterface->fNextTCPReadSize = size; rtpInterface->fNextTCPReadStreamSocketNum = fOurSocketNum; rtpInterface->fNextTCPReadStreamChannelId = fStreamChannelId; } fTCPReadingState = AWAITING_PACKET_DATA; break; } case AWAITING_PACKET_DATA: { // Call the appropriate read handler to get the packet data from the TCP stream: RTPInterface* rtpInterface = lookupRTPInterface(fStreamChannelId); if (rtpInterface != NULL) { if (rtpInterface->fNextTCPReadSize == 0) { // We've already read all the data for this packet. fTCPReadingState = AWAITING_DOLLAR; break; } if (rtpInterface->fReadHandlerProc != NULL) { rtpInterface->fReadHandlerProc(rtpInterface->fOwner, mask); } } return; } } }最开始的注释中解释了多路复用头的格式。这一段引起了我的兴趣:
case AWAITING_DOLLAR: { if (c == $) { fTCPReadingState = AWAITING_STREAM_CHANNEL_ID; } else { // This character is part of a RTSP request or command, which is handled separately: if (fServerRequestAlternativeByteHandler != NULL) { (*fServerRequestAlternativeByteHandler)( fServerRequestAlternativeByteHandlerClientData, c); } } break; }啊!原来ServerRequestAlternativeByteHandler是用于处理RTSP数据的。也就是从这个socket收到RTSP数据时,调用ServerRequestAlternativeByteHandler。如果收到RTP/RTCP数据时,先查看其channel id,跟据id找到RTPInterface(RTCP也是用了RTPIterface进行通信),设置RTPInterface中与读缓冲有关的变量,然后当读到包数据的开始位置时,调用rtpInterface中保存的数据处理handler。还记得吧,rtpInterface中的这个数据处理handler在UDP时也被使用,在这个函数中要做的是读取一个包的数据,然后处理这个包。而SocketDescriptor把读取位置置于包数据开始的位置再交给数据处理handler,正好可以使用与UDP相同的数据处理handler!