Add even more of the source
This should be about everything needed to build so far?
This commit is contained in:
parent
af3619d4fa
commit
849723c9cf
547 changed files with 149239 additions and 0 deletions
1168
RTSPClientLib/ClientSession.cpp
Normal file
1168
RTSPClientLib/ClientSession.cpp
Normal file
File diff suppressed because it is too large
Load diff
340
RTSPClientLib/ClientSession.h
Normal file
340
RTSPClientLib/ClientSession.h
Normal 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__
|
358
RTSPClientLib/ClientSocket.cpp
Normal file
358
RTSPClientLib/ClientSocket.cpp
Normal 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
|
||||
}
|
||||
}
|
165
RTSPClientLib/ClientSocket.h
Normal file
165
RTSPClientLib/ClientSocket.h
Normal 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__
|
366
RTSPClientLib/PlayerSimulator.h
Normal file
366
RTSPClientLib/PlayerSimulator.h
Normal 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
113
RTSPClientLib/RTPPacket.h
Normal 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
1977
RTSPClientLib/RTSPClient.cpp
Normal file
File diff suppressed because it is too large
Load diff
474
RTSPClientLib/RTSPClient.h
Normal file
474
RTSPClientLib/RTSPClient.h
Normal 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__
|
Loading…
Add table
Add a link
Reference in a new issue