1168 lines
43 KiB
C++
1168 lines
43 KiB
C++
/*
|
|
*
|
|
* @APPLE_LICENSE_HEADER_START@
|
|
*
|
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
|
* file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_LICENSE_HEADER_END@
|
|
*
|
|
*/
|
|
/*
|
|
File: ClientSession.cpp
|
|
|
|
Contains: .
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include <arpa/inet.h>
|
|
//#include <stdlib.h>
|
|
#include "ClientSession.h"
|
|
#include "OSMemory.h"
|
|
#include "SafeStdLib.h"
|
|
#include "OSHeaders.h"
|
|
#include "OS.h"
|
|
#include "RTPPacket.h"
|
|
#include "RTCPPacket.h"
|
|
#include "RTCPRRPacket.h"
|
|
#include "RTCPAckPacketFmt.h"
|
|
#include "RTCPNADUPacketFmt.h"
|
|
|
|
//These two parameters governs how the client determines whether an incoming RTP packet is within the range of the sequence number or not.
|
|
enum {
|
|
kMaxDropOut = 3000, //The sequence number can be kMaxDropOut ahead of the reference.
|
|
kMaxMisorder = 100, //The sequence number can be kMaxMisorder behind of the reference.
|
|
kMaxUDPPacketSize = 1450
|
|
};
|
|
|
|
#define CLIENT_SESSION_DEBUG 0
|
|
|
|
static const SInt64 kMaxWaitTimeInMsec = 5000;
|
|
static const SInt64 kIdleTimeoutInMsec = 20000; // Time out in 20 seconds if nothing's doing
|
|
static const SInt16 kSanitySeqNumDifference = 3000; //how large a difference can two sequence number be and still be considered to be in range
|
|
|
|
UInt32 ClientSession::sActiveConnections = 0;
|
|
UInt32 ClientSession::sPlayingConnections = 0;
|
|
UInt32 ClientSession::sTotalConnectionAttempts = 0;
|
|
|
|
UInt32 ClientSession::sBytesReceived = 0;
|
|
UInt32 ClientSession::sPacketsReceived = 0;
|
|
|
|
char* ConvertBytesToCHexString( void* inValue, const UInt32 inValueLen)
|
|
{
|
|
static const char* kHEXChars={ "0123456789ABCDEF" };
|
|
|
|
UInt8* theDataPtr = (UInt8*) inValue;
|
|
UInt32 len = inValueLen *2;
|
|
|
|
char *theString = NEW char[len+1];
|
|
char *resultStr = theString;
|
|
if (theString != NULL)
|
|
{
|
|
UInt8 temp;
|
|
UInt32 count = 0;
|
|
for (count = 0; count < inValueLen; count++)
|
|
{
|
|
temp = *theDataPtr++;
|
|
*theString++ = kHEXChars[temp >> 4];
|
|
*theString++ = kHEXChars[temp & 0xF];
|
|
}
|
|
*theString = 0;
|
|
}
|
|
return resultStr;
|
|
}
|
|
|
|
|
|
ClientSession::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, UInt32 MBW, UInt32 MTD, Bool16 enableForcePlayoutDelay, UInt32 playoutDelay,
|
|
UInt32 bandwidth, UInt32 bufferSpace, UInt32 delayTime, UInt32 startPlayDelay,
|
|
char *controlID, char *name, char *password)
|
|
: fSocket(NULL),
|
|
fClient(NULL),
|
|
fTimeoutTask(this, kIdleTimeoutInMsec),
|
|
|
|
fDurationInSec(inDurationInSec - inStartPlayTimeInSec),
|
|
fStartPlayTimeInSec(inStartPlayTimeInSec),
|
|
fRTCPIntervalInMs(inRTCPIntervalInMS),
|
|
fOptionsIntervalInSec(inOptionsIntervalInSec),
|
|
|
|
fOptions(sendOptions),
|
|
fOptionsRequestRandomData(requestRandomData),
|
|
fOptionsRandomDataSize(randomDataSize),
|
|
fTransactionStartTimeMilli(0),
|
|
|
|
fState(kSendingDescribe),
|
|
fDeathReason(kDiedNormally),
|
|
fNumSetups(0),
|
|
fUDPSocketArray(NULL),
|
|
|
|
fPlayTime(0),
|
|
fTotalPlayTime(0),
|
|
fLastRTCPTime(0),
|
|
fTeardownImmediately(false),
|
|
fAppendJunk(inAppendJunkData),
|
|
fReadInterval(inReadInterval),
|
|
fSockRcvBufSize(inSockRcvBufSize),
|
|
|
|
fSpeed(inSpeed),
|
|
fPacketRangePlayHeader(inPacketRangePlayHeader),
|
|
|
|
fGuarenteedBitRate(GBW),
|
|
fMaxBitRate(MBW),
|
|
fMaxTransferDelay(MTD),
|
|
fEnableForcePlayoutDelay(enableForcePlayoutDelay),
|
|
fPlayoutDelay(playoutDelay),
|
|
fBandwidth(bandwidth),
|
|
fBufferSpace(bufferSpace),
|
|
fDelayTime(delayTime),
|
|
fStartPlayDelay(startPlayDelay),
|
|
fEnable3GPP(enable3GPP),
|
|
|
|
//fStats(NULL),
|
|
fOverbufferWindowSizeInK(inOverbufferWindowSizeInK),
|
|
fCurRTCPTrack(0),
|
|
fNumPacketsReceived(0),
|
|
fNumBytesReceived(0),
|
|
fVerboseLevel(verboseLevel),
|
|
fPlayerSimulator(verboseLevel)
|
|
{
|
|
this->SetTaskName("RTSPClientLib:ClientSession");
|
|
StrPtrLen theURL(inURL);
|
|
|
|
if (true == sendOptions)
|
|
fState = kSendingOptions;
|
|
|
|
#if CLIENT_SESSION_DEBUG
|
|
fVerboseLevel = kUInt32_Max; //maximum possible unsigned int value in 2's complement
|
|
#endif
|
|
if ( fVerboseLevel >= 2)
|
|
{
|
|
in_addr inAddrStruct;
|
|
inAddrStruct.s_addr = inAddr;
|
|
qtss_printf("Connecting to: %s, port %d\n", inet_ntoa(inAddrStruct), inPort);
|
|
}
|
|
|
|
//
|
|
// Construct the appropriate ClientSocket type depending on what type of client we are supposed to be
|
|
switch (inClientType)
|
|
{
|
|
case kRTSPUDPClientType:
|
|
{
|
|
fControlType = kRawRTSPControlType;
|
|
fTransportType = kUDPTransportType;
|
|
fSocket = NEW TCPClientSocket(Socket::kNonBlockingSocketType);
|
|
break;
|
|
}
|
|
case kRTSPTCPClientType:
|
|
{
|
|
fControlType = kRawRTSPControlType;
|
|
fTransportType = kTCPTransportType;
|
|
fSocket = NEW TCPClientSocket(Socket::kNonBlockingSocketType);
|
|
break;
|
|
}
|
|
case kRTSPHTTPClientType:
|
|
{
|
|
fControlType = kRTSPHTTPControlType;
|
|
fTransportType = kTCPTransportType;
|
|
fSocket = NEW HTTPClientSocket(theURL, inHTTPCookie, Socket::kNonBlockingSocketType);
|
|
break;
|
|
}
|
|
case kRTSPHTTPDropPostClientType:
|
|
{
|
|
fControlType = kRTSPHTTPDropPostControlType;
|
|
fTransportType = kTCPTransportType;
|
|
fSocket = NEW HTTPClientSocket(theURL, inHTTPCookie, Socket::kNonBlockingSocketType);
|
|
break;
|
|
}
|
|
case kRTSPReliableUDPClientType:
|
|
{
|
|
fControlType = kRawRTSPControlType;
|
|
fTransportType = kReliableUDPTransportType;
|
|
fSocket = NEW TCPClientSocket(Socket::kNonBlockingSocketType);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
qtss_printf("ClientSession: Attempt to create unsupported client type.\n");
|
|
::exit(-1);
|
|
}
|
|
}
|
|
|
|
fSocket->Set(inAddr, inPort);
|
|
|
|
//
|
|
// Construct the client object using this socket.
|
|
fClient = NEW RTSPClient(fSocket);
|
|
fClient->SetVerboseLevel(fVerboseLevel);
|
|
fClient->Set(theURL);
|
|
fClient->SetSetupParams(inLateTolerance, inMetaInfoFields);
|
|
fClient->SetBandwidth(fBandwidth);
|
|
if(controlID != NULL)
|
|
fClient->SetControlID(controlID);
|
|
|
|
if (enable3GPP)
|
|
{
|
|
fClient->Set3GPPLinkChars(fGuarenteedBitRate, fMaxBitRate, fMaxTransferDelay);
|
|
fClient->Set3GPPRateAdaptation(fBufferSpace, fDelayTime);
|
|
}
|
|
|
|
//user name and password
|
|
if (name != NULL && password != NULL)
|
|
{
|
|
fClient->SetName(name);
|
|
fClient->SetPassword(password);
|
|
}
|
|
|
|
//
|
|
// Start the connection process going
|
|
this->Signal(Task::kStartEvent);
|
|
}
|
|
|
|
ClientSession::~ClientSession()
|
|
{
|
|
if (fUDPSocketArray != NULL)
|
|
{
|
|
for (UInt32 x = 0; x < fSDPParser.GetNumStreams() * 2; x++)
|
|
{
|
|
OS_Error theErr = OS_NoErr;
|
|
|
|
while (theErr == OS_NoErr)
|
|
{
|
|
UInt32 theRemoteAddr = 0;
|
|
UInt32 theLength = 0;
|
|
UInt16 theRemotePort = 0;
|
|
char thePacketBuf[2048];
|
|
|
|
// Get a packet from one of the UDP sockets.
|
|
theErr = fUDPSocketArray[x]->RecvFrom(&theRemoteAddr, &theRemotePort,
|
|
&thePacketBuf[0], 2048,
|
|
&theLength);
|
|
}
|
|
delete fUDPSocketArray[x];
|
|
}
|
|
}
|
|
|
|
delete [] fUDPSocketArray;
|
|
delete fClient;
|
|
delete fSocket;
|
|
//delete fStats;
|
|
}
|
|
|
|
|
|
SInt64 ClientSession::Run()
|
|
{
|
|
EventFlags theEvents = this->GetEvents();
|
|
|
|
if (theEvents & Task::kStartEvent)
|
|
{
|
|
sActiveConnections++;
|
|
sTotalConnectionAttempts++;
|
|
//Sometimes the clientSession can be told to stop before it has a chance to start the connection
|
|
if (theEvents & ClientSession::kTeardownEvent)
|
|
fTeardownImmediately = true;
|
|
else
|
|
{
|
|
Assert(theEvents == Task::kStartEvent);
|
|
// Determine a random connection interval, and go away until that time comes around.
|
|
// Next time the event received would be Task::kIdleEvent, and the initial state is kSendingDescribe
|
|
return ((UInt32) ::rand()) % kMaxWaitTimeInMsec + 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (theEvents & Task::kTimeoutEvent)
|
|
{
|
|
if(fState == kDone)
|
|
return 0;
|
|
if ( fVerboseLevel >= 2)
|
|
qtss_printf("Session timing out.\n");
|
|
fDeathReason = kSessionTimedout;
|
|
fState = kDone;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If we've been told to TEARDOWN, do so.
|
|
if (theEvents & ClientSession::kTeardownEvent)
|
|
{
|
|
if ( fVerboseLevel >= 2)
|
|
qtss_printf("Session tearing down immediately.\n");
|
|
fTeardownImmediately = true;
|
|
}
|
|
|
|
// We have been told to delete ourselves. Do so... NOW!!!!!!!!!!!!!!!
|
|
if (theEvents & Task::kKillEvent)
|
|
{
|
|
if ( fVerboseLevel >= 2)
|
|
qtss_printf("Session killed.\n");
|
|
sActiveConnections--;
|
|
return -1;
|
|
}
|
|
|
|
// Refresh the timeout. There is some legit activity going on...
|
|
fTimeoutTask.RefreshTimeout();
|
|
|
|
OS_Error theErr = OS_NoErr;
|
|
|
|
while ((theErr == OS_NoErr) && (fState != kDone))
|
|
{
|
|
//
|
|
// Do the appropriate thing depending on our current state
|
|
switch (fState)
|
|
{
|
|
case kSendingOptions:
|
|
{
|
|
if (true == fOptionsRequestRandomData)
|
|
theErr = fClient->SendOptionsWithRandomDataRequest(fOptionsRandomDataSize);
|
|
else
|
|
theErr = fClient->SendOptions();
|
|
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Sent OPTIONS. Result = %"_U32BITARG_". Response code = %"_U32BITARG_"\n", theErr, fClient->GetStatus());
|
|
if (0 == fTransactionStartTimeMilli)
|
|
fTransactionStartTimeMilli = OS::Milliseconds();
|
|
|
|
if (theErr == OS_NoErr)
|
|
{
|
|
|
|
// Check that the OPTIONS response is a 200 OK. If not, bail
|
|
if (fClient->GetStatus() != 200)
|
|
{
|
|
theErr = ENOTCONN; // Exit the state machine
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( fVerboseLevel >= 3)
|
|
{
|
|
qtss_printf("--- Options transaction time ms = %qd ---\n", (SInt64) ( OS::Milliseconds() - fTransactionStartTimeMilli) );
|
|
SInt32 receivedLength = (SInt32) fClient->GetContentLength();
|
|
if (receivedLength != 0)
|
|
qtss_printf("--- Options Request Random Data Received requested = %"_S32BITARG_" received = %"_S32BITARG_" ---\n", fOptionsRandomDataSize, receivedLength);
|
|
|
|
StrPtrLenDel theBody(ConvertBytesToCHexString(fClient->GetContentBody(), receivedLength));
|
|
theBody.PrintStr("\n");
|
|
}
|
|
fState = kSendingDescribe;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case kSendingDescribe:
|
|
{
|
|
theErr = fClient->SendDescribe(fAppendJunk);
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Sent DESCRIBE. Result = %"_U32BITARG_". Response code = %"_U32BITARG_"\n", theErr, fClient->GetStatus());
|
|
if (theErr == OS_NoErr)
|
|
{
|
|
// Check that the DESCRIBE response is a 200 OK. If not, bail
|
|
if (fClient->GetStatus() != 200)
|
|
{
|
|
theErr = ENOTCONN; // Exit the state machine
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We've sent a describe and gotten a response from the server.
|
|
// Parse the response and look for track information.
|
|
|
|
fSDPParser.Parse(fClient->GetContentBody(), fClient->GetContentLength());
|
|
|
|
//
|
|
// The SDP must have been misformatted.
|
|
if (fSDPParser.GetNumStreams() == 0)
|
|
fDeathReason = kBadSDP;
|
|
|
|
//
|
|
// We have valid SDP. If this is a UDP connection, construct a UDP
|
|
// socket array to act as incoming sockets.
|
|
if ((fTransportType == kUDPTransportType) || (fTransportType == kReliableUDPTransportType))
|
|
this->SetupUDPSockets();
|
|
|
|
//
|
|
// Setup client stats
|
|
//delete fStats;
|
|
//fStats = NEW TrackStats[fSDPParser.GetNumStreams()];
|
|
fStats.resize(fSDPParser.GetNumStreams());
|
|
//::memset(fStats, 0, sizeof(TrackStats) * fSDPParser.GetNumStreams());
|
|
|
|
|
|
}
|
|
fPlayerSimulator.Setup(fSDPParser.GetNumStreams(), fDelayTime, fStartPlayDelay);
|
|
fState = kSendingSetup;
|
|
}
|
|
break;
|
|
}
|
|
case kSendingSetup:
|
|
{
|
|
// The SETUP request is different depending on whether we are interleaving or not
|
|
if (fTransportType == kUDPTransportType)
|
|
{
|
|
theErr = fClient->SendUDPSetup(fSDPParser.GetStreamInfo(fNumSetups)->fTrackID,
|
|
fUDPSocketArray[fNumSetups*2]->GetLocalPort());
|
|
}
|
|
else if (fTransportType == kTCPTransportType)
|
|
{
|
|
fSocket->SetRcvSockBufSize(fSockRcvBufSize); // Make the rcv buf really big
|
|
theErr = fClient->SendTCPSetup(fSDPParser.GetStreamInfo(fNumSetups)->fTrackID,fNumSetups * 2, (fNumSetups * 2) +1);
|
|
}
|
|
else if (fTransportType == kReliableUDPTransportType)
|
|
{
|
|
theErr = fClient->SendReliableUDPSetup(fSDPParser.GetStreamInfo(fNumSetups)->fTrackID,
|
|
fUDPSocketArray[fNumSetups*2]->GetLocalPort());
|
|
}
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Sent SETUP #%"_U32BITARG_". Result = %"_U32BITARG_". Response code = %"_U32BITARG_"\n",
|
|
fNumSetups, theErr, fClient->GetStatus());
|
|
//
|
|
// If this SETUP request / response is complete, check for errors, and if
|
|
// it succeeded, move onto the next SETUP. If we're done setting up all tracks,
|
|
// move onto PLAY.
|
|
if (theErr == OS_NoErr)
|
|
{
|
|
if (fClient->GetStatus() != 200)
|
|
{
|
|
theErr = ENOTCONN; // Exit the state machine
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Record the server port for RTCPs.
|
|
fStats[fNumSetups].fDestRTCPPort = fClient->GetServerPort() + 1;
|
|
|
|
//obtain the sampling rate of this stream
|
|
StringParser parser = StringParser(&fSDPParser.GetStreamInfo(fNumSetups)->fPayloadName);
|
|
parser.GetThru(NULL, '/');
|
|
UInt32 samplingRate = parser.ConsumeInteger(NULL);
|
|
Assert(samplingRate != 0);
|
|
|
|
//Generates the client SSRC
|
|
SInt64 ms = OS::Microseconds();
|
|
UInt32 ssrc = static_cast<UInt32>((ms >> 32) ^ ms) + ::rand();
|
|
fStats[fNumSetups].fClientSSRC = ssrc + fNumSetups;
|
|
|
|
fPlayerSimulator.SetupTrack(fNumSetups, samplingRate, fBufferSpace);
|
|
fNumSetups++;
|
|
if (fNumSetups == fSDPParser.GetNumStreams())
|
|
fState = kSendingPlay;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case kSendingPlay:
|
|
{
|
|
if (fPacketRangePlayHeader != NULL)
|
|
theErr = fClient->SendPacketRangePlay(fPacketRangePlayHeader, fSpeed);
|
|
else
|
|
theErr = fClient->SendPlay(fStartPlayTimeInSec, fSpeed);
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Sent PLAY. Result = %"_U32BITARG_". Response code = %"_U32BITARG_"\n", theErr, fClient->GetStatus());
|
|
//
|
|
// If this PLAY request / response is complete, then we are done with setting
|
|
// up all the client crap. Now all we have to do is receive the data until it's
|
|
// time to send the TEARDOWN
|
|
if (theErr == OS_NoErr)
|
|
{
|
|
if (fClient->GetStatus() != 200)
|
|
{
|
|
theErr = ENOTCONN; // Exit the state machine
|
|
break;
|
|
}
|
|
|
|
// Mark down the SSRC for each track, if possible.
|
|
for (UInt32 ssrcCount = 0; ssrcCount < fSDPParser.GetNumStreams(); ssrcCount++)
|
|
fStats[ssrcCount].fServerSSRC = fClient->GetSSRCByTrack(fSDPParser.GetStreamInfo(ssrcCount)->fTrackID);
|
|
|
|
fState = kPlaying;
|
|
sPlayingConnections++;
|
|
|
|
//
|
|
// Start our duration timer. Use this to figure out when to send a teardown
|
|
fPlayTime = fLastRTCPTime = OS::Milliseconds();
|
|
|
|
if(fVerboseLevel >= 1)
|
|
{
|
|
for (UInt32 i = 0; i < fSDPParser.GetNumStreams(); i++)
|
|
{
|
|
QTSS_RTPPayloadType type = fSDPParser.GetStreamInfo(i)->fPayloadType;
|
|
qtss_printf("Receiving track %"_U32BITARG_", trackID=%"_U32BITARG_", %s at time %"_S64BITARG_"\n",
|
|
i, fSDPParser.GetStreamInfo(i)->fTrackID,
|
|
type == qtssVideoPayloadType ? "video" : type == qtssAudioPayloadType ? "audio" : "unknown",
|
|
OS::Milliseconds());
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case kPlaying:
|
|
{
|
|
// Should we send a teardown? We should if either we've been told to teardown, or if our time has run out
|
|
SInt64 curTime = OS::Milliseconds();
|
|
fTotalPlayTime = curTime - fPlayTime;
|
|
if (((curTime - fPlayTime) > fDurationInSec * 1000) || (fTeardownImmediately))
|
|
{
|
|
sPlayingConnections--;
|
|
fState = kSendingTeardown;
|
|
break;
|
|
}
|
|
|
|
//Send RTCP if necessary; if we are using TCP for media transport, than 1 set of RTCP total is enough
|
|
Bool16 sendRTCP = ((curTime - fLastRTCPTime) > fRTCPIntervalInMs) && (fTransportType != kTCPTransportType);
|
|
sendRTCP |= (fPlayTime == fLastRTCPTime); //send the first RTCP ASAP.
|
|
if (sendRTCP)
|
|
{
|
|
//(void) fClient->SendSetParameter(); // test for keep alives and error responses
|
|
//(void) fClient->SendOptions(); // test for keep alives and error responses
|
|
for ( ; fCurRTCPTrack < fSDPParser.GetNumStreams(); fCurRTCPTrack++)
|
|
{
|
|
OS_Error err = this->SendRTCPPackets(fCurRTCPTrack);
|
|
if (fTransportType == kTCPTransportType && err != OS_NoErr)
|
|
{
|
|
theErr = err; //if error happens on a TCP RTCP socket, then bail
|
|
break;
|
|
}
|
|
}
|
|
if (theErr != OS_NoErr)
|
|
break;
|
|
|
|
//Done sending the RTCP's
|
|
fCurRTCPTrack = 0;
|
|
fLastRTCPTime = (curTime == fLastRTCPTime) ? curTime + 1 : curTime;
|
|
|
|
//Drop the POST portion of the HTTP connection after every send
|
|
if (fControlType == kRTSPHTTPDropPostControlType)
|
|
((HTTPClientSocket*)fSocket)->ClosePost();
|
|
}
|
|
|
|
//Now read the media data
|
|
theErr = this->ReadMediaData();
|
|
|
|
if ((theErr == EINPROGRESS) || (theErr == EAGAIN) || (theErr == OS_NoErr))
|
|
theErr = OS_NoErr; //ignore control flow errors here
|
|
else
|
|
{
|
|
sPlayingConnections--;
|
|
break;
|
|
}
|
|
curTime = OS::Milliseconds();
|
|
SInt64 nextRTCPTime = fLastRTCPTime + fRTCPIntervalInMs;
|
|
//return curTime < nextRTCPTime ? nextRTCPTime - curTime : fReadInterval;
|
|
return curTime < nextRTCPTime ? MIN(nextRTCPTime - curTime, fReadInterval) : 1;
|
|
}
|
|
|
|
case kSendingTeardown:
|
|
{
|
|
theErr = fClient->SendTeardown();
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Sending TEARDOWN. Result = %"_U32BITARG_". Response code = %"_U32BITARG_"\n", theErr, fClient->GetStatus());
|
|
// Once the TEARDOWN is complete, we are done, so mark ourselves as dead, and wait
|
|
// for the owner of this object to delete us
|
|
if (theErr == OS_NoErr)
|
|
fState = kDone;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((theErr == EINPROGRESS) || (theErr == EAGAIN))
|
|
{
|
|
//
|
|
// Request an async event
|
|
fSocket->GetSocket()->SetTask(this);
|
|
fSocket->GetSocket()->RequestEvent(fSocket->GetEventMask());
|
|
}
|
|
else if (theErr != OS_NoErr)
|
|
{
|
|
//
|
|
// We encountered some fatal error with the socket. Record this as a connection failure
|
|
if (fState == kSendingTeardown)
|
|
fDeathReason = kTeardownFailed;
|
|
else if (fState == kPlaying)
|
|
fDeathReason = kDiedWhilePlaying;
|
|
else if (fClient->GetStatus() != 200)
|
|
fDeathReason = kRequestFailed;
|
|
else
|
|
fDeathReason = kConnectionFailed;
|
|
|
|
fState = kDone;
|
|
}
|
|
|
|
if ( fVerboseLevel >= 2)
|
|
if (fState == kDone)
|
|
qtss_printf("Client connection complete. Death reason = %"_U32BITARG_"\n", fDeathReason);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void ClientSession::SetupUDPSockets()
|
|
{
|
|
|
|
static UInt16 sCurrentRTPPortToUse = 6970;
|
|
static const UInt16 kMinRTPPort = 6970;
|
|
static const UInt16 kMaxRTPPort = 36970;
|
|
|
|
OS_Error theErr = OS_NoErr;
|
|
|
|
//
|
|
// Create a UDP socket pair (RTP, RTCP) for each stream
|
|
fUDPSocketArray = NEW UDPSocket*[fSDPParser.GetNumStreams() * 2];
|
|
for (UInt32 x = 0; x < fSDPParser.GetNumStreams() * 2; x++)
|
|
{
|
|
fUDPSocketArray[x] = NEW UDPSocket(this, Socket::kNonBlockingSocketType);
|
|
theErr = fUDPSocketArray[x]->Open();
|
|
if (theErr != OS_NoErr)
|
|
{
|
|
qtss_printf("ClientSession: Failed to open a UDP socket.\n");
|
|
::exit(-1);
|
|
}
|
|
}
|
|
|
|
for (UInt32 y = 0; y < fSDPParser.GetNumStreams(); y++)
|
|
{
|
|
for (UInt32 portCheck = 0; true; portCheck++)
|
|
{
|
|
theErr = fUDPSocketArray[y * 2]->Bind(INADDR_ANY, sCurrentRTPPortToUse);
|
|
if (theErr == OS_NoErr)
|
|
theErr = fUDPSocketArray[(y*2)+1]->Bind(INADDR_ANY, sCurrentRTPPortToUse + 1);
|
|
|
|
sCurrentRTPPortToUse += 2;
|
|
if (sCurrentRTPPortToUse > 30000)
|
|
sCurrentRTPPortToUse = 6970;
|
|
|
|
if (theErr == OS_NoErr)
|
|
{
|
|
// This is a good pair. Set the rcv buf on the RTP socket to be really big
|
|
fUDPSocketArray[y * 2]->SetSocketRcvBufSize(fSockRcvBufSize);
|
|
break;
|
|
}
|
|
|
|
if (sCurrentRTPPortToUse == kMaxRTPPort)
|
|
sCurrentRTPPortToUse = kMinRTPPort;
|
|
if (portCheck == 5000)
|
|
{
|
|
// Make sure we don't loop forever trying to bind a UDP socket. If we can't
|
|
// after a certain point, just bail...
|
|
qtss_printf("ClientSession: Failed to bind a UDP socket.\n");
|
|
::exit(-1);
|
|
}
|
|
}
|
|
}
|
|
if ( fVerboseLevel >= 3)
|
|
qtss_printf("Opened UDP sockets for %"_U32BITARG_" streams\n", fSDPParser.GetNumStreams());
|
|
}
|
|
|
|
//Will keep reading until all the packets buffered up has been read.
|
|
OS_Error ClientSession::ReadMediaData()
|
|
{
|
|
// For iterating over the array of UDP sockets
|
|
UInt32 theUDPSockIndex = 0;
|
|
OS_Error theErr = OS_NoErr;
|
|
|
|
while (true)
|
|
{
|
|
//
|
|
// If the media data is being interleaved, get it from the control connection
|
|
UInt32 theTrackID = 0;
|
|
UInt32 theLength = 0;
|
|
Bool16 isRTCP = false;
|
|
char* thePacket = NULL;
|
|
|
|
if (fTransportType == kTCPTransportType)
|
|
{
|
|
thePacket = NULL;
|
|
theErr = fClient->GetMediaPacket(&theTrackID, &isRTCP, &thePacket, &theLength);
|
|
if (thePacket == NULL)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
static const UInt32 kMaxPacketSize = 2048;
|
|
|
|
UInt32 theRemoteAddr = 0;
|
|
UInt16 theRemotePort = 0;
|
|
char thePacketBuf[kMaxPacketSize];
|
|
|
|
// Get a packet from one of the UDP sockets.
|
|
theErr = fUDPSocketArray[theUDPSockIndex]->RecvFrom(&theRemoteAddr, &theRemotePort,
|
|
&thePacketBuf[0], kMaxPacketSize,
|
|
&theLength);
|
|
if ((theErr != OS_NoErr) || (theLength == 0))
|
|
{
|
|
//Finished processing all the UDP packets that have been buffered up by the lower layer.
|
|
if ((fTransportType == kReliableUDPTransportType) && (!(theUDPSockIndex & 1)))
|
|
{
|
|
UInt32 trackIndex = TrackID2TrackIndex(fSDPParser.GetStreamInfo(theUDPSockIndex / 2)->fTrackID);
|
|
SendAckPackets(trackIndex);
|
|
/*
|
|
for (UInt32 trackIndex = 0; trackIndex < fSDPParser.GetNumStreams(); trackIndex++)
|
|
{
|
|
if (fSDPParser.GetStreamInfo(trackIndex)->fTrackID == fSDPParser.GetStreamInfo(theUDPSockIndex / 2)->fTrackID)
|
|
{
|
|
SendAckPackets(trackIndex);
|
|
|
|
//if (fStats[trackCount].fHighestSeqNumValid)
|
|
// If we are supposed to be sending acks, and we just finished
|
|
// receiving all packets for this track that are available at this time,
|
|
// send an ACK packet
|
|
//this->AckPackets(trackCount, 0, false);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
theUDPSockIndex++;
|
|
if (theUDPSockIndex == fSDPParser.GetNumStreams() * 2)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
theTrackID = fSDPParser.GetStreamInfo(theUDPSockIndex / 2)->fTrackID;
|
|
isRTCP = (theUDPSockIndex & 1);
|
|
thePacket = &thePacketBuf[0];
|
|
}
|
|
//
|
|
// We have a valid packet. Invoke the packet handler function
|
|
if (isRTCP)
|
|
this->ProcessRTCPPacket(thePacket, theLength, theTrackID);
|
|
else
|
|
this->ProcessRTPPacket(thePacket, theLength, theTrackID);
|
|
}
|
|
return theErr;
|
|
}
|
|
|
|
void ClientSession::ProcessRTPPacket(char* inPacket, UInt32 inLength, UInt32 inTrackID)
|
|
{
|
|
UInt32 trackIndex = TrackID2TrackIndex(inTrackID);
|
|
if (trackIndex == kUInt32_Max)
|
|
{ if(fVerboseLevel >= 3)
|
|
qtss_printf("ClientSession::ProcessRTPPacket fatal packet processing error. unknown track\n");
|
|
return;
|
|
}
|
|
TrackStats &trackStats = fStats[trackIndex];
|
|
|
|
if(fVerboseLevel >= 3)
|
|
{
|
|
SInt64 curTime = OS::Milliseconds();
|
|
qtss_printf("Processing incoming packets at time %"_S64BITARG_"\n", curTime);
|
|
}
|
|
|
|
//first validate the header and check the SSRC
|
|
Bool16 badPacket = false;
|
|
RTPPacket rtpPacket = RTPPacket(inPacket, inLength);
|
|
if (!rtpPacket.HeaderIsValid())
|
|
badPacket = true;
|
|
else
|
|
{
|
|
if (trackStats.fServerSSRC != 0)
|
|
{
|
|
if (rtpPacket.GetSSRC() != trackStats.fServerSSRC)
|
|
badPacket = true;
|
|
}
|
|
else //obtain the SSRC from the first packet if it's not available
|
|
trackStats.fServerSSRC = rtpPacket.GetSSRC();
|
|
}
|
|
|
|
if(badPacket)
|
|
{
|
|
trackStats.fNumMalformedPackets++;
|
|
if (fVerboseLevel >= 1)
|
|
qtss_printf("TrackID=%"_U32BITARG_", len=%"_U32BITARG_"; malformed packet\n", inTrackID, inLength);
|
|
return;
|
|
}
|
|
|
|
//Now check the sequence number
|
|
UInt32 packetSeqNum = kUInt32_Max;
|
|
if (trackStats.fHighestSeqNum == kUInt32_Max) //this is the first sequence number received
|
|
packetSeqNum = trackStats.fBaseSeqNum = trackStats.fHighestSeqNum = static_cast<UInt32>(rtpPacket.GetSeqNum());
|
|
else
|
|
packetSeqNum = CalcSeqNum(trackStats.fHighestSeqNum, rtpPacket.GetSeqNum());
|
|
|
|
if (packetSeqNum == kUInt32_Max) //sequence number is out of range
|
|
{
|
|
trackStats.fNumOutOfBoundPackets++;
|
|
if (fVerboseLevel >= 2)
|
|
qtss_printf("TrackID=%"_U32BITARG_", len=%"_U32BITARG_", seq=%u"", ref(32)=%"_U32BITARG_"; out of bound packet\n",
|
|
inTrackID, inLength, rtpPacket.GetSeqNum(), trackStats.fHighestSeqNum);
|
|
}
|
|
else
|
|
{
|
|
//the packet is good -- update statisics
|
|
Bool16 packetIsOutOfOrder = false;
|
|
if (trackStats.fHighestSeqNum <= packetSeqNum)
|
|
trackStats.fHighestSeqNum = packetSeqNum;
|
|
else
|
|
packetIsOutOfOrder = true;
|
|
|
|
fNumPacketsReceived++;
|
|
sPacketsReceived++;
|
|
trackStats.fNumPacketsReceived++;
|
|
fNumBytesReceived += inLength;
|
|
sBytesReceived += inLength;
|
|
trackStats.fNumBytesReceived += inLength;
|
|
|
|
//record this sequence number so that it can be acked later on
|
|
if (fTransportType == kReliableUDPTransportType)
|
|
trackStats.fPacketsToAck.push_back(packetSeqNum);
|
|
|
|
if (fVerboseLevel >= 3)
|
|
qtss_printf("TrackID=%"_U32BITARG_", len=%"_U32BITARG_", seq(32)=%"_U32BITARG_", ref(32)=%"_U32BITARG_"; good packet\n",
|
|
inTrackID, inLength, packetSeqNum, trackStats.fHighestSeqNum);
|
|
|
|
//RTP-Meta-Info
|
|
RTPMetaInfoPacket::FieldID* theMetaInfoFields = fClient->GetFieldIDArrayByTrack(inTrackID);
|
|
if (theMetaInfoFields != NULL)
|
|
{
|
|
//
|
|
// This packet is an RTP-Meta-Info packet. Parse it out and print out the results
|
|
RTPMetaInfoPacket theMetaInfoPacket;
|
|
Bool16 packetOK = theMetaInfoPacket.ParsePacket((UInt8*)inPacket, inLength, theMetaInfoFields);
|
|
if (!packetOK)
|
|
{
|
|
if( fVerboseLevel >= 2)
|
|
qtss_printf("Received invalid RTP-Meta-Info packet\n");
|
|
}
|
|
else if( fVerboseLevel >= 2)
|
|
{
|
|
qtss_printf("---\n");
|
|
qtss_printf("TrackID: %"_U32BITARG_"\n", inTrackID);
|
|
qtss_printf("Packet transmit time: %"_64BITARG_"d\n", theMetaInfoPacket.GetTransmitTime());
|
|
qtss_printf("Frame type: %u\n", theMetaInfoPacket.GetFrameType());
|
|
qtss_printf("Packet number: %"_64BITARG_"u\n", theMetaInfoPacket.GetPacketNumber());
|
|
qtss_printf("Packet position: %"_64BITARG_"u\n", theMetaInfoPacket.GetPacketPosition());
|
|
qtss_printf("Media data length: %"_U32BITARG_"\n", theMetaInfoPacket.GetMediaDataLen());
|
|
}
|
|
}
|
|
|
|
|
|
if (fEnable3GPP)
|
|
{
|
|
UInt32 timeStamp = rtpPacket.GetTimeStamp();
|
|
Bool16 packetIsDuplicate = fPlayerSimulator.ProcessRTPPacket(trackIndex, inLength, rtpPacket.GetBody().Len, packetSeqNum, timeStamp);
|
|
if(packetIsOutOfOrder && !packetIsDuplicate)
|
|
trackStats.fNumOutOfOrderPackets++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ClientSession::ProcessRTCPPacket(char* inPacket, UInt32 inLength, UInt32 inTrackID)
|
|
{
|
|
UInt32 trackIndex = TrackID2TrackIndex(inTrackID);
|
|
if (trackIndex == kUInt32_Max)
|
|
{
|
|
if (fVerboseLevel >= 3)
|
|
qtss_printf("ClientSession::ProcessRTCPPacket fatal packet processing error. unknown track\n");
|
|
return;
|
|
}
|
|
TrackStats &trackStats = fStats[trackIndex];
|
|
|
|
SInt64 curTime = OS::Milliseconds();
|
|
if(fVerboseLevel >= 2)
|
|
qtss_printf("Processing incoming RTCP packets on track %"_U32BITARG_" at time %"_S64BITARG_"\n", trackIndex, curTime);
|
|
|
|
//first validate the header and check the SSRC
|
|
RTCPSenderReportPacket packet;
|
|
Bool16 badPacket = !packet.ParseReport(reinterpret_cast<UInt8 *>(inPacket), inLength);
|
|
if (!badPacket)
|
|
{
|
|
if (trackStats.fServerSSRC != 0)
|
|
{
|
|
if (packet.GetPacketSSRC() != trackStats.fServerSSRC)
|
|
badPacket = true;
|
|
}
|
|
else //obtain the SSRC from the first packet if it's not available
|
|
trackStats.fServerSSRC = packet.GetPacketSSRC();
|
|
}
|
|
|
|
if(badPacket)
|
|
{
|
|
if (fVerboseLevel >= 1)
|
|
qtss_printf("TrackID=%"_U32BITARG_", len=%"_U32BITARG_"; malformed RTCP packet\n", inTrackID, inLength);
|
|
return;
|
|
}
|
|
|
|
//Now obtains the NTP timestamp and the current time -- we'll need it for the LSR field of the receiver report
|
|
trackStats.fLastSenderReportNTPTime = packet.GetNTPTimeStamp();
|
|
trackStats.fLastSenderReportLocalTime = curTime;
|
|
}
|
|
|
|
|
|
void ClientSession::SendAckPackets(UInt32 inTrackIndex)
|
|
{
|
|
TrackStats &trackStats = fStats[inTrackIndex];
|
|
|
|
if (trackStats.fPacketsToAck.empty())
|
|
return;
|
|
trackStats.fNumAcks++;
|
|
|
|
if(fVerboseLevel >= 3)
|
|
{
|
|
SInt64 curTime = OS::Milliseconds();
|
|
qtss_printf("Sending %"_U32BITARG_" acks at time %"_S64BITARG_" on track %"_U32BITARG_"\n",
|
|
trackStats.fPacketsToAck.size(), curTime, inTrackIndex);
|
|
}
|
|
|
|
char sendBuffer[kMaxUDPPacketSize];
|
|
|
|
//First send an empty Receivor Report
|
|
RTCPRRPacket RRPacket = RTCPRRPacket(sendBuffer, kMaxUDPPacketSize);
|
|
RRPacket.SetSSRC(trackStats.fClientSSRC);
|
|
StrPtrLen buffer = RRPacket.GetBufferRemaining();
|
|
|
|
//Now send the Ack packets
|
|
RTCPAckPacketFmt ackPacket = RTCPAckPacketFmt(buffer);
|
|
ackPacket.SetSSRC(trackStats.fClientSSRC);
|
|
ackPacket.SetAcks(trackStats.fPacketsToAck, trackStats.fServerSSRC);
|
|
trackStats.fPacketsToAck.clear();
|
|
|
|
UInt32 packetLength = RRPacket.GetPacketLen() + ackPacket.GetPacketLen();
|
|
|
|
// Send the packet
|
|
Assert(trackStats.fDestRTCPPort != 0);
|
|
fUDPSocketArray[(inTrackIndex*2)+1]->SendTo(fSocket->GetHostAddr(), trackStats.fDestRTCPPort, sendBuffer, packetLength);
|
|
}
|
|
|
|
OS_Error ClientSession::SendRTCPPackets(UInt32 trackIndex)
|
|
{
|
|
TrackStats &trackStats = fStats[trackIndex];
|
|
|
|
char buffer[kMaxUDPPacketSize];
|
|
//::memset(buffer, 0, kMaxUDPPacketSize);
|
|
|
|
//First send the RTCP Receiver Report packet
|
|
RTCPRRPacket RRPacket = RTCPRRPacket(buffer, kMaxUDPPacketSize);
|
|
RRPacket.SetSSRC(trackStats.fClientSSRC);
|
|
|
|
UInt8 fracLost = 0;
|
|
SInt32 cumLostPackets = 0;
|
|
UInt32 lsr = 0;
|
|
UInt32 dlsr = 0;
|
|
SInt64 curTime = OS::Milliseconds();
|
|
if (trackStats.fHighestSeqNum != kUInt32_Max)
|
|
{
|
|
CalcRTCPRRPacketsLost(trackIndex, fracLost, cumLostPackets);
|
|
|
|
//Now get the middle 32 bits of the NTP time stamp and send it as the LSR
|
|
lsr = static_cast<UInt32>(trackStats.fLastSenderReportNTPTime >> 16);
|
|
|
|
//Get the time difference expressed as units of 1/65536 seconds
|
|
if (trackStats.fLastSenderReportLocalTime != 0)
|
|
dlsr = static_cast<UInt32>(OS::TimeMilli_To_Fixed64Secs(curTime - trackStats.fLastSenderReportLocalTime) >> 16);
|
|
|
|
RRPacket.AddReportBlock(trackStats.fServerSSRC, fracLost, cumLostPackets, trackStats.fHighestSeqNum, lsr, dlsr);
|
|
}
|
|
|
|
StrPtrLen remainingBuf = RRPacket.GetBufferRemaining();
|
|
UInt32 *theWriter = reinterpret_cast<UInt32 *>(remainingBuf.Ptr);
|
|
|
|
// RECEIVER REPORT
|
|
/*
|
|
*(theWriter++) = htonl(0x81c90007); // 1 src RR packet
|
|
*(theWriter++) = htonl(0);
|
|
*(theWriter++) = htonl(0);
|
|
*(theWriter++) = htonl(0);
|
|
*(theWriter++) = htonl(trackStats.fHighestSeqNum == kUInt32_Max ? 0 : trackStats.fHighestSeqNum); //EHSN
|
|
*(theWriter++) = 0; // don't do jitter yet.
|
|
*(theWriter++) = 0; // don't do last SR timestamp
|
|
*(theWriter++) = 0; // don't do delay since last SR
|
|
*/
|
|
|
|
//The implementation should be sending an SDES to conform to the standard...but its not done.
|
|
|
|
if(fTransportType == kRTSPReliableUDPClientType)
|
|
{
|
|
// APP PACKET - QoS info
|
|
*(theWriter++) = htonl(0x80CC000C);
|
|
//*(ia++) = htonl(trk[i].TrackSSRC);
|
|
*(theWriter++) = htonl(trackStats.fClientSSRC);
|
|
// this QTSS changes after beta to 'qtss'
|
|
*(theWriter++) = htonl(FOUR_CHARS_TO_INT('Q', 'T', 'S', 'S'));
|
|
//*(ia++) = toBigEndian_ulong(trk[i].rcvrSSRC);
|
|
*(theWriter++) = htonl(trackStats.fServerSSRC);
|
|
*(theWriter++) = htonl(8); // number of 4-byte quants below
|
|
#define RR 0x72720004
|
|
#define PR 0x70720004
|
|
#define PD 0x70640002
|
|
#define OB 0x6F620004
|
|
*(theWriter++) = htonl(RR);
|
|
//unsigned int now, secs;
|
|
//now = microseconds();
|
|
//secs = now - trk[i].last_rtcp_packet_sent_us / USEC_PER_SEC;
|
|
//if (secs)
|
|
// temp = trk[i].bytes_received_since_last_rtcp / secs;
|
|
//else
|
|
// temp = 0;
|
|
//*(ia++) = htonl(temp);
|
|
*(theWriter++) = htonl(0);
|
|
*(theWriter++) = htonl(PR);
|
|
//*(ia++) = htonl(trk[i].rtp_num_received);
|
|
*(theWriter++) = htonl(0);
|
|
//*(theWriter++) = htonl(PL);
|
|
//*(ia++) = htonl(trk[i].rtp_num_lost);
|
|
//*(theWriter++) = htonl(0);
|
|
*(theWriter++) = htonl(OB);
|
|
*(theWriter++) = htonl(fOverbufferWindowSizeInK * 1024);
|
|
*(theWriter++) = htonl(PD);
|
|
*(theWriter++) = htonl(0); // should be a short, but we need to pad to a long for the entire RTCP app packet
|
|
}
|
|
|
|
char *buf = reinterpret_cast<char *>(theWriter);
|
|
UInt32 packetLen = buf - buffer;
|
|
UInt32 playoutDelay = 0;
|
|
|
|
if (fEnable3GPP && trackStats.fHighestSeqNum != kUInt32_Max)
|
|
{
|
|
//now add the RTCP NADU packet
|
|
RTCPNADUPacketFmt nadu = RTCPNADUPacketFmt(buf, kMaxUDPPacketSize - packetLen);
|
|
nadu.SetSSRC(trackStats.fClientSSRC);
|
|
|
|
//If there is no packet in the buffer, use the extended highest sequence number received + 1
|
|
UInt32 seqNum = fPlayerSimulator.GetNextSeqNumToDecode(trackIndex);
|
|
if(seqNum == kUInt32_Max)
|
|
seqNum = trackStats.fHighestSeqNum + 1;
|
|
|
|
if (fEnableForcePlayoutDelay)
|
|
playoutDelay = fPlayoutDelay;
|
|
else
|
|
playoutDelay = fPlayerSimulator.GetPlayoutDelay(trackIndex);
|
|
|
|
nadu.AddNADUBlock(0, seqNum, 0, fPlayerSimulator.GetFreeBufferSpace(trackIndex), playoutDelay);
|
|
|
|
packetLen += nadu.GetPacketLen();
|
|
}
|
|
|
|
if(fVerboseLevel >= 2)
|
|
{
|
|
qtss_printf("Sending receiver report at time %"_S64BITARG_" on track %"_U32BITARG_"; lostFrac=%u, lost=%"_S32BITARG_
|
|
", FBS=%"_U32BITARG_", delay=%"_U32BITARG_", lsr=%"_U32BITARG_", dlsr=%"_U32BITARG_"\n",
|
|
curTime, trackIndex, fracLost, cumLostPackets,
|
|
fPlayerSimulator.GetFreeBufferSpace(trackIndex),
|
|
playoutDelay,
|
|
lsr, dlsr);
|
|
}
|
|
|
|
// Send the packet
|
|
if (fUDPSocketArray != NULL)
|
|
{
|
|
Assert(trackStats.fDestRTCPPort != 0);
|
|
fUDPSocketArray[(trackIndex*2)+1]->SendTo(fSocket->GetHostAddr(), trackStats.fDestRTCPPort, buffer, packetLen);
|
|
}
|
|
else
|
|
{
|
|
OS_Error theErr = fClient->PutMediaPacket(fSDPParser.GetStreamInfo(trackIndex)->fTrackID, true, buffer, packetLen);
|
|
if (theErr != OS_NoErr)
|
|
return theErr;
|
|
|
|
}
|
|
return OS_NoErr;
|
|
}
|
|
|
|
|
|
//The lost packets in the RTCP RR are defined differently then the GetNumPacketsLost function. See RFC 3550 6.4.1 and A.3
|
|
void ClientSession::CalcRTCPRRPacketsLost(UInt32 trackIndex, UInt8 &outFracLost, SInt32 &outCumLostPackets)
|
|
{
|
|
TrackStats &trackStats = fStats[trackIndex];
|
|
|
|
if (trackStats.fHighestSeqNum == kUInt32_Max)
|
|
{
|
|
outFracLost = 0;
|
|
outCumLostPackets = 0;
|
|
return;
|
|
}
|
|
|
|
UInt32 expected = trackStats.fHighestSeqNum - trackStats.fBaseSeqNum + 1;
|
|
UInt32 expectedInterval = expected - trackStats.fExpectedPrior;
|
|
UInt32 receivedInterval = trackStats.fNumPacketsReceived - trackStats.fReceivedPrior;
|
|
|
|
trackStats.fExpectedPrior = expected;
|
|
trackStats.fReceivedPrior = trackStats.fNumPacketsReceived;
|
|
|
|
if (expectedInterval == 0 || expectedInterval < receivedInterval)
|
|
outFracLost = 0;
|
|
else
|
|
{
|
|
UInt32 lostInterval = expectedInterval - receivedInterval;
|
|
outFracLost = static_cast<UInt8>((lostInterval << 8) / expectedInterval);
|
|
}
|
|
outCumLostPackets = expected - trackStats.fNumPacketsReceived;
|
|
}
|
|
|
|
//If newSeqNum is no more than kMaxMisorder behind and kMaxDropOut ahead of referenceSeqNum(modulo 2^16), returns the corresponding
|
|
//32 bit sequence number. Otherwise returns kUInt32_Max
|
|
UInt32 ClientSession::CalcSeqNum(UInt32 referenceSeqNum, UInt16 newSeqNum)
|
|
{
|
|
|
|
UInt16 refSeqNum16 = static_cast<UInt16>(referenceSeqNum);
|
|
UInt32 refSeqNumHighBits = referenceSeqNum >> 16;
|
|
|
|
if (static_cast<UInt16>(newSeqNum - refSeqNum16) <= kMaxDropOut)
|
|
{
|
|
//new sequence number is ahead and is in range
|
|
if (newSeqNum >= refSeqNum16)
|
|
return (refSeqNumHighBits << 16) | newSeqNum; //no wrap-around
|
|
else
|
|
return ((refSeqNumHighBits + 1) << 16) | newSeqNum; //wrapped-around
|
|
}
|
|
else if (static_cast<UInt16>(refSeqNum16 - newSeqNum) < kMaxMisorder)
|
|
{
|
|
//new sequence number is behind and is in range
|
|
if (newSeqNum < refSeqNum16)
|
|
return (refSeqNumHighBits << 16) | newSeqNum; //no wrap-around
|
|
else if (refSeqNumHighBits != 0)
|
|
return ((refSeqNumHighBits - 1) << 16) | newSeqNum; //wrapped-around
|
|
}
|
|
return kUInt32_Max; //bad sequence number(out of range)
|
|
/*
|
|
if (refSeqNum16 <= newSeqNum)
|
|
{
|
|
UInt16 diff = newSeqNum - refSeqNum16;
|
|
if (diff <= kMaxDropOut) //new sequence number is ahead and is in range, no overflow
|
|
return (refSeqNumHighBits << 16) | newSeqNum;
|
|
|
|
diff = kUInt16_Max - newSeqNum;
|
|
diff += refSeqNum16 + 1;
|
|
if (diff <= kMaxMisorder && refSeqNumHighBits != 0) //new sequence number is behind, underflow
|
|
return ((refSeqNumHighBits - 1) << 16) | newSeqNum;
|
|
}
|
|
else
|
|
{
|
|
UInt16 diff = refSeqNum16 - newSeqNum;
|
|
if (diff <= kMaxMisorder) //new sequence number is behind and is in range, no underflow
|
|
return (refSeqNumHighBits << 16) | newSeqNum;
|
|
|
|
diff = kUInt16_Max - refSeqNum16;
|
|
diff += newSeqNum + 1;
|
|
if (diff <= kMaxDropOut) //new sequence number is ahead and is in range, overflow
|
|
return ((refSeqNumHighBits + 1) << 16) | newSeqNum;
|
|
}
|
|
return kUInt32_Max; //Bad sequence number
|
|
*/
|
|
}
|