Darwin-Streaming-Server/APICommonCode/SDPSourceInfo.cpp

437 lines
16 KiB
C++

/*
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
*/
/*
File: SDPSourceInfo.cpp
Contains: Implementation of object defined in .h file
*/
#include "SDPSourceInfo.h"
#include "StringParser.h"
#include "StringFormatter.h"
#include "OSMemory.h"
#include "SocketUtils.h"
#include "StrPtrLen.h"
#include "SDPUtils.h"
#include "OSArrayObjectDeleter.h"
static StrPtrLen sCLine("c=IN IP4 0.0.0.0");
static StrPtrLen sControlLine("a=control:*");
static StrPtrLen sVideoStr("video");
static StrPtrLen sAudioStr("audio");
static StrPtrLen sRtpMapStr("rtpmap");
static StrPtrLen sControlStr("control");
static StrPtrLen sBufferDelayStr("x-bufferdelay");
static StrPtrLen sBroadcastControlStr("x-broadcastcontrol");
static StrPtrLen sAutoDisconnect("RTSP");
static StrPtrLen sAutoDisconnectTime("TIME");
SDPSourceInfo::~SDPSourceInfo()
{
// Not reqd as the destructor of the
// base class will take care of delete the stream array
// and output array if allocated
/*
if (fStreamArray != NULL)
{
char* theCharArray = (char*)fStreamArray;
delete [] theCharArray;
}
*/
fSDPData.Delete();
}
char* SDPSourceInfo::GetLocalSDP(UInt32* newSDPLen)
{
Assert(fSDPData.Ptr != NULL);
Bool16 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.
Bool16 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
}
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;
Bool16 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))
{
//mark the trackID if that's what this line has
aParser.ConsumeUntil(NULL, '=');
aParser.ConsumeUntil(NULL, StringParser::sDigitMask);
fStreamArray[theStreamIndex - 1].fTrackID = aParser.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 ++;
}
}
UInt32 SDPSourceInfo::GetIPAddr(StringParser* inParser, char inStopChar)
{
StrPtrLen ipAddrStr;
// Get the IP addr str
inParser->ConsumeUntil(&ipAddrStr, inStopChar);
if (ipAddrStr.Len == 0)
return 0;
// NULL terminate it
char endChar = ipAddrStr.Ptr[ipAddrStr.Len];
ipAddrStr.Ptr[ipAddrStr.Len] = '\0';
//inet_addr returns numeric IP addr in network byte order, make
//sure to convert to host order.
UInt32 ipAddr = SocketUtils::ConvertStringToAddr(ipAddrStr.Ptr);
// Make sure to put the old char back!
ipAddrStr.Ptr[ipAddrStr.Len] = endChar;
return ipAddr;
}