void RelaySDPSourceInfo::Parse(StrPtrLen* inSDPData) { // These are the lines of the SDP file that we are interested in static StrPtrLen sRelayAddr("a=x-qt-relay-addr"); static StrPtrLen sRelayPort("a=x-qt-relay-port"); Assert(fOutputArray == NULL); Assert(fStreamArray == NULL); StrPtrLen sdpLine; StrPtrLen outputAddrs; StringParser trackCounter(inSDPData); UInt32 theDestIPAddr = 0; UInt16 theDestTtl = 0; // // FIRST WALK THROUGH SDP // The first walk is to count up the number of StreamInfo & OutputInfo // objects that we will need. while (true) { // grab a line trackCounter.ConsumeUntil(&sdpLine, StringParser::sEOLMask); if (sdpLine.NumEqualIgnoreCase(sRelayAddr.Ptr, sRelayAddr.Len)) { // there is a x-qt-relay-addr line, look for all IP addrs StringParser relayAddrParser(&sdpLine); relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask); // The first IP addr on this line is the destination IP addr of the // source broadcast. theDestIPAddr = SDPSourceInfo::GetIPAddr(&relayAddrParser, ' '); relayAddrParser.ConsumeWhitespace(); // Store this position so we can later go back to it outputAddrs.Ptr = relayAddrParser.GetCurrentPosition(); outputAddrs.Len = relayAddrParser.GetDataRemaining(); StrPtrLen theTtl; while (relayAddrParser.GetDataRemaining() > 0) { relayAddrParser.ConsumeUntil(&theTtl, ' '); relayAddrParser.ConsumeWhitespace(); fNumOutputs++; } fNumOutputs--;// Don't count the ttl as an output! StringParser ttlParser(&theTtl); theDestTtl = (UInt16) ttlParser.ConsumeInteger(NULL); } // Each x=qt-relay-port line corresponds to one source stream. else if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len)) fNumStreams++; //stop when we reach an empty line. if (!trackCounter.ExpectEOL()) break; } // No relay info in this file! if ((fNumStreams == 0) || (fNumOutputs == 0)) return; // x-qt-relay-port lines should always be in pairs (RTP & RTCP) if ((fNumStreams & 1) != 0) return; fNumStreams /= 2; // // CONSTRUCT fStreamInfo AND fOutputInfo ARRAYS fStreamArray = NEW StreamInfo[fNumStreams]; fOutputArray = NEW OutputInfo[fNumOutputs]; // // FILL IN ARRAYS // Filling in the output addresses is easy because the outputAddrs // StrPtrLen points right at the data we want StringParser theOutputAddrParser(&outputAddrs); for (UInt32 x = 0; x < fNumOutputs; x++) { fOutputArray[x].fDestAddr = SDPSourceInfo::GetIPAddr(&theOutputAddrParser, ' '); fOutputArray[x].fLocalAddr = INADDR_ANY; fOutputArray[x].fTimeToLive = theDestTtl; fOutputArray[x].fPortArray = NEW UInt16[fNumStreams];//Each output has one port per stream fOutputArray[x].fNumPorts = fNumStreams; ::memset(fOutputArray[x].fPortArray, 0, fNumStreams * sizeof(UInt16)); fOutputArray[x].fAlreadySetup = false; theOutputAddrParser.ConsumeWhitespace(); Assert(fOutputArray[x].fDestAddr > 0); } StringParser sdpParser(inSDPData); // Now go through and find all the port information on all the x-qt-relay-port lines for (UInt32 theStreamIndex = 0; theStreamIndex < fNumStreams; ) { sdpParser.ConsumeUntil(&sdpLine, StringParser::sEOLMask); // parse through all the x-qt-relay-port lines if (sdpLine.NumEqualIgnoreCase(sRelayPort.Ptr, sRelayPort.Len)) { // Begin parsing... find the first port on the line StringParser relayAddrParser(&sdpLine); relayAddrParser.ConsumeUntil(NULL, StringParser::sDigitMask); // The first port is the source port for this stream fStreamArray[theStreamIndex].fPort = (UInt16) relayAddrParser.ConsumeInteger(NULL); if (fStreamArray[theStreamIndex].fPort & 1) continue; //we only care about RTP ports // Fill in all the fields we can for this stream fStreamArray[theStreamIndex].fDestIPAddr = theDestIPAddr; fStreamArray[theStreamIndex].fTimeToLive = theDestTtl; fStreamArray[theStreamIndex].fTrackID = theStreamIndex + 1; // Now fill in all the output ports for this stream for (UInt32 x = 0; x < fNumOutputs; x++) { relayAddrParser.ConsumeWhitespace(); fOutputArray[x].fPortArray[theStreamIndex] = (UInt16) relayAddrParser.ConsumeInteger(NULL); } theStreamIndex++; } //stop when we reach an empty line. if (!sdpParser.ExpectEOL()) break; } }
void SDPSourceInfo::Parse(char* sdpData, UInt32 sdpLen) { // // There are some situations in which Parse can be called twice. // If that happens, just return and don't do anything the second time. if (fSDPData.Ptr != NULL) return; Assert(fStreamArray == NULL); char *sdpDataCopy = new char[sdpLen]; Assert(sdpDataCopy != NULL); memcpy(sdpDataCopy,sdpData, sdpLen); fSDPData.Set(sdpDataCopy, sdpLen); // If there is no trackID information in this SDP, we make the track IDs start // at 1 -> N UInt32 currentTrack = 1; bool hasGlobalStreamInfo = false; StreamInfo theGlobalStreamInfo; //needed if there is one c= header independent of //individual streams StrPtrLen sdpLine; StringParser trackCounter(&fSDPData); StringParser sdpParser(&fSDPData); UInt32 theStreamIndex = 0; //walk through the SDP, counting up the number of tracks // Repeat until there's no more data in the SDP while (trackCounter.GetDataRemaining() > 0) { //each 'm' line in the SDP file corresponds to another track. trackCounter.GetThruEOL(&sdpLine); if ((sdpLine.Len > 0) && (sdpLine.Ptr[0] == 'm')) fNumStreams++; } //We should scale the # of StreamInfos to the # of trax, but we can't because //of an annoying compiler bug... fStreamArray = new StreamInfo[fNumStreams]; ::memset(fStreamArray, 0, sizeof(StreamInfo) * fNumStreams); // set the default destination as our default IP address and set the default ttl theGlobalStreamInfo.fDestIPAddr = INADDR_ANY; theGlobalStreamInfo.fTimeToLive = kDefaultTTL; //Set bufferdelay to default of 3 theGlobalStreamInfo.fBufferDelay = (Float32) eDefaultBufferDelay; //Now actually get all the data on all the streams while (sdpParser.GetDataRemaining() > 0) { sdpParser.GetThruEOL(&sdpLine); if (sdpLine.Len == 0) continue;//skip over any blank lines switch (*sdpLine.Ptr) { case 't': { StringParser mParser(&sdpLine); mParser.ConsumeUntil(NULL, StringParser::sDigitMask); UInt32 ntpStart = mParser.ConsumeInteger(NULL); mParser.ConsumeUntil(NULL, StringParser::sDigitMask); UInt32 ntpEnd = mParser.ConsumeInteger(NULL); SetActiveNTPTimes(ntpStart,ntpEnd); } break; case 'm': { if (hasGlobalStreamInfo) { fStreamArray[theStreamIndex].fDestIPAddr = theGlobalStreamInfo.fDestIPAddr; fStreamArray[theStreamIndex].fTimeToLive = theGlobalStreamInfo.fTimeToLive; } fStreamArray[theStreamIndex].fTrackID = currentTrack; currentTrack++; StringParser mParser(&sdpLine); //find out what type of track this is mParser.ConsumeLength(NULL, 2);//go past 'm=' StrPtrLen theStreamType; mParser.ConsumeWord(&theStreamType); if (theStreamType.Equal(sVideoStr)) fStreamArray[theStreamIndex].fPayloadType = qtssVideoPayloadType; else if (theStreamType.Equal(sAudioStr)) fStreamArray[theStreamIndex].fPayloadType = qtssAudioPayloadType; //find the port for this stream mParser.ConsumeUntil(NULL, StringParser::sDigitMask); SInt32 tempPort = mParser.ConsumeInteger(NULL); if ((tempPort > 0) && (tempPort < 65536)) fStreamArray[theStreamIndex].fPort = (UInt16) tempPort; // find out whether this is TCP or UDP mParser.ConsumeWhitespace(); StrPtrLen transportID; mParser.ConsumeWord(&transportID); static const StrPtrLen kTCPTransportStr("RTP/AVP/TCP"); if (transportID.Equal(kTCPTransportStr)) fStreamArray[theStreamIndex].fIsTCP = true; theStreamIndex++; } break; case 'a': { StringParser aParser(&sdpLine); aParser.ConsumeLength(NULL, 2);//go past 'a=' StrPtrLen aLineType; aParser.ConsumeWord(&aLineType); if (aLineType.Equal(sBroadcastControlStr)) { // found a control line for the broadcast (delete at time or delete at end of broadcast/server startup) // qtss_printf("found =%s\n",sBroadcastControlStr); aParser.ConsumeUntil(NULL,StringParser::sWordMask); StrPtrLen sessionControlType; aParser.ConsumeWord(&sessionControlType); if (sessionControlType.Equal(sAutoDisconnect)) { fSessionControlType = kRTSPSessionControl; } else if (sessionControlType.Equal(sAutoDisconnectTime)) { fSessionControlType = kSDPTimeControl; } } //if we haven't even hit an 'm' line yet, just ignore all 'a' lines if (theStreamIndex == 0) break; if (aLineType.Equal(sRtpMapStr)) { //mark the codec type if this line has a codec name on it. If we already //have a codec type for this track, just ignore this line if ((fStreamArray[theStreamIndex - 1].fPayloadName.Len == 0) && (aParser.GetThru(NULL, ' '))) { StrPtrLen payloadNameFromParser; (void)aParser.GetThruEOL(&payloadNameFromParser); char* temp = payloadNameFromParser.GetAsCString(); // qtss_printf("payloadNameFromParser (%x) = %s\n", temp, temp); (fStreamArray[theStreamIndex - 1].fPayloadName).Set(temp, payloadNameFromParser.Len); // qtss_printf("%s\n", fStreamArray[theStreamIndex - 1].fPayloadName.Ptr); } } else if (aLineType.Equal(sControlStr)) { // Modify By EasyDarwin //if ((fStreamArray[theStreamIndex - 1].fTrackName.Len == 0) && // (aParser.GetThru(NULL, ' '))) { StrPtrLen trackNameFromParser; aParser.ConsumeUntil(NULL,':'); aParser.ConsumeLength(NULL,1); aParser.GetThruEOL(&trackNameFromParser); char* temp = trackNameFromParser.GetAsCString(); // qtss_printf("trackNameFromParser (%x) = %s\n", temp, temp); (fStreamArray[theStreamIndex - 1].fTrackName).Set(temp, trackNameFromParser.Len); // qtss_printf("%s\n", fStreamArray[theStreamIndex - 1].fTrackName.Ptr); StringParser tParser(&trackNameFromParser); tParser.ConsumeUntil(NULL, '='); tParser.ConsumeUntil(NULL, StringParser::sDigitMask); fStreamArray[theStreamIndex - 1].fTrackID = tParser.ConsumeInteger(NULL); } } else if (aLineType.Equal(sBufferDelayStr)) { // if a BufferDelay is found then set all of the streams to the same buffer delay (it's global) aParser.ConsumeUntil(NULL, StringParser::sDigitMask); theGlobalStreamInfo.fBufferDelay = aParser.ConsumeFloat(); } } break; case 'c': { //get the IP address off this header StringParser cParser(&sdpLine); cParser.ConsumeLength(NULL, 9);//strip off "c=in ip4 " UInt32 tempIPAddr = SDPSourceInfo::GetIPAddr(&cParser, '/'); //grab the ttl SInt32 tempTtl = kDefaultTTL; if (cParser.GetThru(NULL, '/')) { tempTtl = cParser.ConsumeInteger(NULL); Assert(tempTtl >= 0); Assert(tempTtl < 65536); } if (theStreamIndex > 0) { //if this c= line is part of a stream, it overrides the //global stream information fStreamArray[theStreamIndex - 1].fDestIPAddr = tempIPAddr; fStreamArray[theStreamIndex - 1].fTimeToLive = (UInt16) tempTtl; } else { theGlobalStreamInfo.fDestIPAddr = tempIPAddr; theGlobalStreamInfo.fTimeToLive = (UInt16) tempTtl; hasGlobalStreamInfo = true; } } } } // Add the default buffer delay Float32 bufferDelay = (Float32) eDefaultBufferDelay; if (theGlobalStreamInfo.fBufferDelay != (Float32) eDefaultBufferDelay) bufferDelay = theGlobalStreamInfo.fBufferDelay; UInt32 count = 0; while (count < fNumStreams) { fStreamArray[count].fBufferDelay = bufferDelay; count ++; } }