Add even more of the source

This should be about everything needed to build so far?
This commit is contained in:
Darren VanBuren 2017-03-07 17:14:16 -08:00
parent af3619d4fa
commit 849723c9cf
547 changed files with 149239 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,340 @@
/*
*
* @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: ClientSession.h
*/
#ifndef __CLIENT_SESSION__
#define __CLIENT_SESSION__
#include "Task.h"
#include "TimeoutTask.h"
#include "SVector.h"
#include "RTSPClient.h"
#include "ClientSocket.h"
#include "SDPSourceInfo.h"
#include "UDPSocket.h"
#include "PlayerSimulator.h"
class ClientSession : public Task
{
public:
enum
{
kRTSPUDPClientType = 0,
kRTSPTCPClientType = 1,
kRTSPHTTPClientType = 2,
kRTSPHTTPDropPostClientType = 3,
kRTSPReliableUDPClientType = 4
};
typedef UInt32 ClientType;
//The constructor will signal itself with Task::kStartEvent
ClientSession( UInt32 inAddr, UInt16 inPort, char* inURL,
ClientType inClientType,
UInt32 inDurationInSec, UInt32 inStartPlayTimeInSec,
UInt32 inRTCPIntervalInMS, UInt32 inOptionsIntervalInSec,
UInt32 inHTTPCookie, Bool16 inAppendJunkData, UInt32 inReadInterval,
UInt32 inSockRcvBufSize, Float32 inLateTolerance, char* inMetaInfoFields,
Float32 inSpeed, UInt32 verboseLevel, char* inPacketRangePlayHeader, UInt32 inOverbufferWindowSizeInK,
Bool16 sendOptions, Bool16 requestRandomData, SInt32 randomDataSize, Bool16 enable3GPP,
UInt32 GBW = 0, UInt32 MBW = 0, UInt32 MTD = 0, Bool16 enableForcePlayoutDelay = false, UInt32 playoutDelay = 0,
UInt32 bandwidth = 0, UInt32 bufferSpace = 0, UInt32 delayTime = 0, UInt32 startPlayDelay = 0,
char *controlID = NULL, char *name = NULL, char *password = NULL);
virtual ~ClientSession();
//
// Signals.
//
// Send a kKillEvent to delete this object.
// Send a kTeardownEvent to tell the object to send a TEARDOWN and abort
enum
{
kTeardownEvent = 0x00000100
};
virtual SInt64 Run();
//
// States. Find out what the object is currently doing
enum
{
kSendingOptions = 0,
kSendingDescribe = 1,
kSendingSetup = 2,
kSendingPlay = 3,
kPlaying = 4,
kSendingTeardown = 5,
kDone = 6
};
//
// Why did this session die?
enum
{
kDiedNormally = 0, // Session went fine
kTeardownFailed = 1, // Teardown failed, but session stats are all valid
kRequestFailed = 2, // Session couldn't be setup because the server returned an error
kBadSDP = 3, // Server sent back some bad SDP
kSessionTimedout = 4, // Server not responding
kConnectionFailed = 5, // Couldn't connect at all.
kDiedWhilePlaying = 6 // Connection was forceably closed while playing the movie
};
//
// Once this client session is completely done with the TEARDOWN and ready to be
// destructed, this will return true. Until it returns true, this object should not
// be deleted. When it does return true, this object should be deleted.
Bool16 IsDone() { return fState == kDone; }
//
// ACCESSORS
RTSPClient* GetClient() { return fClient; }
ClientSocket* GetSocket() { return fSocket; }
SDPSourceInfo* GetSDPInfo() { return &fSDPParser; }
UInt32 GetState() { return fState; }
// When this object is in the kDone state, this will tell you why the session died.
UInt32 GetReasonForDying() { return fDeathReason; }
UInt32 GetRequestStatus() { return fClient->GetStatus(); }
// Tells you the total time we were receiving packets. You can use this
// for computing bit rate
SInt64 GetTotalPlayTimeInMsec() { return fTotalPlayTime; }
QTSS_RTPPayloadType GetTrackType(UInt32 inTrackIndex)
{ return fSDPParser.GetStreamInfo(inTrackIndex)->fPayloadType; }
UInt32 GetNumPacketsReceived(UInt32 inTrackIndex) { return fStats[inTrackIndex].fNumPacketsReceived; }
UInt32 GetNumBytesReceived(UInt32 inTrackIndex) { return fStats[inTrackIndex].fNumBytesReceived; }
UInt32 GetNumPacketsOutOfOrder(UInt32 inTrackIndex) { return fStats[inTrackIndex].fNumOutOfOrderPackets; }
UInt32 GetNumOutOfBoundPackets(UInt32 inTrackIndex) { return fStats[inTrackIndex].fNumOutOfBoundPackets; }
UInt32 GetNumAcks(UInt32 inTrackIndex) { return fStats[inTrackIndex].fNumAcks; }
UInt32 Get3gNumPacketsLost(UInt32 inTrackIndex) { return fPlayerSimulator.GetNumPacketsLost(inTrackIndex); }
UInt32 Get3gNumDuplicates(UInt32 inTrackIndex) { return fPlayerSimulator.GetNumDuplicates(inTrackIndex); }
UInt32 Get3gNumLatePackets(UInt32 inTrackIndex) { return fPlayerSimulator.GetNumLatePackets(inTrackIndex); }
UInt32 Get3gNumBufferOverflowedPackets(UInt32 inTrackIndex) { return fPlayerSimulator.GetNumBufferOverflowedPackets(inTrackIndex); }
//include packets with bad SSRC
UInt32 GetNumMalformedPackets(UInt32 inTrackIndex)
{ return fStats[inTrackIndex].fNumMalformedPackets; }
//Will reset the counter everytime it is called
UInt32 GetSessionPacketsReceived() { UInt32 result = fNumPacketsReceived; fNumPacketsReceived = 0; return result; }
//
// Global stats
static UInt32 GetActiveConnections() { return sActiveConnections; }
static UInt32 GetPlayingConnections() { return sPlayingConnections; }
static UInt32 GetConnectionAttempts() { return sTotalConnectionAttempts; }
//The following two functions will reset the global counter every time it is called
static UInt32 GetConnectionBytesReceived() { UInt32 result = sBytesReceived; sBytesReceived = 0; return result; }
static UInt32 GetConnectionPacketsReceived() { UInt32 result = sPacketsReceived; sPacketsReceived = 0; return result; }
private:
enum
{
kRawRTSPControlType = 0,
kRTSPHTTPControlType = 1,
kRTSPHTTPDropPostControlType= 2
};
typedef UInt32 ControlType;
enum
{
kUDPTransportType = 0,
kReliableUDPTransportType = 1,
kTCPTransportType = 2
};
typedef UInt32 TransportType;
//Returns kUInt32_Max if there is no track with such trackID
UInt32 TrackID2TrackIndex(UInt32 trackID)
{
for (UInt32 trackIndex = 0; trackIndex < fSDPParser.GetNumStreams(); trackIndex++)
{
if (fSDPParser.GetStreamInfo(trackIndex)->fTrackID == trackID)
return trackIndex;
}
return kUInt32_Max;
}
ClientSocket* fSocket; // Connection object
RTSPClient* fClient; // Manages the client connection
SDPSourceInfo fSDPParser; // Parses the SDP in the DESCRIBE response
TimeoutTask fTimeoutTask; // Kills this connection in the event the server isn't responding
ControlType fControlType;
TransportType fTransportType;
UInt32 fDurationInSec;
UInt32 fStartPlayTimeInSec;
UInt32 fRTCPIntervalInMs;
UInt32 fOptionsIntervalInSec;
Bool16 fOptions;
Bool16 fOptionsRequestRandomData;
SInt32 fOptionsRandomDataSize;
SInt64 fTransactionStartTimeMilli;
UInt32 fState; // For managing the state machine
UInt32 fDeathReason;
UInt32 fNumSetups;
UDPSocket** fUDPSocketArray;
//these values starts as soon as the RTSP Play is completed; does not corresonds to actual media play time
SInt64 fPlayTime;
SInt64 fTotalPlayTime;
SInt64 fLastRTCPTime;
Bool16 fTeardownImmediately;
Bool16 fAppendJunk;
UInt32 fReadInterval;
UInt32 fSockRcvBufSize;
Float32 fSpeed;
char* fPacketRangePlayHeader;
//These values are for the wireless links only -- not end-to-end
//Units are in kbps, milliseconds, and bytes
UInt32 fGuarenteedBitRate;
UInt32 fMaxBitRate;
UInt32 fMaxTransferDelay;
Bool16 fEnableForcePlayoutDelay;
UInt32 fPlayoutDelay;
UInt32 fBandwidth; //bps
//the buffer space is per stream, not total space
UInt32 fBufferSpace;
UInt32 fDelayTime; //target buffering delay
UInt32 fStartPlayDelay; //how much buffer should we keep before we start playing? in milliseconds
Bool16 fEnable3GPP;
// Client stats
struct TrackStats
{
//Modified by ClientSession
UInt32 fNumPacketsReceived; //track only good packets(but include late and duplicates)
UInt32 fNumBytesReceived; //includes RTP header
UInt32 fNumOutOfOrderPackets; //excludes duplicates
UInt32 fNumOutOfBoundPackets;
UInt32 fNumMalformedPackets; //include packets with bad SSRC
UInt32 fNumAcks; //cumulative; counts ACK packets with masks as 1 ACK
UInt16 fDestRTCPPort;
UInt32 fServerSSRC; //0 for not available
UInt32 fClientSSRC;
//Used for the DLSR and LSR field of the RTCP
SInt64 fLastSenderReportNTPTime;
SInt64 fLastSenderReportLocalTime;
//These values are used to calculate the fraction lost and cumulative number of packets lost field in the RTCP RR packet.
//See RFC 3550 6.4.1 and A.3
//fHighestSeqNum is the highest valid sequence number received; note that this is 32 bits so that it never overflows.
//An initial value of kUInt32_Max is used as an invalid marker(such that no valid sequence number has been received yet).
UInt32 fHighestSeqNum;
UInt32 fBaseSeqNum;
UInt32 fExpectedPrior;
UInt32 fReceivedPrior;
SVector<UInt32> fPacketsToAck;
TrackStats() : fNumPacketsReceived(0), fNumBytesReceived(0), fNumOutOfOrderPackets(0), fNumOutOfBoundPackets(0),
fNumMalformedPackets(0), fNumAcks(0), fDestRTCPPort(0), fServerSSRC(0), fClientSSRC(0), fLastSenderReportNTPTime(0),
fLastSenderReportLocalTime(0), fHighestSeqNum(kUInt32_Max), fBaseSeqNum(0), fExpectedPrior(0), fReceivedPrior(0)
{ }
};
/* Client stats
struct TrackStats
{
enum
{
kSeqNumMapSize = 100,
kHalfSeqNumMap = 50
};
UInt16 fDestRTCPPort;
UInt32 fNumPacketsReceived;
UInt32 fNumBytesReceived;
UInt32 fNumLostPackets;
UInt32 fNumOutOfOrderPackets;
UInt32 fNumThrownAwayPackets;
UInt8 fSequenceNumberMap[kSeqNumMapSize];
UInt16 fWrapSeqNum;
UInt32 fSSRC;
Bool16 fIsSSRCValid;
UInt16 fHighestSeqNum;
UInt16 fLastAckedSeqNum;
Bool16 fHighestSeqNumValid;
UInt32 fNumAcks;
UInt32 fNumDuplicates;
};
*/
UInt32 fOverbufferWindowSizeInK;
UInt32 fCurRTCPTrack; //track index not track id
UInt32 fNumPacketsReceived; //track only good packets(but include late and duplicates; see RFC3550 6.4.1)
UInt32 fNumBytesReceived; //includes RTP header
UInt32 fVerboseLevel;
SVector<TrackStats> fStats; //the index of this vector is the same as fSDPParser.GetStreamInfo
PlayerSimulator fPlayerSimulator;
//TrackStats* fStats;
//
// Global stats
static UInt32 sActiveConnections;
static UInt32 sPlayingConnections;
static UInt32 sTotalConnectionAttempts;
static UInt32 sBytesReceived;
static UInt32 sPacketsReceived;
//
// Helper functions for Run()
void SetupUDPSockets();
void ProcessRTPPacket(char* inPacket, UInt32 inLength, UInt32 inTrackID);
void ProcessRTCPPacket(char* inPacket, UInt32 inLength, UInt32 inTrackID);
OS_Error ReadMediaData();
OS_Error SendRTCPPackets(UInt32 trackIndex);
void SendAckPackets(UInt32 inTrackIndex);
//Calculates the RTCP RR's fraction lost and cumulative number of packets lost field info.
void CalcRTCPRRPacketsLost(UInt32 trackIndex, UInt8 &outFracLost, SInt32 &outCumLostPackets);
//Returns kUInt32_Max if newSeqNum is out of bound, otherwise returns the corresponding 32 bit sequence number.
static UInt32 CalcSeqNum(UInt32 referenceSeqNum, UInt16 newSeqNum);
};
#endif //__CLIENT_SESSION__

View file

@ -0,0 +1,358 @@
/*
*
* @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: ClientSocket.cpp
*/
#ifndef __Win32__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/uio.h>
#endif
#include "ClientSocket.h"
#include "OSMemory.h"
#include "base64.h"
#include "MyAssert.h"
#define CLIENT_SOCKET_DEBUG 0
ClientSocket::ClientSocket()
: fHostAddr(0),
fHostPort(0),
fEventMask(0),
fSocketP(NULL),
fSendBuffer(fSendBuf, 0),
fSentLength(0)
{}
OS_Error ClientSocket::Open(TCPSocket* inSocket)
{
OS_Error theErr = OS_NoErr;
if (!inSocket->IsBound())
{
theErr = inSocket->Open();
if (theErr == OS_NoErr)
theErr = inSocket->Bind(0, 0);
if (theErr != OS_NoErr)
return theErr;
inSocket->NoDelay();
#if __FreeBSD__ || __MacOSX__
// no KeepAlive -- probably should be off for all platforms.
#else
inSocket->KeepAlive();
#endif
}
return theErr;
}
OS_Error ClientSocket::Connect(TCPSocket* inSocket)
{
OS_Error theErr = this->Open(inSocket);
Assert(theErr == OS_NoErr);
if (theErr != OS_NoErr)
return theErr;
if (!inSocket->IsConnected())
{
theErr = inSocket->Connect(fHostAddr, fHostPort);
if ((theErr == EINPROGRESS) || (theErr == EAGAIN))
{
fSocketP = inSocket;
fEventMask = EV_RE | EV_WR;
return theErr;
}
}
return theErr;
}
OS_Error ClientSocket::Send(char* inData, const UInt32 inLength)
{
iovec theVec[1];
theVec[0].iov_base = (char*)inData;
theVec[0].iov_len = inLength;
return this->SendV(theVec, 1);
}
OS_Error ClientSocket::SendSendBuffer(TCPSocket* inSocket)
{
OS_Error theErr = OS_NoErr;
UInt32 theLengthSent = 0;
if (fSendBuffer.Len == 0)
return OS_NoErr;
do
{
// theLengthSent should be reset to zero before passing its pointer to Send function
// otherwise the old value will be used and it will go into an infinite loop sometimes
theLengthSent = 0;
//
// Loop, trying to send the entire message.
theErr = inSocket->Send(fSendBuffer.Ptr + fSentLength, fSendBuffer.Len - fSentLength, &theLengthSent);
fSentLength += theLengthSent;
} while (theLengthSent > 0);
if (theErr == OS_NoErr)
fSendBuffer.Len = fSentLength = 0; // Message was sent
else
{
// Message wasn't entirely sent. Caller should wait for a read event on the POST socket
fSocketP = inSocket;
fEventMask = EV_WR;
}
return theErr;
}
TCPClientSocket::TCPClientSocket(UInt32 inSocketType)
: fSocket(NULL, inSocketType)
{
//
// It is necessary to open the socket right when we construct the
// object because the QTSSSplitterModule that uses this class uses
// the socket file descriptor in the QTSS_CreateStreamFromSocket call.
fSocketP = &fSocket;
this->Open(&fSocket);
}
void TCPClientSocket::SetOptions(int sndBufSize,int rcvBufSize)
{ //set options on the socket
//qtss_printf("TCPClientSocket::SetOptions sndBufSize=%d,rcvBuf=%d,keepAlive=%d,noDelay=%d\n",sndBufSize,rcvBufSize,(int)keepAlive,(int)noDelay);
int err = 0;
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDBUF, (char*)&sndBufSize, sizeof(int));
AssertV(err == 0, OSThread::GetErrno());
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVBUF, (char*)&rcvBufSize, sizeof(int));
AssertV(err == 0, OSThread::GetErrno());
#if __FreeBSD__ || __MacOSX__
struct timeval time;
//int len = sizeof(time);
time.tv_sec = 0;
time.tv_usec = 0;
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_RCVTIMEO, (char*)&time, sizeof(time));
AssertV(err == 0, OSThread::GetErrno());
err = ::setsockopt(fSocket.GetSocketFD(), SOL_SOCKET, SO_SNDTIMEO, (char*)&time, sizeof(time));
AssertV(err == 0, OSThread::GetErrno());
#endif
}
OS_Error TCPClientSocket::SendV(iovec* inVec, UInt32 inNumVecs)
{
if (fSendBuffer.Len == 0)
{
for (UInt32 count = 0; count < inNumVecs; count++)
{
::memcpy(fSendBuffer.Ptr + fSendBuffer.Len, inVec[count].iov_base, inVec[count].iov_len);
fSendBuffer.Len += inVec[count].iov_len;
Assert(fSendBuffer.Len < ClientSocket::kSendBufferLen);
}
}
OS_Error theErr = this->Connect(&fSocket);
if (theErr != OS_NoErr)
return theErr;
return this->SendSendBuffer(&fSocket);
}
OS_Error TCPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen)
{
this->Connect(&fSocket);
OS_Error theErr = fSocket.Read(inBuffer, inLength, outRcvLen);
if (theErr != OS_NoErr)
fEventMask = EV_RE;
return theErr;
}
HTTPClientSocket::HTTPClientSocket(const StrPtrLen& inURL, UInt32 inCookie, UInt32 inSocketType)
: fCookie(inCookie),
fSocketType(inSocketType),
fGetReceived(0),
fGetSocket(NULL, inSocketType),
fPostSocket(NULL)
{
fURL.Ptr = NEW char[inURL.Len + 1];
fURL.Len = inURL.Len;
::memcpy(fURL.Ptr, inURL.Ptr, inURL.Len);
fURL.Ptr[fURL.Len] = '\0';
}
HTTPClientSocket::~HTTPClientSocket()
{
delete [] fURL.Ptr;
delete fPostSocket;
}
OS_Error HTTPClientSocket::Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen)
{
//
// Bring up the GET connection if we need to
if (!fGetSocket.IsConnected())
{
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Sending GET\n");
#endif
qtss_sprintf(fSendBuffer.Ptr, "GET %s HTTP/1.0\r\nX-SessionCookie: %"_U32BITARG_"\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr);
Assert(fSentLength == 0);
}
OS_Error theErr = this->Connect(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
if (fSendBuffer.Len > 0)
{
theErr = this->SendSendBuffer(&fGetSocket);
if (theErr != OS_NoErr)
return theErr;
fSentLength = 1; // So we know to execute the receive code below.
}
// We are done sending the GET. If we need to receive the GET response, do that here
if (fSentLength > 0)
{
*outRcvLen = 0;
do
{
// Loop, trying to receive the entire response.
theErr = fGetSocket.Read(&fSendBuffer.Ptr[fGetReceived], kSendBufferLen - fGetReceived, outRcvLen);
fGetReceived += *outRcvLen;
// Check to see if we've gotten a \r\n\r\n. If we have, then we've received
// the entire GET
fSendBuffer.Ptr[fGetReceived] = '\0';
char* theGetEnd = ::strstr(fSendBuffer.Ptr, "\r\n\r\n");
if (theGetEnd != NULL)
{
// We got the entire GET response, so we are ready to move onto
// real RTSP response data. First skip past the \r\n\r\n
theGetEnd += 4;
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Received GET response\n");
#endif
// Whatever remains is part of an RTSP request, so move that to
// the beginning of the buffer and blow away the GET
*outRcvLen = fGetReceived - (theGetEnd - fSendBuffer.Ptr);
::memcpy(inBuffer, theGetEnd, *outRcvLen);
fGetReceived = fSentLength = 0;
return OS_NoErr;
}
Assert(fGetReceived < inLength);
} while (*outRcvLen > 0);
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Read: Waiting for GET response\n");
#endif
// Message wasn't entirely received. Caller should wait for a read event on the GET socket
Assert(theErr != OS_NoErr);
fSocketP = &fGetSocket;
fEventMask = EV_RE;
return theErr;
}
theErr = fGetSocket.Read(&((char*)inBuffer)[fGetReceived], inLength - fGetReceived, outRcvLen);
if (theErr != OS_NoErr)
{
#if CLIENT_SOCKET_DEBUG
//qtss_printf("HTTPClientSocket::Read: Waiting for data\n");
#endif
fSocketP = &fGetSocket;
fEventMask = EV_RE;
}
#if CLIENT_SOCKET_DEBUG
//else
//qtss_printf("HTTPClientSocket::Read: Got some data\n");
#endif
return theErr;
}
OS_Error HTTPClientSocket::SendV(iovec* inVec, UInt32 inNumVecs)
{
//
// Bring up the POST connection if we need to
if (fPostSocket == NULL)
fPostSocket = NEW TCPSocket(NULL, fSocketType);
if (!fPostSocket->IsConnected())
{
#if CLIENT_SOCKET_DEBUG
qtss_printf("HTTPClientSocket::Send: Sending POST\n");
#endif
qtss_sprintf(fSendBuffer.Ptr, "POST %s HTTP/1.0\r\nX-SessionCookie: %"_U32BITARG_"\r\nAccept: application/x-rtsp-rtp-interleaved\r\nUser-Agent: QTSS/2.0\r\n\r\n", fURL.Ptr, fCookie);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr);
this->EncodeVec(inVec, inNumVecs);
}
OS_Error theErr = this->Connect(fPostSocket);
if (theErr != OS_NoErr)
return theErr;
//
// If we have nothing to send currently, this should be a new message, in which case
// we can encode it and send it
if (fSendBuffer.Len == 0)
this->EncodeVec(inVec, inNumVecs);
#if CLIENT_SOCKET_DEBUG
//qtss_printf("HTTPClientSocket::Send: Sending data\n");
#endif
return this->SendSendBuffer(fPostSocket);
}
void HTTPClientSocket::EncodeVec(iovec* inVec, UInt32 inNumVecs)
{
for (UInt32 count = 0; count < inNumVecs; count++)
{
fSendBuffer.Len += ::Base64encode(fSendBuffer.Ptr + fSendBuffer.Len, (char*)inVec[count].iov_base, inVec[count].iov_len);
Assert(fSendBuffer.Len < ClientSocket::kSendBufferLen);
fSendBuffer.Len = ::strlen(fSendBuffer.Ptr); //Don't trust what the above function returns for a length
}
}

View file

@ -0,0 +1,165 @@
/*
*
* @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: ClientSocket.h
*/
#ifndef __CLIENT_SOCKET__
#define __CLIENT_SOCKET__
#include "OSHeaders.h"
#include "TCPSocket.h"
class ClientSocket
{
public:
ClientSocket();
virtual ~ClientSocket() {}
void Set(UInt32 hostAddr, UInt16 hostPort)
{ fHostAddr = hostAddr; fHostPort = hostPort; }
//
// Sends data to the server. If this returns EAGAIN or EINPROGRESS, call again
// until it returns OS_NoErr or another error. On subsequent calls, you need not
// provide a buffer.
//
// When this call returns EAGAIN or EINPROGRESS, caller should use GetEventMask
// and GetSocket to wait for a socket event.
OS_Error Send(char* inData, const UInt32 inLength);
//
// Sends an ioVec to the server. Same conditions apply as above function
virtual OS_Error SendV(iovec* inVec, UInt32 inNumVecs) = 0;
//
// Reads data from the server. If this returns EAGAIN or EINPROGRESS, call
// again until it returns OS_NoErr or another error. This call may return OS_NoErr
// and 0 for rcvLen, in which case you should call it again.
//
// When this call returns EAGAIN or EINPROGRESS, caller should use GetEventMask
// and GetSocket to wait for a socket event.
virtual OS_Error Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen) = 0;
//
// ACCESSORS
UInt32 GetHostAddr() { return fHostAddr; }
virtual UInt32 GetLocalAddr() = 0;
// If one of the above methods returns EWOULDBLOCK or EINPROGRESS, you
// can check this to see what events you should wait for on the socket
UInt32 GetEventMask() { return fEventMask; }
Socket* GetSocket() { return fSocketP; }
virtual void SetRcvSockBufSize(UInt32 inSize) = 0;
protected:
// Generic connect function
OS_Error Connect(TCPSocket* inSocket);
// Generic open function
OS_Error Open(TCPSocket* inSocket);
OS_Error SendSendBuffer(TCPSocket* inSocket);
UInt32 fHostAddr;
UInt16 fHostPort;
UInt32 fEventMask;
Socket* fSocketP;
enum
{
kSendBufferLen = 2048
};
// Buffer for sends.
char fSendBuf[kSendBufferLen + 1];
StrPtrLen fSendBuffer;
UInt32 fSentLength;
};
class TCPClientSocket : public ClientSocket
{
public:
TCPClientSocket(UInt32 inSocketType);
virtual ~TCPClientSocket() {}
//
// Implements the ClientSocket Send and Receive interface for a TCP connection
virtual OS_Error SendV(iovec* inVec, UInt32 inNumVecs);
virtual OS_Error Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen);
virtual UInt32 GetLocalAddr() { return fSocket.GetLocalAddr(); }
virtual void SetRcvSockBufSize(UInt32 inSize) { fSocket.SetSocketRcvBufSize(inSize); }
virtual void SetOptions(int sndBufSize = 8192,int rcvBufSize=1024);
virtual UInt16 GetLocalPort() { return fSocket.GetLocalPort(); }
private:
TCPSocket fSocket;
};
class HTTPClientSocket : public ClientSocket
{
public:
HTTPClientSocket(const StrPtrLen& inURL, UInt32 inCookie, UInt32 inSocketType);
virtual ~HTTPClientSocket();
//
// Closes the POST half of the RTSP / HTTP connection
void ClosePost() { delete fPostSocket; fPostSocket = NULL; }
//
// Implements the ClientSocket Send and Receive interface for an RTSP / HTTP connection
virtual OS_Error SendV(iovec* inVec, UInt32 inNumVecs);
// Both SendV and Read use the fSendBuffer; so you cannot have both operations be running at the same time.
virtual OS_Error Read(void* inBuffer, const UInt32 inLength, UInt32* outRcvLen);
virtual UInt32 GetLocalAddr() { return fGetSocket.GetLocalAddr(); }
virtual void SetRcvSockBufSize(UInt32 inSize) { fGetSocket.SetSocketRcvBufSize(inSize); }
private:
void EncodeVec(iovec* inVec, UInt32 inNumVecs);
StrPtrLen fURL;
UInt32 fCookie;
UInt32 fSocketType;
UInt32 fGetReceived;
TCPSocket fGetSocket;
TCPSocket* fPostSocket;
};
#endif //__CLIENT_SOCKET__

View file

@ -0,0 +1,366 @@
/*
*
* @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: PlayerSimulator.h
Simulates a client; track duplicate/late/missing packets
*/
#ifndef _PLAYERSIMULATOR_H_
#define _PLAYERSIMULATOR_H_
#include "SafeStdLib.h"
#include "OSHeaders.h"
#include "OS.h"
#include "SVector.h"
/* Macros for min/max. */
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif
class PlayerSimulator
{
public:
PlayerSimulator(UInt32 verboseLevel = 0)
: fTargetBufferingDelay(0), fStartPlayDelay(0), fLocalStartPlayTime(0), fCurrentMediaTime(0), fVerboseLevel(verboseLevel), fIsPlaying(false)
{}
//call Setup and then SetupTrack per stream to initialize the class. Use a targetBufferingDelay of 0 to start playing immediately
//The PlayerSimulator can be reused by calling Setup and SetupTrack again
void Setup(UInt32 numTracks, UInt32 targetBufferingDelay = 0, UInt32 startPlayDelay = 0)
{
fTargetBufferingDelay = targetBufferingDelay;
fStartPlayDelay = startPlayDelay;
fLocalStartPlayTime = 0;
fIsPlaying = false;
fTrackInfo.clear();
fTrackInfo.resize(numTracks);
}
//Use a bufferSize of 0 to have an unlimited buffer size;
void SetupTrack(UInt32 trackIndex, UInt32 samplingRate, UInt32 bufferSize = 0)
{
fTrackInfo[trackIndex].fBufferSize = bufferSize == 0 ? kUInt32_Max : bufferSize;
fTrackInfo[trackIndex].fSamplingRate = samplingRate;
}
//Please note that PlayerSimulator use track index, not track ID; payloadSize should exclude the RTP header.
//Returns true if the packet is a duplicate; the sequence number of the RTP packet MUST be in range.
Bool16 ProcessRTPPacket(UInt32 trackIndex, UInt32 packetLen, UInt32 payloadSize, UInt32 seqNum, UInt32 timeStamp)
{
Assert(trackIndex <= fTrackInfo.size());
TrackInfo &trackInfo = fTrackInfo[trackIndex];
SVector<PacketInfo> &playBuffer = trackInfo.fPlayBuffer;
Bool16 addPacketToBuffer = false;
Bool16 packetIsDuplicate = false;
if (fIsPlaying)
{
AdvanceTime();
if(seqNum < trackInfo.fNextSeqNum)
{
//definitely a late packet; try to see if the packet is one of the lost packets
UInt32 index = trackInfo.fLostPackets.find(seqNum);
if (index != trackInfo.fLostPackets.size())
{
//a late, non-duplicate packet
trackInfo.fNumLatePackets++;
trackInfo.fLostPackets.erase(index);
}
else //a late-duplicate
{
trackInfo.fNumDuplicates++;
packetIsDuplicate = true;
}
}
else
{
//look in the play buffer
if (playBuffer.empty() || seqNum < playBuffer.front().fSeqNum)
{
//the packet has an earlier sequence number than any other packets in the buffer; it may still be late
if (RTPTime2MediaTime(trackIndex, timeStamp) <= fCurrentMediaTime)
trackInfo.fNumLatePackets++;
if (GetFreeBufferSpace(trackIndex) >= packetLen)
playBuffer.insert(0, PacketInfo(seqNum, timeStamp, packetLen));
else
trackInfo.fNumBufferOverflowedPackets++;
}
else
addPacketToBuffer = true;
}
}
else
{
if (playBuffer.empty())
{
//This is the first packet in the stream
if (GetFreeBufferSpace(trackIndex) >= packetLen)
playBuffer.push_back(PacketInfo(seqNum, timeStamp, packetLen));
else
trackInfo.fNumBufferOverflowedPackets++;
}
else
addPacketToBuffer = true;
}
if (addPacketToBuffer)
{
//check the buffer to see if the packet is a duplicate
UInt32 index = 0;
for(index = 0; index < playBuffer.size(); ++index)
{
if (seqNum == playBuffer[index].fSeqNum)
{
//a non-late, duplicate packet
trackInfo.fNumDuplicates++;
packetIsDuplicate = true;
break;
}
else if (seqNum < playBuffer[index].fSeqNum) //found where to insert
break;
}
if(!packetIsDuplicate)
{
//a non-late non-duplicate -- may be out of order
if (GetFreeBufferSpace(trackIndex) >= packetLen)
playBuffer.insert(index, PacketInfo(seqNum, timeStamp, packetLen));
else
trackInfo.fNumBufferOverflowedPackets++;
}
}
if(!fIsPlaying)
{
//is it time to start playing?
Bool16 haveEnoughBuffer = true;
for(UInt32 index = 0; index < fTrackInfo.size(); ++index)
{
if (fTrackInfo[index].fPlayBuffer.empty() || GetBufferingDelay(index) <= fStartPlayDelay)
{
haveEnoughBuffer = false;
break;
}
}
if(haveEnoughBuffer) //lets start playing!
{
fIsPlaying = true;
fLocalStartPlayTime = OS::Milliseconds();
for(UInt32 index = 0; index < fTrackInfo.size(); ++index)
{
//for each track, set the next sequence number and the earliest RTP timestamp
TrackInfo &curTrack = fTrackInfo[index];
curTrack.fRTPTimeStampBase = curTrack.fPlayBuffer.front().fTimeStamp;
curTrack.fNextSeqNum = curTrack.fPlayBuffer.front().fSeqNum;
if(fVerboseLevel >= 1)
qtss_printf("Track %"_U32BITARG_" is now playing; initial seq=%"_U32BITARG_", initial time stamp=%"_U32BITARG_"\n",
index, curTrack.fNextSeqNum, curTrack.fRTPTimeStampBase);
}
AdvanceTime();
}
}
if(fVerboseLevel >= 2)
{
if (fIsPlaying)
qtss_printf("Processed packet: track=%"_U32BITARG_", len=%"_U32BITARG_", seq=%"_U32BITARG_", time=%"_U32BITARG_"(%"_U32BITARG_"); "
"bufferingDelay=%"_U32BITARG_", FBS=%"_U32BITARG_", media time=%"_U32BITARG_"\n",
trackIndex, packetLen, seqNum, timeStamp, RTPTime2MediaTime(trackIndex, timeStamp),
GetBufferingDelay(trackIndex), GetFreeBufferSpace(trackIndex), fCurrentMediaTime);
else
qtss_printf("Processed packet: track=%"_U32BITARG_", len=%"_U32BITARG_", seq=%"_U32BITARG_", time=%"_U32BITARG_"(%"_U32BITARG_"); "
"bufferingDelay=%"_U32BITARG_", FBS=%"_U32BITARG_"\n",
trackIndex, packetLen, seqNum, timeStamp, RTPTime2MediaTime(trackIndex, timeStamp),
GetBufferingDelay(trackIndex), GetFreeBufferSpace(trackIndex));
}
return packetIsDuplicate;
}
//returns kUInt32_Max if the buffer is empty; otherwise returns the first sequence number in the buffer
UInt32 GetNextSeqNumToDecode(UInt32 trackIndex)
{
SVector<PacketInfo> &playBuffer = fTrackInfo[trackIndex].fPlayBuffer;
return playBuffer.empty() ? kUInt32_Max : playBuffer.front().fSeqNum;
}
//in milliseconds; returns kUInt32_Max if the stream is not playing or if the buffer is empty
UInt32 GetPlayoutDelay(UInt32 trackIndex)
{
SVector<PacketInfo> &playBuffer = fTrackInfo[trackIndex].fPlayBuffer;
if(!fIsPlaying || playBuffer.empty())
return kUInt32_Max;
else
{
UInt32 nextPacketTime = playBuffer.front().fTimeStamp;
return RTPTime2MediaTime(trackIndex, nextPacketTime) - fCurrentMediaTime;
}
}
//in milliseconds; returns kUInt32_Max if the buffer is empty
UInt32 GetBufferingDelay(UInt32 trackIndex)
{
SVector<PacketInfo> &playBuffer = fTrackInfo[trackIndex].fPlayBuffer;
if(playBuffer.empty())
return kUInt32_Max;
UInt64 delta = playBuffer.back().fTimeStamp - playBuffer.front().fTimeStamp;
UInt32 bufferDelay = static_cast<UInt32>((delta * 1000) / fTrackInfo[trackIndex].fSamplingRate);
if (fIsPlaying)
bufferDelay += GetPlayoutDelay(trackIndex);
return bufferDelay;
}
//in bytes
UInt32 GetFreeBufferSpace(UInt32 trackIndex)
{
SVector<PacketInfo> &playBuffer = fTrackInfo[trackIndex].fPlayBuffer;
UInt32 bytesInBuffer = 0;
for(UInt32 i = 0; i < playBuffer.size(); ++i)
bytesInBuffer += playBuffer[i].fPayloadSize;
Assert(fTrackInfo[trackIndex].fBufferSize >= bytesInBuffer);
return fTrackInfo[trackIndex].fBufferSize - bytesInBuffer;
}
//The lost packets returned by this function is NOT the same as the lost packets defined by the RTCP RR.
UInt32 GetNumPacketsLost(UInt32 trackIndex) { return fTrackInfo[trackIndex].fLostPackets.size(); }
UInt32 GetNumLatePackets(UInt32 trackIndex) { return fTrackInfo[trackIndex].fNumLatePackets; }
UInt32 GetNumBufferOverflowedPackets(UInt32 trackIndex) { return fTrackInfo[trackIndex].fNumBufferOverflowedPackets; }
UInt32 GetNumDuplicates(UInt32 trackIndex) { return fTrackInfo[trackIndex].fNumDuplicates; }
private:
struct PacketInfo
{
UInt32 fSeqNum; //kUInt32_Max is invalid sequence number
UInt32 fTimeStamp;
UInt32 fPayloadSize;
PacketInfo() : fSeqNum(0), fTimeStamp(0), fPayloadSize(0) {}
PacketInfo(UInt32 seqNum, UInt32 timeStamp, UInt32 payloadSize)
: fSeqNum(seqNum), fTimeStamp(timeStamp), fPayloadSize(payloadSize)
{}
};
struct TrackInfo
{
UInt32 fNumDuplicates;
UInt32 fNumBufferOverflowedPackets; //number of packets lost due to buffer overflow
UInt32 fNumLatePackets; //packet that did not arrive on time, but did arrive
UInt32 fBufferSize; //in bytes
UInt32 fSamplingRate; //in samples per second
//These two values have meaning only while the stream is playing.
//All packets with sequence number less than fNextSeqNum have already expired, and all other packets PROBABLY goes into the buffer
UInt32 fRTPTimeStampBase;
UInt32 fNextSeqNum;
SVector<UInt32> fLostPackets; //All the packets with seq number less than fNextSeqNum that has not yet arrived.
SVector<PacketInfo> fPlayBuffer; //Can contain only packets with seq number >= than fNextSeqNum
TrackInfo() : fNumDuplicates(0), fNumBufferOverflowedPackets(0), fNumLatePackets(0), fBufferSize(0), fSamplingRate(0),
fRTPTimeStampBase(0), fNextSeqNum(0)
{}
};
//Advances the curent media play time according to the wallclock time; remove packets in the buffer whose timestamp has expired,
//increases fNextSeqNum, and find out which packet is missing and put them on the lost packts list.
//Also updates the current media play time -- all packets with an earlier timestamp has expired, and all packets with a later timestamp
//must be in the buffer or is not received yet.
void AdvanceTime()
{
fCurrentMediaTime = LocalTime2MediaTime(OS::Milliseconds());
for(UInt32 trackIndex = 0; trackIndex < fTrackInfo.size(); ++trackIndex)
{
TrackInfo &trackInfo = fTrackInfo[trackIndex];
SVector<PacketInfo> &playBuffer = trackInfo.fPlayBuffer;
UInt32 prevTimeStamp = 0;
//go through the play buffer until a packet with a later timestamp than the current time is found
UInt32 i = 0;
for(; i < playBuffer.size(); ++i)
{
if(prevTimeStamp > playBuffer[i].fTimeStamp)
{ if(fVerboseLevel >= 2) //this can happen from out of order packets from udp network routing or retransmits from rudp. It should never happen over tcp or http.
qtss_printf("WARNING: RTP timestamp is not monotonic! seq=%"_U32BITARG_" timestamp=%"_U32BITARG_"\n", playBuffer[i].fSeqNum, playBuffer[i].fTimeStamp);
}
else
prevTimeStamp = playBuffer[i].fTimeStamp;
if (fCurrentMediaTime >= RTPTime2MediaTime(trackIndex, playBuffer[i].fTimeStamp))
{
//the packet has expired; add packets that has not yet arrived to the lost packet list
while(trackInfo.fNextSeqNum != playBuffer[i].fSeqNum)
{
trackInfo.fLostPackets.push_back(trackInfo.fNextSeqNum);
trackInfo.fNextSeqNum++;
}
trackInfo.fNextSeqNum++;
}
else
break;
}
//remove packets that have expired
playBuffer.erase(0, i);
}
}
//can be called only when the media is playing; media time is measured in milliseconds
UInt32 LocalTime2MediaTime(SInt64 localTime)
{
SInt64 delta = localTime - fLocalStartPlayTime;
Assert(fIsPlaying && (SInt64) 0 <= (SInt64) delta && delta <= (SInt64)kUInt32_Max);
return delta;
}
UInt32 RTPTime2MediaTime(UInt32 trackIndex, UInt32 RTPTimestamp)
{
TrackInfo &trackInfo = fTrackInfo[trackIndex];
UInt32 RTPTimestampBase = RTPTimestamp;
if(fIsPlaying)
RTPTimestampBase = trackInfo.fRTPTimeStampBase;
else if (!trackInfo.fPlayBuffer.empty())
RTPTimestampBase = trackInfo.fPlayBuffer.front().fTimeStamp;
UInt64 delta = RTPTimestamp - RTPTimestampBase;
return static_cast<UInt32>((delta*1000) / trackInfo.fSamplingRate);
}
SVector<TrackInfo> fTrackInfo;
UInt32 fTargetBufferingDelay; //in milliseconds
UInt32 fStartPlayDelay; //in milliseconds
SInt64 fLocalStartPlayTime; //in UNIX time(milliseconds);
UInt32 fCurrentMediaTime; //current media time(in millisecond); valid only while playing
UInt32 fVerboseLevel;
Bool16 fIsPlaying;
};
#endif //_PLAYERSIMULATOR_H_

113
RTSPClientLib/RTPPacket.h Normal file
View file

@ -0,0 +1,113 @@
/*
*
* @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: RTPPacket.h
Some useful things for parsing RTPPackets.
*/
#ifndef _RTPPACKET_H_
#define _RTPPACKET_H_
#include "arpa/inet.h"
#include "OSHeaders.h"
#include "StrPtrLen.h"
class RTPPacket
{
public:
enum {
RTP_VERSION = 2,
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
RTCP_BYE = 203,
RTCP_APP = 204
};
/*
* RTP data header
*/
struct RTPHeader {
#if 0
//rtp header
unsigned int version:2; /* protocol version */
unsigned int p:1; /* padding flag */
unsigned int x:1; /* header extension flag */
unsigned int cc:4; /* CSRC count */
unsigned int m:1; /* marker bit */
unsigned int pt:7; /* payload type */
#endif
UInt16 rtpheader;
UInt16 seq; /* sequence number */
UInt32 ts; /* timestamp */
UInt32 ssrc; /* synchronization source */
//UInt32 csrc[1]; /* optional CSRC list */
};
RTPPacket(StrPtrLen inPacket)
: fPacket(reinterpret_cast<RTPHeader *>(inPacket.Ptr)), fLen(inPacket.Len)
{}
RTPPacket(char *inPacket = NULL, UInt32 inLen = NULL)
: fPacket(reinterpret_cast<RTPHeader *>(inPacket)), fLen(inLen)
{}
UInt8 GetPayloadType() const { return ntohs(fPacket->rtpheader) & 0x007F; }
UInt8 GetCSRCCount() const { return (ntohs(fPacket->rtpheader) & 0x0F00 ) >> 8; }
//The following get functions will convert from network byte order to host byte order.
//Conversely the set functions will convert from host byte order to network byte order.
UInt16 GetSeqNum() const { return ntohs(fPacket->seq); }
void SetSeqNum(UInt16 seqNum) { fPacket->seq = htons(seqNum); }
UInt32 GetTimeStamp() const { return ntohl(fPacket->ts); }
void SetTimeStamp(UInt32 timeStamp) { fPacket->ts = htonl(timeStamp); }
UInt32 GetSSRC() const { return ntohl(fPacket->ssrc); }
void SetSSRC(UInt32 SSRC) { fPacket->ssrc = htonl(SSRC); }
//Includes the variable CSRC portion
UInt32 GetHeaderLen() const { return sizeof(RTPHeader) + GetCSRCCount() * 4; }
StrPtrLen GetBody() const { return StrPtrLen(reinterpret_cast<char *>(fPacket) + GetHeaderLen(), fLen - GetHeaderLen()); }
//Returns true if the header is not bad; do some very basic checking
Bool16 HeaderIsValid() const
{
Assert(sizeof(RTPHeader) == 12);
if (fLen < sizeof(RTPHeader))
return false;
if ( ( ntohs(fPacket->rtpheader) >> 14) != RTP_VERSION )
return false;
if (GetHeaderLen() > fLen)
return false;
return true;
}
RTPHeader * fPacket;
UInt32 fLen; //total length of the packet, including the header
};
#endif

1977
RTSPClientLib/RTSPClient.cpp Normal file

File diff suppressed because it is too large Load diff

474
RTSPClientLib/RTSPClient.h Normal file
View file

@ -0,0 +1,474 @@
/*
*
* @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: RTSPClient.h
Works only for QTSS.
Assumes that different streams within a session is distinguished by trackID=
For example, streams within sample.mov would be refered to as sample.mov/trackID=4
Does not work if the URL contains digits!!!
*/
#ifndef __RTSP_CLIENT_H__
#define __RTSP_CLIENT_H__
#include "OSHeaders.h"
#include "StrPtrLen.h"
#include "TCPSocket.h"
#include "ClientSocket.h"
#include "RTPMetaInfoPacket.h"
#include "StringFormatter.h"
#include "SVector.h"
// for authentication
#include "StringParser.h"
class Authenticator
{
public:
enum { kAuthBufferLen = 1024 };
enum
{
kNoType = 0,
kBasicType = 1,
kDigestType = 2 // higher number is stronger type
};
Authenticator();
virtual ~Authenticator() {};
virtual SInt16 GetType() { return 0;};
virtual Bool16 ParseParams(StrPtrLen *authParamsPtr) {return 0;};
virtual void AttachAuthParams(StrPtrLen *theRequestPtr) {};
virtual void ResetAuthParams() {};
StrPtrLen fAuthBuffer;
StrPtrLen fNameSPL;// client
StrPtrLen fPasswordSPL;// client
StrPtrLen fRealmSPL; // server
StrPtrLen fURISPL; // client
StrPtrLen fMethodSPL; // client -- must be all caps
time_t fAuthTime;
//the outHeaderStr argument is not actually implemented
//char *GetRequestHeader( StrPtrLen *inSourceStr, StrPtrLen *searchHeaderStr,StrPtrLen *outHeaderStr = NULL);
char *GetRequestHeader( StrPtrLen *inSourceStr, StrPtrLen *searchHeaderStr);
//Does nothing if the Authorization header is not found
void RemoveAuthLine(StrPtrLen *theRequestPtr);
static Bool16 CopyParam(StrPtrLen *inPtr, StrPtrLen *outPtr);
void SetName(StrPtrLen *inNamePtr);
void SetPassword(StrPtrLen *inPasswordPtr);
void SetMethod(StrPtrLen *inMethodStr);
void SetRealm(StrPtrLen *inRealmPtr);
void SetURI(StrPtrLen *inURIPtr);
void ResetRequestLen(StrPtrLen *theRequestPtr, StrPtrLen *theParamsPtr);
void ParseTag(StringParser *parserPtr,StrPtrLen *outTagPtr);
Bool16 GetParamValue(StringParser *valueSourcePtr, StrPtrLen *outParamValuePtr);
Bool16 GetParamValueAsNewCopy(StringParser *valueSourcePtr, StrPtrLen *outParamValueCopyPtr);
Bool16 GetMatchListParamValueAsNewCopy(StringParser *valueSourcePtr, StrPtrLen *inMatchListParamValuePtr, SInt16 numToMatch, StrPtrLen *outParamValueCopyPtr);
Bool16 ParseRealm(StringParser *realmParserPtr);
static StrPtrLen sAuthorizationStr;
static StrPtrLen sRealmStr;
static StrPtrLen sAuthBasicStr;
static StrPtrLen sAuthDigestStr;
static StrPtrLen sUsernameStr;
static StrPtrLen sWildCardMatch;
static StrPtrLen sTrue;
static StrPtrLen sFalse;
void Clean();
private:
StrPtrLen fCurrentAuthLine;
};
class BasicAuth : public Authenticator
{
public:
BasicAuth() {};
~BasicAuth() {Clean();}
SInt16 GetType() { return kBasicType; };
Bool16 ParseParams(StrPtrLen *authParamsPtr);
void AttachAuthParams(StrPtrLen *theRequestPtr);
UInt32 ParamsLen(StrPtrLen *requestParams);
void ResetAuthParams() {};
protected:
enum { kEncodedBufferLen = 256 };
char fEncodedBuffer[kEncodedBufferLen];
};
class DigestAuth : public Authenticator
{
public:
SInt16 GetType() { return kDigestType; };
Bool16 ParseParams(StrPtrLen *authParamsPtr);
void AttachAuthParams(StrPtrLen *theRequestPtr);
void ResetAuthParams();
StrPtrLen fcnonce;
StrPtrLen fNonceCountStr;
SInt16 fNonceCount;
StrPtrLen fnonce;
StrPtrLen fopaque;
StrPtrLen fqop;
StrPtrLen fAlgorithmStr;
StrPtrLen fStaleStr;
StrPtrLen fURIStr;
StrPtrLen fRequestDigestStr;
SInt16 fAlgorithm;
Bool16 fStale;
void GenerateAuthorizationRequestLine(StrPtrLen *requestPtr);
DigestAuth();
~DigestAuth();
enum {kMaxReqParams = 10};
private:
struct ReqFields
{
StrPtrLen *fReqParamTags[kMaxReqParams];
StrPtrLen *fReqParamValues[kMaxReqParams];
Bool16 fQuoted[kMaxReqParams];
SInt16 fNumFields;
};
ReqFields fReqFields;
void ReqFieldsClean() { memset( (void *) fReqFields.fReqParamTags,0,sizeof(StrPtrLen *) * kMaxReqParams);
memset( (void *) fReqFields.fReqParamValues,0,sizeof(StrPtrLen *) * kMaxReqParams);
memset( (void *) fReqFields.fQuoted,0,sizeof(Bool16) * kMaxReqParams);
fReqFields.fNumFields = 0;
}
void AddAuthParam(StrPtrLen *theTagPtr, StrPtrLen *theValuePtr, Bool16 quoted);
UInt32 ParamsLen(StrPtrLen *requestParams);
void SetNonceCountStr();
void MakeRequestDigest();
void MakeCNonce();
// request tags
static StrPtrLen sCnonceStr;
static StrPtrLen sUriStr;
static StrPtrLen sResponseStr;
static StrPtrLen sNonceCountStr;
// response tags
static StrPtrLen sStaleStr;
// request and response tags
static StrPtrLen sNonceStr;
static StrPtrLen sQopStr;
static StrPtrLen sOpaqueStr;
static StrPtrLen sDomainStr;
static StrPtrLen sAlgorithmStr;
// response values
static StrPtrLen sQopAuthStr;
static StrPtrLen sQopAuthIntStr;
static StrPtrLen sMD5Str;
static StrPtrLen sMD5SessStr;
};
class AuthParser
{
public:
AuthParser() {};
~AuthParser() {}
Authenticator *ParseChallenge(StrPtrLen *challenge);
};
class RTSPClient
{
public:
//
// Before using this class, you must set the User Agent this way.
static void SetUserAgentStr(char* inUserAgent) { sUserAgent = inUserAgent; }
// verbosePrinting = print out all requests and responses
RTSPClient(ClientSocket* inSocket, Bool16 verbosePrinting = false, char* inUserAgent = NULL);
~RTSPClient();
// This must be called before any other function. Sets up very important info; sets the URL
void Set(const StrPtrLen& inURL);
//
// This function allows you to add some special-purpose headers to your
// SETUP request if you want. These are mainly used for the caching proxy protocol,
// though there may be other uses.
//
// inLateTolerance: default = 0 (don't care)
// inMetaInfoFields: default = NULL (don't want RTP-Meta-Info packets
// inSpeed: default = 1 (normal speed)
void SetSetupParams(Float32 inLateTolerance, char* inMetaInfoFields);
// Send specified RTSP request to server, wait for complete response.
// These return EAGAIN if transaction is in progress, OS_NoErr if transaction
// was successful, EINPROGRESS if connection is in progress, or some other
// error if transaction failed entirely.
OS_Error SendDescribe(Bool16 inAppendJunkData = false);
OS_Error SendReliableUDPSetup(UInt32 inTrackID, UInt16 inClientPort);
OS_Error SendUDPSetup(UInt32 inTrackID, UInt16 inClientPort);
OS_Error SendTCPSetup(UInt32 inTrackID, UInt16 inClientRTPid, UInt16 inClientRTCPid);
OS_Error SendPlay(UInt32 inStartPlayTimeInSec, Float32 inSpeed = 1, UInt32 inTrackID = kUInt32_Max); //use a inTrackID of kUInt32_Max to turn off per stream headers
OS_Error SendPacketRangePlay(char* inPacketRangeHeader, Float32 inSpeed = 1);
OS_Error SendReceive(UInt32 inStartPlayTimeInSec);
OS_Error SendAnnounce(char *sdp);
OS_Error SendTeardown();
OS_Error SendInterleavedWrite(UInt8 channel, UInt16 len, char*data,Bool16 *getNext);
OS_Error SendSetParameter();
OS_Error SendOptions();
OS_Error SendOptionsWithRandomDataRequest(SInt32 dataSize);
//
// If you just want to send a generic request, do it this way
OS_Error SendRTSPRequest(iovec* inRequest, UInt32 inNumVecs);
//
// Once you call all of the above functions, assuming they return an error, you
// should call DoTransaction until it returns OS_NoErr, then you can move onto your
// next request
OS_Error DoTransaction();
//
// If any of the tracks are being interleaved, this fetches a media packet from
// the control connection. This function assumes that SendPlay has already completed
// successfully and media packets are being sent.
OS_Error GetMediaPacket( UInt32* outTrackID, Bool16* outIsRTCP,
char** outBuffer, UInt32* outLen);
//
// If any of the tracks are being interleaved, this puts a media packet to the control
// connection. This function assumes that SendPlay has already completed
// successfully and media packets are being sent.
OS_Error PutMediaPacket( UInt32 inTrackID, Bool16 isRTCP, char* inBuffer, UInt16 inLen);
// set name and password for authentication in case we are challenged
void SetName(char *name);
void SetPassword(char *password);
// set control id string default is "trackID"
void SetControlID(char* controlID);
// level 0, 1, 2, or 3
void SetVerboseLevel(UInt32 level) { fVerboseLevel = level; }
//Sets the 3GPP-Link-Char header values; set to all 0 to not send this header
void Set3GPPLinkChars(UInt32 GBW = 0, UInt32 MBW = 0, UInt32 MTD = 0)
{
fGuarenteedBitRate = GBW;
fMaxBitRate = MBW;
fMaxTransferDelay = MTD;
}
//Use 0 for undefined
void SetBandwidth(UInt32 bps = 0) { fBandwidth = bps; }
void Set3GPPRateAdaptation(UInt32 bufferSpace = 0, UInt32 delayTime = 0)
{
fBufferSpace = bufferSpace;
fDelayTime = delayTime;
}
// ACCESSORS
StrPtrLen* GetURL() { return &fURL; }
UInt32 GetStatus() { return fStatus; }
StrPtrLen* GetSessionID() { return &fSessionID; }
UInt16 GetServerPort() { return fServerPort; }
UInt32 GetContentLength() { return fContentLength; }
char* GetContentBody() { return fRecvContentBuffer; }
ClientSocket* GetSocket() { return fSocket; }
UInt32 GetTransportMode() { return fTransportMode; }
void SetTransportMode(UInt32 theMode) { fTransportMode = theMode; };
char* GetResponse() { return fRecvHeaderBuffer; }
UInt32 GetResponseLen() { return fHeaderLen; }
Bool16 IsTransactionInProgress() { return fState != kInitial; }
Bool16 IsVerbose() { return fVerboseLevel >= 1; }
// If available, returns the SSRC associated with the track in the PLAY response.
// Returns 0 if SSRC is not available.
UInt32 GetSSRCByTrack(UInt32 inTrackID);
enum { kPlayMode=0,kPushMode=1,kRecordMode=2};
//
// If available, returns the RTP-Meta-Info field ID array for
// a given track. For more details, see RTPMetaInfoPacket.h
RTPMetaInfoPacket::FieldID* GetFieldIDArrayByTrack(UInt32 inTrackID);
enum
{
kMinNumChannelElements = 5,
kReqBufSize = 4095,
kMethodBuffLen = 24 //buffer for "SETUP" or "PLAY" etc.
};
OSMutex* GetMutex() { return &fMutex; }
private:
static char* sUserAgent;
// Helper methods
void ParseInterleaveSubHeader(StrPtrLen* inSubHeader);
void ParseRTPInfoHeader(StrPtrLen* inHeader);
void ParseRTPMetaInfoHeader(StrPtrLen* inHeader);
//Use a inTrackID of kUInt32_Max to turn the Rate-Adaptation header off
void Attach3GPPHeaders(StringFormatter &fmt, UInt32 inTrackID = kUInt32_Max);
// Call this to receive an RTSP response from the server.
// Returns EAGAIN until a complete response has been received.
OS_Error ReceiveResponse();
OSMutex fMutex;//this data structure is shared!
AuthParser fAuthenticationParser;
Authenticator *fAuthenticator; // only one will be supported
ClientSocket* fSocket;
UInt32 fVerboseLevel;
// Information we need to send the request
StrPtrLen fURL;
UInt32 fCSeq;
// authenticate info
StrPtrLen fName;
StrPtrLen fPassword;
// Response data we get back
UInt32 fStatus;
StrPtrLen fSessionID;
UInt16 fServerPort;
UInt32 fContentLength;
//StrPtrLen fRTPInfoHeader;
// Special purpose SETUP params
char* fSetupHeaders;
// If we are interleaving, this maps channel numbers to track IDs
struct ChannelMapElem
{
UInt32 fTrackID;
Bool16 fIsRTCP;
};
ChannelMapElem* fChannelTrackMap;
UInt8 fNumChannelElements;
//Maps between trackID and SSRC number
struct SSRCMapElem
{
UInt32 fTrackID;
UInt32 fSSRC;
SSRCMapElem(UInt32 trackID = kUInt32_Max, UInt32 inSSRC = 0)
: fTrackID(trackID), fSSRC(inSSRC)
{}
};
SVector<SSRCMapElem> fSSRCMap;
// For storing FieldID arrays
struct FieldIDArrayElem
{
UInt32 fTrackID;
RTPMetaInfoPacket::FieldID fFieldIDs[RTPMetaInfoPacket::kNumFields];
};
FieldIDArrayElem* fFieldIDMap;
UInt32 fNumFieldIDElements;
UInt32 fFieldIDMapSize;
// If we are interleaving, we need this stuff to support the GetMediaPacket function
char* fPacketBuffer;
UInt32 fPacketBufferOffset;
Bool16 fPacketOutstanding;
// Data buffers
char fMethod[kMethodBuffLen]; // holds the current method
char fSendBuffer[kReqBufSize + 1]; // for sending requests
char fRecvHeaderBuffer[kReqBufSize + 1];// for receiving response headers
char* fRecvContentBuffer; // for receiving response body
// Tracking the state of our receives
UInt32 fContentRecvLen;
UInt32 fHeaderRecvLen;
UInt32 fHeaderLen;
UInt32 fSetupTrackID; //is valid during a Setup Transaction
enum { kInitial, kRequestSending, kResponseReceiving, kHeaderReceived };
UInt32 fState;
//UInt32 fEventMask;
Bool16 fAuthAttempted;
UInt32 fTransportMode;
//
// For tracking media data that got read into the header buffer
UInt32 fPacketDataInHeaderBufferLen;
char* fPacketDataInHeaderBuffer;
char* fUserAgent;
static char* sControlID;
char* fControlID;
//These values are for the wireless links only -- not end-to-end
//For the following values; use 0 for undefined.
UInt32 fGuarenteedBitRate; //kbps
UInt32 fMaxBitRate; //kbps
UInt32 fMaxTransferDelay; //milliseconds
UInt32 fBandwidth; //bps
UInt32 fBufferSpace; //bytes
UInt32 fDelayTime; //milliseconds
struct InterleavedParams
{
char *extraBytes;
int extraLen;
UInt8 extraChannel;
int extraByteOffset;
};
static InterleavedParams sInterleavedParams;
};
#endif //__CLIENT_SESSION_H__