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; } }
char* SDPSourceInfo::GetLocalSDP(UInt32* newSDPLen) { Assert(fSDPData.Ptr != NULL); bool appendCLine = true; UInt32 trackIndex = 0; char *localSDP = new char[fSDPData.Len * 2]; OSCharArrayDeleter charArrayPathDeleter(localSDP); StringFormatter localSDPFormatter(localSDP, fSDPData.Len * 2); StrPtrLen sdpLine; StringParser sdpParser(&fSDPData); char trackIndexBuffer[50]; // Only generate our own trackIDs if this file doesn't have 'em. // Our assumption here is that either the file has them, or it doesn't. // A file with some trackIDs, and some not, won't work. bool hasControlLine = false; while (sdpParser.GetDataRemaining() > 0) { //stop when we reach an empty line. sdpParser.GetThruEOL(&sdpLine); if (sdpLine.Len == 0) continue; switch (*sdpLine.Ptr) { case 'c': break;//ignore connection information case 'm': { //append new connection information right before the first 'm' if (appendCLine) { localSDPFormatter.Put(sCLine); localSDPFormatter.PutEOL(); if (!hasControlLine) { localSDPFormatter.Put(sControlLine); localSDPFormatter.PutEOL(); } appendCLine = false; } //the last "a=" for each m should be the control a= if ((trackIndex > 0) && (!hasControlLine)) { qtss_sprintf(trackIndexBuffer, "a=control:trackID=%" _S32BITARG_ "\r\n",trackIndex); localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer)); } //now write the 'm' line, but strip off the port information StringParser mParser(&sdpLine); StrPtrLen mPrefix; mParser.ConsumeUntil(&mPrefix, StringParser::sDigitMask); localSDPFormatter.Put(mPrefix); localSDPFormatter.Put("0", 1); (void)mParser.ConsumeInteger(NULL); localSDPFormatter.Put(mParser.GetCurrentPosition(), mParser.GetDataRemaining()); localSDPFormatter.PutEOL(); trackIndex++; break; } case 'a': { StringParser aParser(&sdpLine); aParser.ConsumeLength(NULL, 2);//go past 'a=' StrPtrLen aLineType; aParser.ConsumeWord(&aLineType); if (aLineType.Equal(sControlStr)) { aParser.ConsumeUntil(NULL, '='); aParser.ConsumeUntil(NULL, StringParser::sDigitMask); StrPtrLen aDigitType; (void)aParser.ConsumeInteger(&aDigitType); if (aDigitType.Len > 0) { localSDPFormatter.Put("a=control:trackID=", 18); localSDPFormatter.Put(aDigitType); localSDPFormatter.PutEOL(); hasControlLine = true; break; } } localSDPFormatter.Put(sdpLine); localSDPFormatter.PutEOL(); break; } default: { localSDPFormatter.Put(sdpLine); localSDPFormatter.PutEOL(); } } } if ((trackIndex > 0) && (!hasControlLine)) { qtss_sprintf(trackIndexBuffer, "a=control:trackID=%" _S32BITARG_ "\r\n",trackIndex); localSDPFormatter.Put(trackIndexBuffer, ::strlen(trackIndexBuffer)); } *newSDPLen = (UInt32)localSDPFormatter.GetCurrentOffset(); StrPtrLen theSDPStr(localSDP, *newSDPLen);//localSDP is not 0 terminated so initialize theSDPStr with the len. SDPContainer rawSDPContainer; (void) rawSDPContainer.SetSDPBuffer( &theSDPStr ); SDPLineSorter sortedSDP(&rawSDPContainer); return sortedSDP.GetSortedSDPCopy(); // return a new copy of the sorted SDP }
SDPLineSorter::SDPLineSorter(SDPContainer *rawSDPContainerPtr, Float32 adjustMediaBandwidthPercent, SDPContainer *insertMediaLinesArray) : fSessionLineCount(0), fSDPSessionHeaders(NULL, 0), fSDPMediaHeaders(NULL, 0) { Assert(rawSDPContainerPtr != NULL); if (NULL == rawSDPContainerPtr) return; StrPtrLen theSDPData(rawSDPContainerPtr->fSDPBuffer.Ptr, rawSDPContainerPtr->fSDPBuffer.Len); StrPtrLen *theMediaStart = rawSDPContainerPtr->GetLine(rawSDPContainerPtr->FindHeaderLineType('m', 0)); if (theMediaStart && theMediaStart->Ptr && theSDPData.Ptr) { UInt32 mediaLen = theSDPData.Len - (UInt32)(theMediaStart->Ptr - theSDPData.Ptr); char *mediaStartPtr = theMediaStart->Ptr; fMediaHeaders.Set(mediaStartPtr, mediaLen); StringParser sdpParser(&fMediaHeaders); SDPLine sdpLine; Bool16 foundLine = false; Bool16 newMediaSection = true; SDPLine* insertLine = NULL; while (sdpParser.GetDataRemaining() > 0) { foundLine = sdpParser.GetThruEOL(&sdpLine); if (!foundLine) { break; } if (sdpLine.GetHeaderType() == 'm') newMediaSection = true; if (insertMediaLinesArray && newMediaSection && (sdpLine.GetHeaderType() == 'a')) { newMediaSection = false; for (insertLine = insertMediaLinesArray->GetLine(0); insertLine; insertLine = insertMediaLinesArray->GetNextLine()) fSDPMediaHeaders.Put(*insertLine); } if (('b' == sdpLine.GetHeaderType()) && (1.0 != adjustMediaBandwidthPercent)) { StringParser bLineParser(&sdpLine); bLineParser.ConsumeUntilDigit(); UInt32 bandwidth = (UInt32)(.5 + (adjustMediaBandwidthPercent * (Float32)bLineParser.ConsumeInteger())); if (bandwidth < 1) bandwidth = 1; char bandwidthStr[10]; qtss_snprintf(bandwidthStr, sizeof(bandwidthStr) - 1, "%" _U32BITARG_ "", bandwidth); bandwidthStr[sizeof(bandwidthStr) - 1] = 0; fSDPMediaHeaders.Put(sMaxBandwidthTag); fSDPMediaHeaders.Put(bandwidthStr); } else fSDPMediaHeaders.Put(sdpLine); fSDPMediaHeaders.Put(SDPLineSorter::sEOL); } fMediaHeaders.Set(fSDPMediaHeaders.GetBufPtr(), fSDPMediaHeaders.GetBytesWritten()); } fSessionLineCount = rawSDPContainerPtr->FindHeaderLineType('m', 0); if (fSessionLineCount < 0) // didn't find it use the whole buffer { fSessionLineCount = rawSDPContainerPtr->GetNumLines(); } for (SInt16 sessionLineIndex = 0; sessionLineIndex < fSessionLineCount; sessionLineIndex++) fSessionSDPContainer.AddHeaderLine((StrPtrLen *)rawSDPContainerPtr->GetLine(sessionLineIndex)); //qtss_printf("\nSession raw Lines:\n"); fSessionSDPContainer.PrintAllLines(); SInt16 numHeaderTypes = sizeof(SDPLineSorter::sSessionOrderedLines) - 1; Bool16 addLine = true; for (SInt16 fieldTypeIndex = 0; fieldTypeIndex < numHeaderTypes; fieldTypeIndex++) { SInt32 lineIndex = fSessionSDPContainer.FindHeaderLineType(SDPLineSorter::sSessionOrderedLines[fieldTypeIndex], 0); StrPtrLen *theHeaderLinePtr = fSessionSDPContainer.GetLine(lineIndex); while (theHeaderLinePtr != NULL) { addLine = this->ValidateSessionHeader(theHeaderLinePtr); if (addLine) { fSDPSessionHeaders.Put(*theHeaderLinePtr); fSDPSessionHeaders.Put(SDPLineSorter::sEOL); } if (NULL != ::strchr(sessionSingleLines, theHeaderLinePtr->Ptr[0])) // allow 1 of this type: use first found break; // move on to next line type lineIndex = fSessionSDPContainer.FindHeaderLineType(SDPLineSorter::sSessionOrderedLines[fieldTypeIndex], lineIndex + 1); theHeaderLinePtr = fSessionSDPContainer.GetLine(lineIndex); } } fSessionHeaders.Set(fSDPSessionHeaders.GetBufPtr(), fSDPSessionHeaders.GetBytesWritten()); }
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 ++; } }
void SDPContainer::Parse() { char* validChars = "vosiuepcbtrzkam"; char nameValueSeparator = '='; Bool16 valid = true; StringParser sdpParser(&fSDPBuffer); StrPtrLen line; StrPtrLen fieldName; StrPtrLen space; Bool16 foundLine = false; while (sdpParser.GetDataRemaining() != 0) { foundLine = sdpParser.GetThruEOL(&line); // Read each line if (!foundLine) { break; } StringParser lineParser(&line); lineParser.ConsumeWhitespace();//skip over leading whitespace if (lineParser.GetDataRemaining() == 0) // must be an empty line continue; char firstChar = lineParser.PeekFast(); if (firstChar == '\0') continue; //skip over blank lines fFieldStr[(UInt8)firstChar] = firstChar; switch (firstChar) { case 'v': fReqLines |= kV; break; case 's': fReqLines |= kS; break; case 't': fReqLines |= kT; break; case 'o': fReqLines |= kO; break; } lineParser.ConsumeUntil(&fieldName, nameValueSeparator); if ((fieldName.Len != 1) || (::strchr(validChars, fieldName.Ptr[0]) == NULL)) { valid = false; // line doesn't begin with one of the valid characters followed by an "=" break; } if (!lineParser.Expect(nameValueSeparator)) { valid = false; // line doesn't have the "=" after the first char break; } lineParser.ConsumeUntil(&space, StringParser::sWhitespaceMask); if (space.Len != 0) { valid = false; // line has whitespace after the "=" break; } AddHeaderLine(&line); } if (fNumUsedLines == 0) // didn't add any lines { valid = false; } fValid = valid; }