live555学习一:数据流的处理
来源:互联网 发布:阿里云邮箱个人 编辑:程序博客网 时间:2024/05/21 07:12
最近一直在看live555,准备用live555搭建自己的流媒体转发服务器,我用的live555的proxyserver目录下的源文件:live555ProxyServer.cpp搭建的转发服务器,要搭建自己的可以借鉴这个实例。
live555的数据流:
在live555下用结构体
struct streamState{ ServerMediaSubsession *subsession; //Track:静态数据,会被其他rtp会话重用 void *streamToken; //类StreamState(真正的数据流),}*fStreamStates;表示一个流。
该流的初始化:
在服务器收到setUp请求时,会建立连接同时准备流(fStreamStates)——Track(subsession)+数据流(streamToken),数据流streamToken包含了Source和RTPSink。
1.fStreamState中subsession初始化
①先根据rtsp://协议传入的streamName生成一个ServerMediaSession类对象:fOurServerMediaSession,
②将该类对象fOurServerMediaSession存入hash表中:
ServerMediaSubsessionIterator iter(*fOurServerMediaSession);③从hash表中提取subsession并赋给fStreamStates中的subsession
iter.reset(); ServerMediaSubsession* subsession; for (unsigned i = 0; i < fNumStreamStates; ++i) { subsession = iter.next(); fStreamStates[i].subsession = subsession;//初始化Track …… }2.fStreamState中streamToken初始化
streamToken的初始化位于函数void OnDemandServerMediaSubsession::getStreamParameters(……)中,
①创建原始流FramedSource* mediaSource = createNewStreamSource(clientSessionId,streamBitrate);
②创建RTPSink,rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType,mediaSource);
③根据FramedSource和RTPSink创建数据流streamToken:
// Set up the state of the stream. The stream will get started later: streamToken = fLastStreamToken = new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink, streamBitrate, mediaSource, rtpGroupsock, rtcpGroupsock);
3.根据得到的Track和streamToken实例化对象fStreamStates
do { // First, make sure the specified stream name exists: fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName); if (fOurServerMediaSession == NULL) { // Check for the special case (noted above), before we up: if (urlPreSuffix[0] == '\0') { streamName = urlSuffix; } else { concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0' sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix); streamName = concatenatedStreamName; } trackId = NULL; // Check again: fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName); } if (fOurServerMediaSession == NULL) { handleCmd_notFound(cseq); break; } fOurServerMediaSession->incrementReferenceCount(); //为一个流中所有的track都分配一个stream state if (fStreamStates == NULL) { // This is the first "SETUP" for this session. Set up our // array of states for all of this session's subsessions (tracks): ServerMediaSubsessionIterator iter(*fOurServerMediaSession); for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) { } // begin by counting the number of subsessions (tracks) fStreamStates = new struct streamState[fNumStreamStates]; iter.reset(); ServerMediaSubsession* subsession; for (unsigned i = 0; i < fNumStreamStates; ++i) { subsession = iter.next(); fStreamStates[i].subsession = subsession; fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later } }
服务器处理SETUP请求:
void RTSPServer::RTSPClientSession::handleCmd_SETUP( char const* cseq, char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) { // Normally, "urlPreSuffix" should be the session (stream) name, // and "urlSuffix" should be the subsession (track) name. // However (being "liberal in what we accept"), we also handle // 'aggregate' SETUP requests (i.e., without a track name), // in the special case where we have only a single track. I.e., // in this case, we also handle: // "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or // "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) // is the session (stream) name. char const* streamName = urlPreSuffix; // in the normal case char const* trackId = urlSuffix; // in the normal case char* concatenatedStreamName = NULL; // in the normal case do { // First, make sure the specified stream name exists: fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName); if (fOurServerMediaSession == NULL) { // Check for the special case (noted above), before we up: if (urlPreSuffix[0] == '\0') { streamName = urlSuffix; } else { concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0' sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix); streamName = concatenatedStreamName; } trackId = NULL; // Check again: fOurServerMediaSession = fOurServer.lookupServerMediaSession(streamName); } if (fOurServerMediaSession == NULL) { handleCmd_notFound(cseq); break; } fOurServerMediaSession->incrementReferenceCount(); //为一个流中所有的track都分配一个stream state if (fStreamStates == NULL) { // This is the first "SETUP" for this session. Set up our // array of states for all of this session's subsessions (tracks): ServerMediaSubsessionIterator iter(*fOurServerMediaSession); for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) { } // begin by counting the number of subsessions (tracks) fStreamStates = new struct streamState[fNumStreamStates]; iter.reset(); ServerMediaSubsession* subsession; for (unsigned i = 0; i < fNumStreamStates; ++i) { subsession = iter.next(); fStreamStates[i].subsession = subsession; fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later } } //查找当前请求的track的信息 // Look up information for the specified subsession (track): ServerMediaSubsession* subsession = NULL; unsigned streamNum; if (trackId != NULL && trackId[0] != '\0') { // normal case for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) { subsession = fStreamStates[streamNum].subsession; if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break; //找到啦! } if (streamNum >= fNumStreamStates) { // The specified track id doesn't exist, so this request fails: handleCmd_notFound(cseq); break; } } else { // Weird case: there was no track id in the URL. // This works only if we have only one subsession: if (fNumStreamStates != 1) { handleCmd_bad(cseq); break; } streamNum = 0; subsession = fStreamStates[streamNum].subsession; } // ASSERT: subsession != NULL //分析RTSP请求字符串中的传输要求 // Look for a "Transport:" header in the request string, to extract client parameters: StreamingMode streamingMode; char* streamingModeString = NULL; // set when RAW_UDP streaming is specified char* clientsDestinationAddressStr; u_int8_t clientsDestinationTTL; portNumBits clientRTPPortNum, clientRTCPPortNum; unsigned char rtpChannelId, rtcpChannelId; parseTransportHeader(fullRequestStr, streamingMode, streamingModeString, clientsDestinationAddressStr, clientsDestinationTTL, clientRTPPortNum, clientRTCPPortNum, rtpChannelId, rtcpChannelId); if (streamingMode == RTP_TCP && rtpChannelId == 0xFF || streamingMode != RTP_TCP && fClientOutputSocket != fClientInputSocket) { // An anomolous situation, caused by a buggy client. Either: // 1/ TCP streaming was requested, but with no "interleaving=" fields. (QuickTime Player sometimes does this.), or // 2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming). // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values: streamingMode = RTP_TCP; rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount + 1; } fTCPStreamIdCount += 2; Port clientRTPPort(clientRTPPortNum); Port clientRTCPPort(clientRTCPPortNum); // Next, check whether a "Range:" header is present in the request. // This isn't legal, but some clients do this to combine "SETUP" and "PLAY": double rangeStart = 0.0, rangeEnd = 0.0; fStreamAfterSETUP = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd) || parsePlayNowHeader(fullRequestStr); // Then, get server parameters from the 'subsession': int tcpSocketNum = streamingMode == RTP_TCP ? fClientOutputSocket : -1; netAddressBits destinationAddress = 0; u_int8_t destinationTTL = 255; #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING if (clientsDestinationAddressStr != NULL) { // Use the client-provided "destination" address. // Note: This potentially allows the server to be used in denial-of-service // attacks, so don't enable this code unless you're sure that clients are // trusted. destinationAddress = our_inet_addr(clientsDestinationAddressStr); } // Also use the client-provided TTL. destinationTTL = clientsDestinationTTL; #endif delete[] clientsDestinationAddressStr; Port serverRTPPort(0); Port serverRTCPPort(0); // Make sure that we transmit on the same interface that's used by // the client (in case we're a multi-homed server): struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr; getsockname(fClientInputSocket, (struct sockaddr*) &sourceAddr, &namelen); netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr; netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr; // NOTE: The following might not work properly, so we ifdef it out for now: #ifdef HACK_FOR_MULTIHOMED_SERVERS ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr; #endif //获取rtp连接信息,在其中已建立起了server端的rtp和rtcp socket,返回 //fStreamStates[streamNum].streamToken表示数据流已经建立起来了 subsession->getStreamParameters(fOurSessionId, fClientAddr.sin_addr.s_addr, clientRTPPort, clientRTCPPort, tcpSocketNum, rtpChannelId, rtcpChannelId, destinationAddress, destinationTTL, fIsMulticast, serverRTPPort, serverRTCPPort, fStreamStates[streamNum].streamToken); SendingInterfaceAddr = origSendingInterfaceAddr; ReceivingInterfaceAddr = origReceivingInterfaceAddr; //形成RTSP回应字符串 struct in_addr destinationAddr; destinationAddr.s_addr = destinationAddress; char* destAddrStr = strDup(our_inet_ntoa(destinationAddr)); char* sourceAddrStr = strDup(our_inet_ntoa(sourceAddr.sin_addr)); if (fIsMulticast) { switch (streamingMode) { case RTP_UDP: snprintf( (char*) fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL, fOurSessionId); break; case RTP_TCP: // multicast streams can't be sent via TCP handleCmd_unsupportedTransport(cseq); break; case RAW_UDP: snprintf( (char*) fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), streamingModeString, destAddrStr, sourceAddrStr, ntohs(serverRTPPort.num()), destinationTTL, fOurSessionId); break; } } else { switch (streamingMode) { case RTP_UDP: { snprintf( (char*) fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), fOurSessionId); break; } case RTP_TCP: { snprintf( (char*) fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), destAddrStr, sourceAddrStr, rtpChannelId, rtcpChannelId, fOurSessionId); break; } case RAW_UDP: { snprintf( (char*) fResponseBuffer, sizeof fResponseBuffer, "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "%s" "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n" "Session: %08X\r\n\r\n", cseq, dateHeader(), streamingModeString, destAddrStr, sourceAddrStr, ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()), fOurSessionId); break; } } } delete[] destAddrStr; delete[] sourceAddrStr; delete[] streamingModeString; } while (0); delete[] concatenatedStreamName; //返回后,回应字符串会被立即发送 }
- live555学习一:数据流的处理
- live555学习二:数据流的发送(PLAY请求的处理)
- live555学习三:RTP数据流的获取
- Live555学习之(一)-------Live555的基本介绍
- 学习live555的点点滴滴(一)
- 学习Live555 (一)
- 学习Live555 (一)
- 流媒体Live555学习(一)
- 流媒体Live555学习(一)
- live555 server 数据流发送流程和时间戳的分析
- live555 h264 videostream 数据流和时间戳的分析
- live555 的学习使用
- Live555学习笔记(一)—— live555概述
- 网络数据流的java处理
- 网络数据流的java处理
- 网络数据流的java处理
- 网络数据流的java处理
- 网络数据流的java处理
- 团购留给国际的N个经验:过度扣头是一剂毒药
- 定义一个TABLEVIEW对象.
- Linux下redis安装部署
- 使用gdb调试内存重复释放导致的malloc_error_break错误崩溃
- javascript获取鼠标当前位置坐标 详细出处参考:http://www.jb51.net/article/27204.htm
- live555学习一:数据流的处理
- _00013 一致性哈希算法 Consistent Hashing 探讨以及相应的新问题出现解决
- 斯坦福机器学习公开课学习笔记(1)—机器学习的动机与应用
- PCB板层定义介绍
- Number Sequence——HDU 1711————KMP算法
- java中static{}语句块详解
- Linux的进程/线程间通信方式总结
- 程序调优方法之一:是否缺页
- 分析Poco中ServerApplication监听中断机制