Darwin-Streaming-Server/APIModules/QTSSReflectorModule/QTSSReflectorModule.cpp
Darren VanBuren 849723c9cf Add even more of the source
This should be about everything needed to build so far?
2017-03-07 17:14:16 -08:00

2368 lines
98 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: QTSSReflectorModule.cpp
Contains: Implementation of QTSSReflectorModule class.
*/
#include "QTSSReflectorModule.h"
#include "QTSSModuleUtils.h"
#include "ReflectorSession.h"
#include "OSArrayObjectDeleter.h"
#include "QTSS_Private.h"
#include "QTSSMemoryDeleter.h"
#include "OSMemory.h"
#include "OSRef.h"
#include "IdleTask.h"
#include "Task.h"
#include "OS.h"
#include "Socket.h"
#include "SocketUtils.h"
#include "FilePrefsSource.h"
#include "ResizeableStringFormatter.h"
#include "StringParser.h"
#include "QTAccessFile.h"
#include "QTSSModuleUtils.h"
#include "QTSS3GPPModuleUtils.h"
//ReflectorOutput objects
#include "RTPSessionOutput.h"
//SourceInfo objects
#include "SDPSourceInfo.h"
#include "SDPUtils.h"
#ifndef __Win32__
#include <unistd.h>
#endif
#if DEBUG
#define REFLECTOR_MODULE_DEBUGGING 0
#else
#define REFLECTOR_MODULE_DEBUGGING 0
#endif
// ATTRIBUTES
static QTSS_AttributeID sOutputAttr = qtssIllegalAttrID;
static QTSS_AttributeID sSessionAttr = qtssIllegalAttrID;
static QTSS_AttributeID sStreamCookieAttr = qtssIllegalAttrID;
static QTSS_AttributeID sRequestBodyAttr = qtssIllegalAttrID;
static QTSS_AttributeID sBufferOffsetAttr = qtssIllegalAttrID;
static QTSS_AttributeID sExpectedDigitFilenameErr = qtssIllegalAttrID;
static QTSS_AttributeID sReflectorBadTrackIDErr = qtssIllegalAttrID;
static QTSS_AttributeID sDuplicateBroadcastStreamErr= qtssIllegalAttrID;
static QTSS_AttributeID sClientBroadcastSessionAttr = qtssIllegalAttrID;
static QTSS_AttributeID sRTSPBroadcastSessionAttr = qtssIllegalAttrID;
static QTSS_AttributeID sAnnounceRequiresSDPinNameErr = qtssIllegalAttrID;
static QTSS_AttributeID sAnnounceDisabledNameErr = qtssIllegalAttrID;
static QTSS_AttributeID sSDPcontainsInvalidMinimumPortErr = qtssIllegalAttrID;
static QTSS_AttributeID sSDPcontainsInvalidMaximumPortErr = qtssIllegalAttrID;
static QTSS_AttributeID sStaticPortsConflictErr = qtssIllegalAttrID;
static QTSS_AttributeID sInvalidPortRangeErr = qtssIllegalAttrID;
static QTSS_AttributeID sKillClientsEnabledAttr = qtssIllegalAttrID;
static QTSS_AttributeID sRTPInfoWaitTimeAttr = qtssIllegalAttrID;
// STATIC DATA
// ref to the prefs dictionary object
static OSRefTable* sSessionMap = NULL;
static const StrPtrLen kCacheControlHeader("no-cache");
static QTSS_PrefsObject sServerPrefs = NULL;
static QTSS_ServerObject sServer = NULL;
static QTSS_ModulePrefsObject sPrefs = NULL;
//
// Prefs
static Bool16 sAllowNonSDPURLs = true;
static Bool16 sDefaultAllowNonSDPURLs = true;
static Bool16 sRTPInfoDisabled = false;
static Bool16 sDefaultRTPInfoDisabled = false;
static Bool16 sAnnounceEnabled = true;
static Bool16 sDefaultAnnounceEnabled = true;
static Bool16 sBroadcastPushEnabled = true;
static Bool16 sDefaultBroadcastPushEnabled = true;
static Bool16 sAllowDuplicateBroadcasts = false;
static Bool16 sDefaultAllowDuplicateBroadcasts = false;
static UInt32 sMaxBroadcastAnnounceDuration = 0;
static UInt32 sDefaultMaxBroadcastAnnounceDuration = 0;
static UInt16 sMinimumStaticSDPPort = 0;
static UInt16 sDefaultMinimumStaticSDPPort = 20000;
static UInt16 sMaximumStaticSDPPort = 0;
static UInt16 sDefaultMaximumStaticSDPPort = 65535;
static Bool16 sTearDownClientsOnDisconnect = false;
static Bool16 sDefaultTearDownClientsOnDisconnect = false;
static Bool16 sOneSSRCPerStream = true;
static Bool16 sDefaultOneSSRCPerStream = true;
static UInt32 sTimeoutSSRCSecs = 30;
static UInt32 sDefaultTimeoutSSRCSecs = 30;
static UInt32 sBroadcasterSessionTimeoutSecs = 20;
static UInt32 sDefaultBroadcasterSessionTimeoutSecs = 20;
static UInt32 sBroadcasterSessionTimeoutMilliSecs = sBroadcasterSessionTimeoutSecs * 1000;
static UInt16 sLastMax = 0;
static UInt16 sLastMin = 0;
static Bool16 sEnforceStaticSDPPortRange = false;
static Bool16 sDefaultEnforceStaticSDPPortRange = false;
static UInt32 sMaxAnnouncedSDPLengthInKbytes = 4;
//static UInt32 sDefaultMaxAnnouncedSDPLengthInKbytes = 4;
static QTSS_AttributeID sIPAllowListID = qtssIllegalAttrID;
static char* sIPAllowList = NULL;
static char* sLocalLoopBackAddress = "127.0.0.*";
static Bool16 sAuthenticateLocalBroadcast = false;
static Bool16 sDefaultAuthenticateLocalBroadcast = false;
static Bool16 sDisableOverbuffering = false;
static Bool16 sDefaultDisableOverbuffering = false;
static Bool16 sFalse = false;
static Bool16 sReflectBroadcasts = true;
static Bool16 sDefaultReflectBroadcasts = true;
static Bool16 sAnnouncedKill = true;
static Bool16 sDefaultAnnouncedKill = true;
static Bool16 sPlayResponseRangeHeader = true;
static Bool16 sDefaultPlayResponseRangeHeader = true;
static Bool16 sPlayerCompatibility = true;
static Bool16 sDefaultPlayerCompatibility = true;
static UInt32 sAdjustMediaBandwidthPercent = 100;
static UInt32 sAdjustMediaBandwidthPercentDefault = 100;
static Bool16 sForceRTPInfoSeqAndTime = false;
static Bool16 sDefaultForceRTPInfoSeqAndTime = false;
static char* sRedirectBroadcastsKeyword = NULL;
static char* sDefaultRedirectBroadcastsKeyword = "";
static char* sBroadcastsRedirectDir = NULL;
static char* sDefaultBroadcastsRedirectDir = ""; // match none
static char* sDefaultBroadcastsDir = ""; // match all
static char* sDefaultsBroadcasterGroup = "broadcaster";
static StrPtrLen sBroadcasterGroup;
static QTSS_AttributeID sBroadcastDirListID = qtssIllegalAttrID;
static SInt32 sWaitTimeLoopCount = 10;
// Important strings
static StrPtrLen sSDPKillSuffix(".kill");
static StrPtrLen sSDPSuffix(".sdp");
static StrPtrLen sMOVSuffix(".mov");
static StrPtrLen sSDPTooLongMessage("Announced SDP is too long");
static StrPtrLen sSDPNotValidMessage("Announced SDP is not a valid SDP");
static StrPtrLen sKILLNotValidMessage("Announced .kill is not a valid SDP");
static StrPtrLen sSDPTimeNotValidMessage("SDP time is not valid or movie not available at this time.");
static StrPtrLen sBroadcastNotAllowed("Broadcast is not allowed.");
static StrPtrLen sBroadcastNotActive("Broadcast is not active.");
static StrPtrLen sTheNowRangeHeader("npt=now-");
const int kBuffLen = 512;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSReflectorModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error DoAnnounce(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams);
ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams, StrPtrLen* inData = NULL,Bool16 isPush=false, Bool16 *foundSessionPtr = NULL);
static QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession);
static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
static void RemoveOutput(ReflectorOutput* inOutput, ReflectorSession* inSession, Bool16 killClients);
static ReflectorSession* DoSessionSetup(QTSS_StandardRTSP_Params* inParams, QTSS_AttributeID inPathType,Bool16 isPush=false,Bool16 *foundSessionPtr= NULL, char** resultFilePath = NULL);
static QTSS_Error RereadPrefs();
static QTSS_Error ProcessRTPData(QTSS_IncomingData_Params* inParams);
static QTSS_Error ReflectorAuthorizeRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static Bool16 InfoPortsOK(QTSS_StandardRTSP_Params* inParams, SDPSourceInfo* theInfo, StrPtrLen* inPath);
void KillCommandPathInList();
Bool16 KillSession(StrPtrLen *sdpPath, Bool16 killClients);
QTSS_Error IntervalRole();
static Bool16 AcceptSession(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error RedirectBroadcast(QTSS_StandardRTSP_Params* inParams);
static Bool16 AllowBroadcast(QTSS_RTSPRequestObject inRTSPRequest);
static Bool16 InBroadcastDirList(QTSS_RTSPRequestObject inRTSPRequest);
static Bool16 IsAbsolutePath(StrPtrLen *inPathPtr);
inline void KeepSession(QTSS_RTSPRequestObject theRequest,Bool16 keep)
{
(void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &keep, sizeof(keep));
}
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSReflectorModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSReflectorModuleDispatch);
}
QTSS_Error QTSSReflectorModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPRoute_Role:
return RedirectBroadcast(&inParams->rtspRouteParams);
case QTSS_RTSPPreProcessor_Role:
return ProcessRTSPRequest(&inParams->rtspRequestParams);
case QTSS_RTSPIncomingData_Role:
return ProcessRTPData(&inParams->rtspIncomingDataParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParams->clientSessionClosingParams);
case QTSS_Shutdown_Role:
return Shutdown();
case QTSS_RTSPAuthorize_Role:
return ReflectorAuthorizeRTSPRequest(&inParams->rtspRequestParams);
case QTSS_Interval_Role:
return IntervalRole();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_Shutdown_Role);
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
(void)QTSS_AddRole(QTSS_RTSPIncomingData_Role); // call me with interleaved RTP streams on the RTSP session
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPRoute_Role);
// Add text messages attributes
static char* sExpectedDigitFilenameName = "QTSSReflectorModuleExpectedDigitFilename";
static char* sReflectorBadTrackIDErrName = "QTSSReflectorModuleBadTrackID";
static char* sDuplicateBroadcastStreamName = "QTSSReflectorModuleDuplicateBroadcastStream";
static char* sAnnounceRequiresSDPinName = "QTSSReflectorModuleAnnounceRequiresSDPSuffix";
static char* sAnnounceDisabledName = "QTSSReflectorModuleAnnounceDisabled";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sDuplicateBroadcastStreamName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sDuplicateBroadcastStreamName, &sDuplicateBroadcastStreamErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sAnnounceRequiresSDPinName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sAnnounceRequiresSDPinName, &sAnnounceRequiresSDPinNameErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sAnnounceDisabledName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sAnnounceDisabledName, &sAnnounceDisabledNameErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sExpectedDigitFilenameName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sExpectedDigitFilenameName, &sExpectedDigitFilenameErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sReflectorBadTrackIDErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sReflectorBadTrackIDErrName, &sReflectorBadTrackIDErr);
static char* sSDPcontainsInvalidMinumumPortErrName = "QTSSReflectorModuleSDPPortMinimumPort";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sSDPcontainsInvalidMinumumPortErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sSDPcontainsInvalidMinumumPortErrName, &sSDPcontainsInvalidMinimumPortErr);
static char* sSDPcontainsInvalidMaximumPortErrName = "QTSSReflectorModuleSDPPortMaximumPort";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sSDPcontainsInvalidMaximumPortErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sSDPcontainsInvalidMaximumPortErrName, &sSDPcontainsInvalidMaximumPortErr);
static char* sStaticPortsConflictErrName = "QTSSReflectorModuleStaticPortsConflict";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sStaticPortsConflictErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sStaticPortsConflictErrName, &sStaticPortsConflictErr);
static char* sInvalidPortRangeErrName = "QTSSReflectorModuleStaticPortPrefsBadRange";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sInvalidPortRangeErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sInvalidPortRangeErrName, &sInvalidPortRangeErr);
// Add an RTP session attribute for tracking ReflectorSession objects
static char* sOutputName = "QTSSReflectorModuleOutput";
static char* sSessionName = "QTSSReflectorModuleSession";
static char* sStreamCookieName = "QTSSReflectorModuleStreamCookie";
static char* sRequestBufferName = "QTSSReflectorModuleRequestBuffer";
static char* sRequestBufferLenName= "QTSSReflectorModuleRequestBufferLen";
static char* sBroadcasterSessionName= "QTSSReflectorModuleBroadcasterSession";
static char* sKillClientsEnabledName= "QTSSReflectorModuleTearDownClients";
static char* sRTPInfoWaitTime = "QTSSReflectorModuleRTPInfoWaitTime";
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sRTPInfoWaitTime, NULL, qtssAttrDataTypeSInt32);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sRTPInfoWaitTime, &sRTPInfoWaitTimeAttr);
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sOutputName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sOutputName, &sOutputAttr);
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sSessionName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sSessionName, &sSessionAttr);
(void)QTSS_AddStaticAttribute(qtssRTPStreamObjectType, sStreamCookieName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTPStreamObjectType, sStreamCookieName, &sStreamCookieAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sRequestBufferName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sRequestBufferName, &sRequestBodyAttr);
(void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sRequestBufferLenName, NULL, qtssAttrDataTypeUInt32);
(void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sRequestBufferLenName, &sBufferOffsetAttr);
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sBroadcasterSessionName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sBroadcasterSessionName, &sClientBroadcastSessionAttr);
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sKillClientsEnabledName, NULL, qtssAttrDataTypeBool16);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sKillClientsEnabledName, &sKillClientsEnabledAttr);
// keep the same attribute name for the RTSPSessionObject as used int he ClientSessionObject
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sBroadcasterSessionName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sBroadcasterSessionName, &sRTSPBroadcastSessionAttr);
// Reflector session needs to setup some parameters too.
ReflectorStream::Register();
// RTPSessionOutput needs to do the same
RTPSessionOutput::Register();
// Tell the server our name!
static char* sModuleName = "QTSSReflectorModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
QTSS3GPPModuleUtils::Initialize(inParams);
QTAccessFile::Initialize();
sSessionMap = NEW OSRefTable();
sServerPrefs = inParams->inPrefs;
sServer = inParams->inServer;
#if QTSS_REFLECTOR_EXTERNAL_MODULE
// The reflector is dependent on a number of objects in the Common Utilities
// library that get setup by the server if the reflector is internal to the
// server.
//
// So, if the reflector is being built as a code fragment, it must initialize
// those pieces itself
#if !MACOSXEVENTQUEUE
::select_startevents();//initialize the select() implementation of the event queue
#endif
OS::Initialize();
Socket::Initialize();
SocketUtils::Initialize();
const UInt32 kNumReflectorThreads = 8;
TaskThreadPool::AddThreads(kNumReflectorThreads);
IdleTask::Initialize();
Socket::StartThread();
#endif
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
// Call helper class initializers
ReflectorStream::Initialize(sPrefs);
ReflectorSession::Initialize();
// Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod, qtssAnnounceMethod, qtssRecordMethod };
QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 7);
RereadPrefs();
return QTSS_NoErr;
}
char *GetTrimmedKeyWord(char *prefKeyWord)
{
StrPtrLen redirKeyWordStr(prefKeyWord);
StringParser theRequestPathParser(&redirKeyWordStr);
// trim leading / from the keyword
while(theRequestPathParser.Expect(kPathDelimiterChar)) {};
StrPtrLen theKeyWordStr;
theRequestPathParser.ConsumeUntil(&theKeyWordStr, kPathDelimiterChar); // stop when we see a / and don't include
char *keyword = NEW char[theKeyWordStr.Len +1];
::memcpy(keyword, theKeyWordStr.Ptr, theKeyWordStr.Len);
keyword[theKeyWordStr.Len] = 0;
return keyword;
}
void SetMoviesRelativeDir()
{
char* movieFolderString = NULL;
(void) QTSS_GetValueAsString (sServerPrefs, qtssPrefsMovieFolder, 0, &movieFolderString);
OSCharArrayDeleter deleter(movieFolderString);
ResizeableStringFormatter redirectPath(NULL,0);
redirectPath.Put(movieFolderString);
if (redirectPath.GetBytesWritten() > 0 && kPathDelimiterChar != redirectPath.GetBufPtr()[redirectPath.GetBytesWritten() -1])
redirectPath.PutChar(kPathDelimiterChar);
redirectPath.Put(sBroadcastsRedirectDir);
char *newMovieRelativeDir = NEW char[redirectPath.GetBytesWritten() +1];
::memcpy(newMovieRelativeDir, redirectPath.GetBufPtr(), redirectPath.GetBytesWritten());
newMovieRelativeDir[redirectPath.GetBytesWritten()] = 0;
delete [] sBroadcastsRedirectDir;
sBroadcastsRedirectDir = newMovieRelativeDir;
}
QTSS_Error RereadPrefs()
{
//
// Use the standard GetPref routine to retrieve the correct values for our preferences
QTSSModuleUtils::GetAttribute(sPrefs, "disable_rtp_play_info", qtssAttrDataTypeBool16,
&sRTPInfoDisabled, &sDefaultRTPInfoDisabled, sizeof(sDefaultRTPInfoDisabled));
QTSSModuleUtils::GetAttribute(sPrefs, "allow_non_sdp_urls", qtssAttrDataTypeBool16,
&sAllowNonSDPURLs, &sDefaultAllowNonSDPURLs, sizeof(sDefaultAllowNonSDPURLs));
QTSSModuleUtils::GetAttribute(sPrefs, "enable_broadcast_announce", qtssAttrDataTypeBool16,
&sAnnounceEnabled, &sDefaultAnnounceEnabled, sizeof(sDefaultAnnounceEnabled));
QTSSModuleUtils::GetAttribute(sPrefs, "enable_broadcast_push", qtssAttrDataTypeBool16,
&sBroadcastPushEnabled, &sDefaultBroadcastPushEnabled, sizeof(sDefaultBroadcastPushEnabled));
QTSSModuleUtils::GetAttribute(sPrefs, "max_broadcast_announce_duration_secs", qtssAttrDataTypeUInt32,
&sMaxBroadcastAnnounceDuration, &sDefaultMaxBroadcastAnnounceDuration, sizeof(sDefaultMaxBroadcastAnnounceDuration));
QTSSModuleUtils::GetAttribute(sPrefs, "allow_duplicate_broadcasts", qtssAttrDataTypeBool16,
&sAllowDuplicateBroadcasts, &sDefaultAllowDuplicateBroadcasts, sizeof(sDefaultAllowDuplicateBroadcasts));
QTSSModuleUtils::GetAttribute(sPrefs, "enforce_static_sdp_port_range", qtssAttrDataTypeBool16,
&sEnforceStaticSDPPortRange, &sDefaultEnforceStaticSDPPortRange, sizeof(sDefaultEnforceStaticSDPPortRange));
QTSSModuleUtils::GetAttribute(sPrefs, "minimum_static_sdp_port", qtssAttrDataTypeUInt16,
&sMinimumStaticSDPPort, &sDefaultMinimumStaticSDPPort, sizeof(sDefaultMinimumStaticSDPPort));
QTSSModuleUtils::GetAttribute(sPrefs, "maximum_static_sdp_port", qtssAttrDataTypeUInt16,
&sMaximumStaticSDPPort, &sDefaultMaximumStaticSDPPort, sizeof(sDefaultMaximumStaticSDPPort));
QTSSModuleUtils::GetAttribute(sPrefs, "kill_clients_when_broadcast_stops", qtssAttrDataTypeBool16,
&sTearDownClientsOnDisconnect, &sDefaultTearDownClientsOnDisconnect, sizeof(sDefaultTearDownClientsOnDisconnect));
QTSSModuleUtils::GetAttribute(sPrefs, "use_one_SSRC_per_stream", qtssAttrDataTypeBool16,
&sOneSSRCPerStream, &sDefaultOneSSRCPerStream, sizeof(sDefaultOneSSRCPerStream));
QTSSModuleUtils::GetAttribute(sPrefs, "timeout_stream_SSRC_secs", qtssAttrDataTypeUInt32,
&sTimeoutSSRCSecs, &sDefaultTimeoutSSRCSecs, sizeof(sDefaultTimeoutSSRCSecs));
QTSSModuleUtils::GetAttribute(sPrefs, "timeout_broadcaster_session_secs", qtssAttrDataTypeUInt32,
&sBroadcasterSessionTimeoutSecs, &sDefaultBroadcasterSessionTimeoutSecs, sizeof(sDefaultTimeoutSSRCSecs));
QTSSModuleUtils::GetAttribute(sPrefs, "authenticate_local_broadcast", qtssAttrDataTypeBool16,
&sAuthenticateLocalBroadcast, &sDefaultAuthenticateLocalBroadcast, sizeof(sDefaultAuthenticateLocalBroadcast));
QTSSModuleUtils::GetAttribute(sPrefs, "disable_overbuffering", qtssAttrDataTypeBool16,
&sDisableOverbuffering, &sDefaultDisableOverbuffering, sizeof(sDefaultDisableOverbuffering));
QTSSModuleUtils::GetAttribute(sPrefs, "allow_broadcasts", qtssAttrDataTypeBool16,
&sReflectBroadcasts, &sDefaultReflectBroadcasts, sizeof(sDefaultReflectBroadcasts));
QTSSModuleUtils::GetAttribute(sPrefs, "allow_announced_kill", qtssAttrDataTypeBool16,
&sAnnouncedKill, &sDefaultAnnouncedKill, sizeof(sDefaultAnnouncedKill));
QTSSModuleUtils::GetAttribute(sPrefs, "enable_play_response_range_header", qtssAttrDataTypeBool16,
&sPlayResponseRangeHeader, &sDefaultPlayResponseRangeHeader, sizeof(sDefaultPlayResponseRangeHeader));
QTSSModuleUtils::GetAttribute(sPrefs, "enable_player_compatibility", qtssAttrDataTypeBool16,
&sPlayerCompatibility, &sDefaultPlayerCompatibility, sizeof(sDefaultPlayerCompatibility));
QTSSModuleUtils::GetAttribute(sPrefs, "compatibility_adjust_sdp_media_bandwidth_percent", qtssAttrDataTypeUInt32,
&sAdjustMediaBandwidthPercent, &sAdjustMediaBandwidthPercentDefault, sizeof(sAdjustMediaBandwidthPercentDefault));
if (sAdjustMediaBandwidthPercent > 100)
sAdjustMediaBandwidthPercent = 100;
if (sAdjustMediaBandwidthPercent < 1)
sAdjustMediaBandwidthPercent = 1;
QTSSModuleUtils::GetAttribute(sPrefs, "force_rtp_info_sequence_and_time", qtssAttrDataTypeBool16,
&sForceRTPInfoSeqAndTime, &sDefaultForceRTPInfoSeqAndTime, sizeof(sDefaultForceRTPInfoSeqAndTime));
sBroadcasterGroup.Delete();
sBroadcasterGroup.Set(QTSSModuleUtils::GetStringAttribute(sPrefs, "BroadcasterGroup", sDefaultsBroadcasterGroup));
delete [] sRedirectBroadcastsKeyword;
char* tempKeyWord = QTSSModuleUtils::GetStringAttribute(sPrefs, "redirect_broadcast_keyword", sDefaultRedirectBroadcastsKeyword);
sRedirectBroadcastsKeyword = GetTrimmedKeyWord(tempKeyWord);
delete [] tempKeyWord;
delete [] sBroadcastsRedirectDir;
sBroadcastsRedirectDir = QTSSModuleUtils::GetStringAttribute(sPrefs, "redirect_broadcasts_dir", sDefaultBroadcastsRedirectDir);
if (sBroadcastsRedirectDir && sBroadcastsRedirectDir[0] != kPathDelimiterChar)
SetMoviesRelativeDir();
delete [] QTSSModuleUtils::GetStringAttribute(sPrefs, "broadcast_dir_list", sDefaultBroadcastsDir); // initialize if there isn't one
sBroadcastDirListID = QTSSModuleUtils::GetAttrID(sPrefs, "broadcast_dir_list");
delete [] sIPAllowList;
sIPAllowList = QTSSModuleUtils::GetStringAttribute(sPrefs, "ip_allow_list", sLocalLoopBackAddress);
sIPAllowListID = QTSSModuleUtils::GetAttrID(sPrefs, "ip_allow_list");
sBroadcasterSessionTimeoutMilliSecs = sBroadcasterSessionTimeoutSecs * 1000;
if (sEnforceStaticSDPPortRange)
{ Bool16 reportErrors = false;
if (sLastMax != sMaximumStaticSDPPort)
{ sLastMax = sMaximumStaticSDPPort;
reportErrors = true;
}
if (sLastMin != sMinimumStaticSDPPort)
{ sLastMin = sMinimumStaticSDPPort;
reportErrors = true;
}
if (reportErrors)
{
UInt16 minServerPort = 6970;
UInt16 maxServerPort = 9999;
char min[32];
char max[32];
if ( ( (sMinimumStaticSDPPort <= minServerPort) && (sMaximumStaticSDPPort >= minServerPort) )
|| ( (sMinimumStaticSDPPort >= minServerPort) && (sMinimumStaticSDPPort <= maxServerPort) )
)
{
qtss_sprintf(min,"%u",minServerPort);
qtss_sprintf(max,"%u",maxServerPort);
QTSSModuleUtils::LogError( qtssWarningVerbosity, sStaticPortsConflictErr, 0, min, max);
}
if (sMinimumStaticSDPPort > sMaximumStaticSDPPort)
{
qtss_sprintf(min,"%u",sMinimumStaticSDPPort);
qtss_sprintf(max,"%u",sMaximumStaticSDPPort);
QTSSModuleUtils::LogError( qtssWarningVerbosity, sInvalidPortRangeErr, 0, min, max);
}
}
}
KillCommandPathInList();
QTSS3GPPModuleUtils::ReadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
#if QTSS_REFLECTOR_EXTERNAL_MODULE
TaskThreadPool::RemoveThreads();
#endif
return QTSS_NoErr;
}
QTSS_Error IntervalRole() // not used
{
(void) QTSS_SetIntervalRoleTimer(0); // turn off
return QTSS_NoErr;
}
QTSS_Error ProcessRTPData(QTSS_IncomingData_Params* inParams)
{
if (!sBroadcastPushEnabled)
return QTSS_NoErr;
//qtss_printf("QTSSReflectorModule:ProcessRTPData inRTSPSession=%"_U32BITARG_" inClientSession=%"_U32BITARG_"\n",inParams->inRTSPSession, inParams->inClientSession);
ReflectorSession* theSession = NULL;
UInt32 theLen = sizeof(theSession);
QTSS_Error theErr = QTSS_GetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &theSession, &theLen);
//qtss_printf("QTSSReflectorModule.cpp:ProcessRTPData sClientBroadcastSessionAttr=%"_U32BITARG_" theSession=%"_U32BITARG_" err=%"_S32BITARG_" \n",sClientBroadcastSessionAttr, theSession,theErr);
if (theSession == NULL || theErr != QTSS_NoErr)
return QTSS_NoErr;
// it is a broadcaster session
//qtss_printf("QTSSReflectorModule.cpp:is broadcaster session\n");
SourceInfo* theSoureInfo = theSession->GetSourceInfo();
Assert(theSoureInfo != NULL);
if (theSoureInfo == NULL)
return QTSS_NoErr;
UInt32 numStreams = theSession->GetNumStreams();
//qtss_printf("QTSSReflectorModule.cpp:ProcessRTPData numStreams=%"_U32BITARG_"\n",numStreams);
{
/*
Stream data such as RTP packets is encapsulated by an ASCII dollar
sign (24 hexadecimal), followed by a one-byte channel identifier,
followed by the length of the encapsulated binary data as a binary,
two-byte integer in network byte order. The stream data follows
immediately afterwards, without a CRLF, but including the upper-layer
protocol headers. Each $ block contains exactly one upper-layer
protocol data unit, e.g., one RTP packet.
*/
char* packetData= inParams->inPacketData;
UInt8 packetChannel;
packetChannel = (UInt8) packetData[1];
UInt16 packetDataLen;
memcpy(&packetDataLen,&packetData[2],2);
packetDataLen = ntohs(packetDataLen);
char* rtpPacket = &packetData[4];
//UInt32 packetLen = inParams->inPacketLen;
//qtss_printf("QTSSReflectorModule.cpp:ProcessRTPData channel=%u theSoureInfo=%"_U32BITARG_" packetLen=%"_U32BITARG_" packetDatalen=%u\n",(UInt16) packetChannel,theSoureInfo,inParams->inPacketLen,packetDataLen);
if (1)
{
UInt32 inIndex = packetChannel / 2; // one stream per every 2 channels rtcp channel handled below
ReflectorStream* theStream = NULL;
if (inIndex < numStreams)
{ theStream = theSession->GetStreamByIndex(inIndex);
SourceInfo::StreamInfo* theStreamInfo =theStream->GetStreamInfo();
UInt16 serverReceivePort =theStreamInfo->fPort;
Bool16 isRTCP =false;
if (theStream != NULL)
{ if (packetChannel & 1)
{ serverReceivePort ++;
isRTCP = true;
}
theStream->PushPacket(rtpPacket,packetDataLen, isRTCP);
//qtss_printf("QTSSReflectorModule.cpp:ProcessRTPData Send RTSP packet channel=%u to UDP localServerAddr=%"_U32BITARG_" serverReceivePort=%"_U32BITARG_" packetDataLen=%u \n", (UInt16) packetChannel, localServerAddr, serverReceivePort,packetDataLen);
}
}
}
}
return theErr;
}
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
OSMutexLocker locker (sSessionMap->GetMutex()); //operating on sOutputAttr
QTSS_RTSPMethod* theMethod = NULL;
//qtss_printf("QTSSReflectorModule:ProcessRTSPRequest inClientSession=%"_U32BITARG_"\n", (UInt32) inParams->inClientSession);
UInt32 theLen = 0;
if ((QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theLen) != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
{
Assert(0);
return QTSS_RequestFailed;
}
if (*theMethod == qtssAnnounceMethod)
return DoAnnounce(inParams);
if (*theMethod == qtssDescribeMethod)
return DoDescribe(inParams);
if (*theMethod == qtssSetupMethod)
return DoSetup(inParams);
RTPSessionOutput** theOutput = NULL;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*))) // a broadcaster push session
{ if (*theMethod == qtssPlayMethod || *theMethod == qtssRecordMethod)
return DoPlay(inParams, NULL);
else
return QTSS_RequestFailed;
}
switch (*theMethod)
{
case qtssPlayMethod:
return DoPlay(inParams, (*theOutput)->GetReflectorSession());
case qtssTeardownMethod:
// Tell the server that this session should be killed, and send a TEARDOWN response
(void)QTSS_Teardown(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
case qtssPauseMethod:
(void)QTSS_Pause(inParams->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
break;
default:
break;
}
return QTSS_NoErr;
}
ReflectorSession* DoSessionSetup(QTSS_StandardRTSP_Params* inParams, QTSS_AttributeID inPathType,Bool16 isPush, Bool16 *foundSessionPtr, char** resultFilePath)
{
char* theFullPathStr = NULL;
QTSS_Error theErr = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
Assert(theErr == QTSS_NoErr);
QTSSCharArrayDeleter theFullPathStrDeleter(theFullPathStr);
if (theErr != QTSS_NoErr)
return NULL;
StrPtrLen theFullPath(theFullPathStr);
if (theFullPath.Len > sMOVSuffix.Len )
{ StrPtrLen endOfPath2(&theFullPath.Ptr[theFullPath.Len - sMOVSuffix.Len], sMOVSuffix.Len);
if (endOfPath2.Equal(sMOVSuffix)) // it is a .mov so it is not meant for us
{
return NULL;
}
}
if (sAllowNonSDPURLs && !isPush)
{
// Check and see if the full path to this file matches an existing ReflectorSession
StrPtrLen thePathPtr;
OSCharArrayDeleter sdpPath(QTSSModuleUtils::GetFullPath( inParams->inRTSPRequest,
inPathType,
&thePathPtr.Len, &sSDPSuffix));
thePathPtr.Ptr = sdpPath.GetObject();
// If the actual file path has a .sdp in it, first look for the URL without the extra .sdp
if (thePathPtr.Len > (sSDPSuffix.Len * 2))
{
// Check and see if there is a .sdp in the file path.
// If there is, truncate off our extra ".sdp", cuz it isn't needed
StrPtrLen endOfPath(&sdpPath.GetObject()[thePathPtr.Len - (sSDPSuffix.Len * 2)], sSDPSuffix.Len);
if (endOfPath.Equal(sSDPSuffix))
{
sdpPath.GetObject()[thePathPtr.Len - sSDPSuffix.Len] = '\0';
thePathPtr.Len -= sSDPSuffix.Len;
}
}
if (resultFilePath != NULL)
*resultFilePath = thePathPtr.GetAsCString();
return FindOrCreateSession(&thePathPtr, inParams);
}
else
{
if (!sDefaultBroadcastPushEnabled)
return NULL;
//
// We aren't supposed to auto-append a .sdp, so just get the URL path out of the server
//StrPtrLen theFullPath;
//QTSS_Error theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqLocalPath, 0, (void**)&theFullPath.Ptr, &theFullPath.Len);
//Assert(theErr == QTSS_NoErr);
if (theFullPath.Len > sSDPSuffix.Len)
{
//
// Check to make sure this path has a .sdp at the end. If it does,
// attempt to get a reflector session for this URL.
StrPtrLen endOfPath2(&theFullPath.Ptr[theFullPath.Len - sSDPSuffix.Len], sSDPSuffix.Len);
if (endOfPath2.Equal(sSDPSuffix))
{ if (resultFilePath != NULL)
*resultFilePath = theFullPath.GetAsCString();
return FindOrCreateSession(&theFullPath, inParams,NULL, isPush,foundSessionPtr);
}
}
return NULL;
}
return NULL;
}
void DoAnnounceAddRequiredSDPLines(QTSS_StandardRTSP_Params* inParams, ResizeableStringFormatter *editedSDP, char* theSDPPtr)
{
SDPContainer checkedSDPContainer;
checkedSDPContainer.SetSDPBuffer( theSDPPtr );
if (!checkedSDPContainer.HasReqLines())
{
if (!checkedSDPContainer.HasLineType('v'))
{ // add v line
editedSDP->Put("v=0\r\n");
}
if (!checkedSDPContainer.HasLineType('s'))
{ // add s line
char* theSDPName = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFilePath, 0, &theSDPName);
QTSSCharArrayDeleter thePathStrDeleter(theSDPName);
if (theSDPName == NULL)
editedSDP->Put("s=unknown\r\n");
else
{
editedSDP->Put("s=");
editedSDP->Put(theSDPName);
editedSDP->PutEOL();
}
}
if (!checkedSDPContainer.HasLineType('t'))
{ // add t line
editedSDP->Put("t=0 0\r\n");
}
if (!checkedSDPContainer.HasLineType('o'))
{ // add o line
editedSDP->Put("o=");
char tempBuff[256] = ""; tempBuff[255] = 0;
char *nameStr = tempBuff;
UInt32 buffLen = sizeof(tempBuff) - 1;
(void)QTSS_GetValue(inParams->inClientSession, qtssCliSesFirstUserAgent, 0, nameStr, &buffLen);
for (UInt32 c = 0; c < buffLen; c++)
{
if (StringParser::sEOLWhitespaceMask[ (UInt8) nameStr[c]])
{ nameStr[c] = 0;
break;
}
}
buffLen = ::strlen(nameStr);
if (buffLen == 0)
editedSDP->Put("announced_broadcast");
else
editedSDP->Put(nameStr, buffLen);
editedSDP->Put(" ");
buffLen = sizeof(tempBuff) -1;
(void)QTSS_GetValue(inParams->inClientSession, qtssCliSesRTSPSessionID, 0, &tempBuff, &buffLen);
editedSDP->Put(tempBuff, buffLen );
editedSDP->Put(" ");
qtss_snprintf(tempBuff, sizeof(tempBuff) -1, "%"_64BITARG_"d", (SInt64) OS::UnixTime_Secs() + 2208988800LU);
editedSDP->Put(tempBuff);
editedSDP->Put(" IN IP4 ");
(void)QTSS_GetValue(inParams->inClientSession, qtssCliRTSPSessRemoteAddrStr, 0, tempBuff, &buffLen);
editedSDP->Put(tempBuff, buffLen);
editedSDP->PutEOL();
}
}
editedSDP->Put(theSDPPtr);
}
QTSS_Error DoAnnounce(QTSS_StandardRTSP_Params* inParams)
{
if (!sAnnounceEnabled)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssPreconditionFailed, sAnnounceDisabledNameErr);
//
// If this is SDP data, the reflector has the ability to write the data
// to the file system location specified by the URL.
//
// This is a completely stateless action. No ReflectorSession gets created (obviously).
//
// Eventually, we should really require access control before we do this.
//qtss_printf("QTSSReflectorModule:DoAnnounce\n");
//
// Get the full path to this file
char* theFullPathStr = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFullPathStr);
QTSSCharArrayDeleter theFullPathStrDeleter(theFullPathStr);
StrPtrLen theFullPath(theFullPathStr);
// Check for a .kill at the end
Bool16 pathOK = false;
Bool16 killBroadcast = false;
if (sAnnouncedKill && theFullPath.Len > sSDPKillSuffix.Len)
{
StrPtrLen endOfPath(theFullPath.Ptr + (theFullPath.Len - sSDPKillSuffix.Len), sSDPKillSuffix.Len);
if (endOfPath.Equal(sSDPKillSuffix))
{
pathOK = true;
killBroadcast = true;
}
}
// Check for a .sdp at the end
if (!pathOK)
{
if (theFullPath.Len <= sSDPSuffix.Len)
{
StrPtrLen endOfPath(theFullPath.Ptr + (theFullPath.Len - sSDPSuffix.Len), sSDPSuffix.Len);
if (!endOfPath.Equal(sSDPSuffix))
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssPreconditionFailed, sAnnounceRequiresSDPinNameErr);
}
}
//
// Ok, this is an sdp file. Retreive the entire contents of the SDP.
// This has to be done asynchronously (in case the SDP stuff is fragmented across
// multiple packets. So, we have to have a simple state machine.
//
// We need to know the content length to manage memory
UInt32 theLen = 0;
UInt32* theContentLenP = NULL;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqContentLen, 0, (void**)&theContentLenP, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
{
//
// RETURN ERROR RESPONSE: ANNOUNCE WITHOUT CONTENT LENGTH
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,0);
}
// Check if the content-length is more than the imposed maximum
// if it is then return error response
if ( (sMaxAnnouncedSDPLengthInKbytes != 0) && (*theContentLenP > (sMaxAnnouncedSDPLengthInKbytes * 1024)) )
return QTSSModuleUtils::SendErrorResponseWithMessage( inParams->inRTSPRequest, qtssPreconditionFailed, &sSDPTooLongMessage );
//
// Check for the existence of 2 attributes in the request: a pointer to our buffer for
// the request body, and the current offset in that buffer. If these attributes exist,
// then we've already been here for this request. If they don't exist, add them.
UInt32 theBufferOffset = 0;
char* theRequestBody = NULL;
theLen = sizeof(theRequestBody);
theErr = QTSS_GetValue(inParams->inRTSPRequest, sRequestBodyAttr, 0, &theRequestBody, &theLen);
//qtss_printf("QTSSReflectorModule:DoAnnounce theRequestBody =%s\n",theRequestBody);
if (theErr != QTSS_NoErr)
{
//
// First time we've been here for this request. Create a buffer for the content body and
// shove it in the request.
theRequestBody = NEW char[*theContentLenP + 1];
memset(theRequestBody,0,*theContentLenP + 1);
theLen = sizeof(theRequestBody);
theErr = QTSS_SetValue(inParams->inRTSPRequest, sRequestBodyAttr, 0, &theRequestBody, theLen);// SetValue creates an internal copy.
Assert(theErr == QTSS_NoErr);
//
// Also store the offset in the buffer
theLen = sizeof(theBufferOffset);
theErr = QTSS_SetValue(inParams->inRTSPRequest, sBufferOffsetAttr, 0, &theBufferOffset, theLen);
Assert(theErr == QTSS_NoErr);
}
theLen = sizeof(theBufferOffset);
theErr = QTSS_GetValue(inParams->inRTSPRequest, sBufferOffsetAttr, 0, &theBufferOffset, &theLen);
//
// We have our buffer and offset. Read the data.
theErr = QTSS_Read(inParams->inRTSPRequest, theRequestBody + theBufferOffset, *theContentLenP - theBufferOffset, &theLen);
Assert(theErr != QTSS_BadArgument);
if (theErr == QTSS_RequestFailed)
{
OSCharArrayDeleter charArrayPathDeleter(theRequestBody);
//
// NEED TO RETURN RTSP ERROR RESPONSE
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,0);
}
if ((theErr == QTSS_WouldBlock) || (theLen < (*theContentLenP - theBufferOffset)))
{
//
// Update our offset in the buffer
theBufferOffset += theLen;
(void)QTSS_SetValue(inParams->inRTSPRequest, sBufferOffsetAttr, 0, &theBufferOffset, sizeof(theBufferOffset));
//qtss_printf("QTSSReflectorModule:DoAnnounce Request some more data \n");
//
// The entire content body hasn't arrived yet. Request a read event and wait for it.
// Our DoAnnounce function will get called again when there is more data.
theErr = QTSS_RequestEvent(inParams->inRTSPRequest, QTSS_ReadableEvent);
Assert(theErr == QTSS_NoErr);
return QTSS_NoErr;
}
Assert(theErr == QTSS_NoErr);
//
// If we've gotten here, we have the entire content body in our buffer.
//
if (killBroadcast)
{
theFullPath.Len -= sSDPKillSuffix.Len;
if (KillSession(&theFullPath, killBroadcast))
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerInternal,0);
else
return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssClientNotFound, &sKILLNotValidMessage);
}
// ------------ Clean up missing required SDP lines
ResizeableStringFormatter editedSDP(NULL,0);
DoAnnounceAddRequiredSDPLines(inParams, &editedSDP, theRequestBody);
StrPtrLen editedSDPSPL(editedSDP.GetBufPtr(),editedSDP.GetBytesWritten());
// ------------ Check the headers
SDPContainer checkedSDPContainer;
checkedSDPContainer.SetSDPBuffer( &editedSDPSPL );
if (!checkedSDPContainer.IsSDPBufferValid())
{
return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssUnsupportedMediaType, &sSDPNotValidMessage);
}
SDPSourceInfo theSDPSourceInfo(editedSDPSPL.Ptr, editedSDPSPL.Len );
OSCharArrayDeleter charArrayPathDeleter(theRequestBody);
if (!InfoPortsOK(inParams,&theSDPSourceInfo,&theFullPath)) // All validity checks like this check should be done before touching the file.
{ return QTSS_NoErr; // InfoPortsOK is sending back the error.
}
// ------------ reorder the sdp headers to make them proper.
SDPLineSorter sortedSDP(&checkedSDPContainer );
// ------------ Write the SDP
char* sessionHeaders = sortedSDP.GetSessionHeaders()->GetAsCString();
OSCharArrayDeleter sessionHeadersDeleter(sessionHeaders);
char* mediaHeaders = sortedSDP.GetMediaHeaders()->GetAsCString();
OSCharArrayDeleter mediaHeadersDeleter(mediaHeaders);
// sortedSDP.GetSessionHeaders()->PrintStrEOL();
// sortedSDP.GetMediaHeaders()->PrintStrEOL();
// write the file !! need error reporting
FILE* theSDPFile= ::fopen(theFullPath.Ptr, "wb");//open
if (theSDPFile != NULL)
{
qtss_fprintf(theSDPFile, "%s", sessionHeaders);
qtss_fprintf(theSDPFile, "%s", mediaHeaders);
::fflush(theSDPFile);
::fclose(theSDPFile);
}
else
{ return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientForbidden,0);
}
//qtss_printf("QTSSReflectorModule:DoAnnounce SendResponse OK=200\n");
return QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, 0);
}
void DoDescribeAddRequiredSDPLines(QTSS_StandardRTSP_Params* inParams, ReflectorSession* theSession, QTSS_TimeVal modDate, ResizeableStringFormatter *editedSDP, StrPtrLen* theSDPPtr)
{
SDPContainer checkedSDPContainer;
checkedSDPContainer.SetSDPBuffer( theSDPPtr );
if (!checkedSDPContainer.HasReqLines())
{
if (!checkedSDPContainer.HasLineType('v'))
{ // add v line
editedSDP->Put("v=0\r\n");
}
if (!checkedSDPContainer.HasLineType('s'))
{ // add s line
char* theSDPName = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFilePath, 0, &theSDPName);
QTSSCharArrayDeleter thePathStrDeleter(theSDPName);
editedSDP->Put("s=");
editedSDP->Put(theSDPName);
editedSDP->PutEOL();
}
if (!checkedSDPContainer.HasLineType('t'))
{ // add t line
editedSDP->Put("t=0 0\r\n");
}
if (!checkedSDPContainer.HasLineType('o'))
{ // add o line
editedSDP->Put("o=broadcast_sdp ");
char tempBuff[256]= "";
tempBuff[255] = 0;
qtss_snprintf(tempBuff,sizeof(tempBuff) - 1, "%"_U32BITARG_"", *(UInt32 *) &theSession);
editedSDP->Put(tempBuff);
editedSDP->Put(" ");
// modified date is in milliseconds. Convert to NTP seconds as recommended by rfc 2327
qtss_snprintf(tempBuff, sizeof(tempBuff) - 1, "%"_64BITARG_"d", (SInt64) (modDate/1000) + 2208988800LU);
editedSDP->Put(tempBuff);
editedSDP->Put(" IN IP4 ");
UInt32 buffLen = sizeof(tempBuff) -1;
(void)QTSS_GetValue(inParams->inClientSession, qtssCliSesHostName, 0, &tempBuff, &buffLen);
editedSDP->Put(tempBuff, buffLen);
editedSDP->PutEOL();
}
}
editedSDP->Put(*theSDPPtr);
}
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams)
{
char *theFilepath = NULL;
ReflectorSession* theSession = DoSessionSetup(inParams, qtssRTSPReqFilePath, false, NULL, &theFilepath );
OSCharArrayDeleter tempFilePath(theFilepath);
if (theSession == NULL)
return QTSS_RequestFailed;
RTPSessionOutput** theOutput = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
// If there already was an RTPSessionOutput attached to this Client Session,
// destroy it.
if (theErr == QTSS_NoErr && theOutput != NULL)
{ RemoveOutput(*theOutput, (*theOutput)->GetReflectorSession(), false);
RTPSessionOutput* theOutput = NULL;
(void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theOutput, sizeof(theOutput));
}
// send the DESCRIBE response
//above function has signalled that this request belongs to us, so let's respond
iovec theDescribeVec[3] = { {0 }};
Assert(theSession->GetLocalSDP()->Ptr != NULL);
StrPtrLen theFileData;
QTSS_TimeVal outModDate = 0;
QTSS_TimeVal inModDate = -1;
(void)QTSSModuleUtils::ReadEntireFile(theFilepath, &theFileData, inModDate, &outModDate);
OSCharArrayDeleter fileDataDeleter(theFileData.Ptr);
// -------------- process SDP to remove connection info and add track IDs, port info, and default c= line
StrPtrLen theSDPData;
SDPSourceInfo tempSDPSourceInfo(theFileData.Ptr, theFileData.Len); // will make a copy and delete in destructor
theSDPData.Ptr = tempSDPSourceInfo.GetLocalSDP(&theSDPData.Len); // returns a new buffer with processed sdp
OSCharArrayDeleter sdpDeleter(theSDPData.Ptr); // delete the temp sdp source info buffer returned by GetLocalSDP
if (theSDPData.Len <= 0) // can't find it on disk or it failed to parse just use the one in the session.
{
theSDPData.Ptr = theSession->GetLocalSDP()->Ptr; // this sdp isn't ours it must not be deleted
theSDPData.Len = theSession->GetLocalSDP()->Len;
}
// ------------ Clean up missing required SDP lines
ResizeableStringFormatter editedSDP(NULL,0);
DoDescribeAddRequiredSDPLines(inParams, theSession, outModDate, &editedSDP, &theSDPData);
StrPtrLen editedSDPSPL(editedSDP.GetBufPtr(),editedSDP.GetBytesWritten());
// ------------ Check the headers
SDPContainer checkedSDPContainer;
checkedSDPContainer.SetSDPBuffer( &editedSDPSPL );
if (!checkedSDPContainer.IsSDPBufferValid())
{
return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssUnsupportedMediaType, &sSDPNotValidMessage);
}
// ------------ Put SDP header lines in correct order
Float32 adjustMediaBandwidthPercent = 1.0;
Bool16 adjustMediaBandwidth = false;
if (sPlayerCompatibility )
adjustMediaBandwidth = QTSSModuleUtils::HavePlayerProfile(sServerPrefs,inParams,QTSSModuleUtils::kAdjustBandwidth);
if (adjustMediaBandwidth)
adjustMediaBandwidthPercent = (Float32) sAdjustMediaBandwidthPercent / 100.0;
ResizeableStringFormatter buffer;
SDPContainer* insertMediaLines = QTSS3GPPModuleUtils::Get3GPPSDPFeatureListCopy(buffer);
SDPLineSorter sortedSDP(&checkedSDPContainer,adjustMediaBandwidthPercent,insertMediaLines);
delete insertMediaLines;
// ------------ Write the SDP
UInt32 sessLen = sortedSDP.GetSessionHeaders()->Len;
UInt32 mediaLen = sortedSDP.GetMediaHeaders()->Len;
theDescribeVec[1].iov_base = sortedSDP.GetSessionHeaders()->Ptr;
theDescribeVec[1].iov_len = sortedSDP.GetSessionHeaders()->Len;
theDescribeVec[2].iov_base = sortedSDP.GetMediaHeaders()->Ptr;
theDescribeVec[2].iov_len = sortedSDP.GetMediaHeaders()->Len;
(void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
QTSSModuleUtils::SendDescribeResponse(inParams->inRTSPRequest, inParams->inClientSession,
&theDescribeVec[0], 3, sessLen + mediaLen );
return QTSS_NoErr;
}
Bool16 InfoPortsOK(QTSS_StandardRTSP_Params* inParams, SDPSourceInfo* theInfo, StrPtrLen* inPath)
{ // Check the ports based on the Pref whether to enforce a static SDP port range.
Bool16 isOK = true;
if (sEnforceStaticSDPPortRange)
{ UInt16 theInfoPort = 0;
for (UInt32 x = 0; x < theInfo->GetNumStreams(); x++)
{ theInfoPort = theInfo->GetStreamInfo(x)->fPort;
QTSS_AttributeID theErrorMessageID = qtssIllegalAttrID;
if (theInfoPort != 0)
{ if (theInfoPort < sMinimumStaticSDPPort)
theErrorMessageID = sSDPcontainsInvalidMinimumPortErr;
else if (theInfoPort > sMaximumStaticSDPPort)
theErrorMessageID = sSDPcontainsInvalidMaximumPortErr;
}
if (theErrorMessageID != qtssIllegalAttrID)
{
char thePort[32];
qtss_sprintf(thePort,"%u",theInfoPort);
char *thePath = inPath->GetAsCString();
OSCharArrayDeleter charArrayPathDeleter(thePath);
char *thePathPort = NEW char[inPath->Len + 32];
OSCharArrayDeleter charArrayPathPortDeleter(thePathPort);
qtss_sprintf(thePathPort,"%s:%s",thePath,thePort);
(void) QTSSModuleUtils::LogError(qtssWarningVerbosity, theErrorMessageID, 0, thePathPort);
StrPtrLen thePortStr(thePort);
(void) QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssUnsupportedMediaType, theErrorMessageID,&thePortStr);
return false;
}
}
}
return isOK;
}
ReflectorSession* FindOrCreateSession(StrPtrLen* inPath, QTSS_StandardRTSP_Params* inParams, StrPtrLen* inData, Bool16 isPush, Bool16 *foundSessionPtr)
{
// This function assumes that inPath is NULL terminated
// Ok, look for a reflector session matching this full path as the ID
OSMutexLocker locker(sSessionMap->GetMutex());
OSRef* theSessionRef = sSessionMap->Resolve(inPath);
ReflectorSession* theSession = NULL;
if (theSessionRef == NULL)
{
//If this URL doesn't already have a reflector session, we must make a new
//one. The first step is to create an SDPSourceInfo object.
StrPtrLen theFileData;
StrPtrLen theFileDeleteData;
//
// If no file data is provided by the caller, read the file data out of the file.
// If file data is provided, use that as our SDP data
if (inData == NULL)
{ (void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileDeleteData);
theFileData = theFileDeleteData;
}
else
theFileData = *inData;
OSCharArrayDeleter fileDataDeleter(theFileDeleteData.Ptr);
if (theFileData.Len <= 0)
return NULL;
SDPSourceInfo* theInfo = NEW SDPSourceInfo(theFileData.Ptr, theFileData.Len); // will make a copy
if (!theInfo->IsReflectable())
{ delete theInfo;
return NULL;
}
if (!InfoPortsOK(inParams, theInfo, inPath))
{ delete theInfo;
return NULL;
}
// Check if broadcast is allowed before doing anything else
// At this point we know it is a definitely a reflector session
// It is either incoming automatic broadcast setup or a client setup to view broadcast
// In either case, verify whether the broadcast is allowed, and send forbidden response
// back
if (!AllowBroadcast(inParams->inRTSPRequest))
{
(void) QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssClientForbidden, &sBroadcastNotAllowed);
return NULL;
}
//
// Setup a ReflectorSession and bind the sockets. If we are negotiating,
// make sure to let the session know that this is a Push Session so
// ports may be modified.
UInt32 theSetupFlag = ReflectorSession::kMarkSetup;
if (isPush)
theSetupFlag |= ReflectorSession::kIsPushSession;
theSession = NEW ReflectorSession(inPath);
if (theSession == NULL)
{ return NULL;
}
theSession->SetHasBufferedStreams(true); // buffer the incoming streams for clients
// SetupReflectorSession stores theInfo in theSession so DONT delete the Info if we fail here, leave it alone.
// deleting the session will delete the info.
QTSS_Error theErr = theSession->SetupReflectorSession(theInfo, inParams, theSetupFlag,sOneSSRCPerStream, sTimeoutSSRCSecs);
if (theErr != QTSS_NoErr)
{ delete theSession;
return NULL;
}
//qtss_printf("Created reflector session = %"_U32BITARG_" theInfo=%"_U32BITARG_" \n", (UInt32) theSession,(UInt32)theInfo);
//put the session's ID into the session map.
theErr = sSessionMap->Register(theSession->GetRef());
Assert(theErr == QTSS_NoErr);
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
if (!isPush)
{ OSRef* debug = sSessionMap->Resolve(inPath);
Assert(debug == theSession->GetRef());
}
}
else
{
// Check if broadcast is allowed before doing anything else
// At this point we know it is a definitely a reflector session
// It is either incoming automatic broadcast setup or a client setup to view broadcast
// In either case, verify whether the broadcast is allowed, and send forbidden response
// back
if (!AllowBroadcast(inParams->inRTSPRequest))
{
(void) QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssClientForbidden, &sBroadcastNotAllowed);
return NULL;
}
if (foundSessionPtr)
*foundSessionPtr = true;
StrPtrLen theFileData;
SDPSourceInfo* theInfo = NULL;
if (inData == NULL)
(void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileData);
OSCharArrayDeleter charArrayDeleter(theFileData.Ptr);
if (theFileData.Len <= 0)
return NULL;
theInfo = NEW SDPSourceInfo(theFileData.Ptr, theFileData.Len);
if (theInfo == NULL)
return NULL;
if (!InfoPortsOK(inParams, theInfo, inPath))
{ delete theInfo;
return NULL;
}
delete theInfo;
theSession = (ReflectorSession*)theSessionRef->GetObject();
if (isPush && theSession)
{
UInt32 theSetupFlag = ReflectorSession::kMarkSetup | ReflectorSession::kIsPushSession;
QTSS_Error theErr = theSession->SetupReflectorSession(NULL, inParams, theSetupFlag);
if (theErr != QTSS_NoErr)
{ return NULL;
}
}
}
Assert(theSession != NULL);
return theSession;
if (theSessionRef == NULL)
{
//If this URL doesn't already have a reflector session, we must make a new
//one. The first step is to create an SDPSourceInfo object.
StrPtrLen theFileData;
StrPtrLen theFileDeleteData;
//
// If no file data is provided by the caller, read the file data out of the file.
// If file data is provided, use that as our SDP data
if (inData == NULL)
{ (void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileDeleteData);
theFileData = theFileDeleteData;
}
else
theFileData = *inData;
OSCharArrayDeleter fileDataDeleter(theFileDeleteData.Ptr);
if (theFileData.Len <= 0)
return NULL;
SDPSourceInfo* theInfo = NEW SDPSourceInfo(theFileData.Ptr, theFileData.Len); // will make a copy
if (!theInfo->IsReflectable())
{ delete theInfo;
return NULL;
}
if ( !theInfo->IsActiveNow() && !isPush)
{ delete theInfo;
return NULL;
}
if (!InfoPortsOK(inParams, theInfo, inPath))
{ delete theInfo;
return NULL;
}
//
// Setup a ReflectorSession and bind the sockets. If we are negotiating,
// make sure to let the session know that this is a Push Session so
// ports may be modified.
UInt32 theSetupFlag = ReflectorSession::kMarkSetup;
if (isPush)
theSetupFlag |= ReflectorSession::kIsPushSession;
theSession = NEW ReflectorSession(inPath);
// SetupReflectorSession stores theInfo in theSession so DONT delete the Info if we fail here, leave it alone.
// deleting the session will delete the info.
QTSS_Error theErr = theSession->SetupReflectorSession(theInfo, inParams, theSetupFlag,sOneSSRCPerStream, sTimeoutSSRCSecs);
if (theErr != QTSS_NoErr || theSession == NULL)
{ delete theSession;
return NULL;
}
//printf("Created reflector session = %"_U32BITARG_" theInfo=%"_U32BITARG_" \n", (UInt32) theSession,(UInt32)theInfo);
//put the session's ID into the session map.
theErr = sSessionMap->Register(theSession->GetRef());
Assert(theErr == QTSS_NoErr);
//unless we do this, the refcount won't increment (and we'll delete the session prematurely
if (!isPush)
{ OSRef* debug = sSessionMap->Resolve(inPath);
Assert(debug == theSession->GetRef());
}
}
else
{
if (isPush)
sSessionMap->Release(theSessionRef); // don't need if a push;// don't need if a push; A Release is necessary or we will leak ReflectorSessions.
if (foundSessionPtr)
*foundSessionPtr = true;
StrPtrLen theFileData;
SDPSourceInfo* theInfo = NULL;
if (inData == NULL)
(void)QTSSModuleUtils::ReadEntireFile(inPath->Ptr, &theFileData);
OSCharArrayDeleter charArrayDeleter(theFileData.Ptr);
if (theFileData.Len <= 0)
return NULL;
theInfo = NEW SDPSourceInfo(theFileData.Ptr, theFileData.Len);
if (theInfo == NULL)
return NULL;
if ( !theInfo->IsActiveNow() && !isPush)
{ delete theInfo;
return NULL;
}
if (!InfoPortsOK(inParams, theInfo, inPath))
{ delete theInfo;
return NULL;
}
delete theInfo;
theSession = (ReflectorSession*)theSessionRef->GetObject();
if (isPush && theSession)
{
UInt32 theSetupFlag = ReflectorSession::kMarkSetup | ReflectorSession::kIsPushSession;
QTSS_Error theErr = theSession->SetupReflectorSession(NULL, inParams, theSetupFlag,sOneSSRCPerStream, sTimeoutSSRCSecs);
if (theErr != QTSS_NoErr)
{ return NULL;
}
}
}
Assert(theSession != NULL);
// Turn off overbuffering if the "disable_overbuffering" pref says so
if (sDisableOverbuffering)
(void)QTSS_SetValue(inParams->inClientSession, qtssCliSesOverBufferEnabled, 0, &sFalse, sizeof(sFalse));
return theSession;
}
// ONLY call when performing a setup.
void DeleteReflectorPushSession(QTSS_StandardRTSP_Params* inParams, ReflectorSession* theSession, Bool16 foundSession)
{
ReflectorSession* stopSessionProcessing = NULL;
QTSS_Error theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &stopSessionProcessing, sizeof(stopSessionProcessing));
Assert(theErr == QTSS_NoErr);
if (foundSession)
return; // we didn't allocate the session so don't delete
OSRef* theSessionRef = theSession->GetRef();
if (theSessionRef != NULL)
{
theSession->TearDownAllOutputs(); // just to be sure because we are about to delete the session.
sSessionMap->UnRegister(theSessionRef);// we had an error while setting up-- don't let anyone get the session
delete theSession;
}
}
QTSS_Error AddRTPStream(ReflectorSession* theSession,QTSS_StandardRTSP_Params* inParams, QTSS_RTPStreamObject *newStreamPtr)
{
// Ok, this is completely crazy but I can't think of a better way to do this that's
// safe so we'll do it this way for now. Because the ReflectorStreams use this session's
// stream queue, we need to make sure that each ReflectorStream is not reflecting to this
// session while we call QTSS_AddRTPStream. One brutal way to do this is to grab each
// ReflectorStream's mutex, which will stop every reflector stream from running.
Assert(newStreamPtr != NULL);
if (theSession != NULL)
for (UInt32 x = 0; x < theSession->GetNumStreams(); x++)
theSession->GetStreamByIndex(x)->GetMutex()->Lock();
//
// Turn off reliable UDP transport, because we are not yet equipped to
// do overbuffering.
QTSS_Error theErr = QTSS_AddRTPStream(inParams->inClientSession, inParams->inRTSPRequest, newStreamPtr, qtssASFlagsForceUDPTransport);
if (theSession != NULL)
for (UInt32 y = 0; y < theSession->GetNumStreams(); y++)
theSession->GetStreamByIndex(y)->GetMutex()->Unlock();
return theErr;
}
QTSS_Error DoSetup(QTSS_StandardRTSP_Params* inParams)
{
ReflectorSession* theSession = NULL;
//qtss_printf("QTSSReflectorModule.cpp:DoSetup \n");
// See if this is a push from a Broadcaster
UInt32 theLen = 0;
UInt32 *transportModePtr = NULL;
QTSS_Error theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqTransportMode, 0, (void**)&transportModePtr, &theLen);
Bool16 isPush = (transportModePtr != NULL && *transportModePtr == qtssRTPTransportModeRecord) ? true : false;
Bool16 foundSession = false;
// Check to see if we have a RTPSessionOutput for this Client Session. If we don't,
// we should make one
RTPSessionOutput** theOutput = NULL;
theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if (theLen != sizeof(RTPSessionOutput*))
{
//
// This may be an incoming data session. If that's the case, there will be a Reflector
// Session in the ClientSession
//theLen = sizeof(theSession);
//theErr = QTSS_GetValue(inParams->inClientSession, sSessionAttr, 0, &theSession, &theLen);
if (theErr != QTSS_NoErr && !isPush)
{
// This is not an incoming data session...
// Do the standard ReflectorSession setup, create an RTPSessionOutput
theSession = DoSessionSetup(inParams, qtssRTSPReqFilePathTrunc);
if (theSession == NULL)
return QTSS_RequestFailed;
RTPSessionOutput* theNewOutput = NEW RTPSessionOutput(inParams->inClientSession, theSession, sServerPrefs, sStreamCookieAttr );
theSession->AddOutput(theNewOutput,true);
(void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theNewOutput, sizeof(theNewOutput));
}
else
{
theSession = DoSessionSetup(inParams, qtssRTSPReqFilePathTrunc,isPush,&foundSession);
if (theSession == NULL)
return QTSS_RequestFailed;
// This is an incoming data session. Set the Reflector Session in the ClientSession
theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, sizeof(theSession));
Assert(theErr == QTSS_NoErr);
//qtss_printf("QTSSReflectorModule.cpp:SETsession sClientBroadcastSessionAttr=%"_U32BITARG_" theSession=%"_U32BITARG_" err=%"_S32BITARG_" \n",(UInt32)sClientBroadcastSessionAttr, (UInt32) theSession,theErr);
(void) QTSS_SetValue(inParams->inClientSession, qtssCliSesTimeoutMsec, 0, &sBroadcasterSessionTimeoutMilliSecs, sizeof(sBroadcasterSessionTimeoutMilliSecs));
}
}
else
{ theSession = (*theOutput)->GetReflectorSession();
if (theSession == NULL)
return QTSS_RequestFailed;
}
//unless there is a digit at the end of this path (representing trackID), don't
//even bother with the request
char* theDigitStr = NULL;
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr);
QTSSCharArrayDeleter theDigitStrDeleter(theDigitStr);
if (theDigitStr == NULL)
{
if (isPush)
DeleteReflectorPushSession(inParams,theSession, foundSession);
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,sExpectedDigitFilenameErr);
}
UInt32 theTrackID = ::strtol(theDigitStr, NULL, 10);
//
// If this is an incoming data session, skip everything having to do with setting up a new
// RTP Stream.
if (isPush)
{
//qtss_printf("QTSSReflectorModule.cpp:DoSetup is push setup\n");
// Get info about this trackID
SourceInfo::StreamInfo* theStreamInfo = theSession->GetSourceInfo()->GetStreamInfoByTrackID(theTrackID);
// If theStreamInfo is NULL, we don't have a legit track, so return an error
if (theStreamInfo == NULL)
{
DeleteReflectorPushSession(inParams,theSession, foundSession);
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
sReflectorBadTrackIDErr);
}
if (!sAllowDuplicateBroadcasts && theStreamInfo->fSetupToReceive)
{
DeleteReflectorPushSession(inParams,theSession, foundSession);
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssPreconditionFailed, sDuplicateBroadcastStreamErr);
}
UInt16 theReceiveBroadcastStreamPort = theStreamInfo->fPort;
theErr = QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqSetUpServerPort, 0, &theReceiveBroadcastStreamPort, sizeof(theReceiveBroadcastStreamPort));
Assert(theErr == QTSS_NoErr);
QTSS_RTPStreamObject newStream = NULL;
theErr = AddRTPStream(theSession,inParams,&newStream);
Assert(theErr == QTSS_NoErr);
if (theErr != QTSS_NoErr)
{
DeleteReflectorPushSession(inParams,theSession, foundSession);
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0);
}
//send the setup response
(void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, newStream, 0);
theStreamInfo->fSetupToReceive = true;
// This is an incoming data session. Set the Reflector Session in the ClientSession
theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, sizeof(theSession));
Assert(theErr == QTSS_NoErr);
if (theSession != NULL)
theSession->AddBroadcasterClientSession(inParams);
return QTSS_NoErr;
}
// Get info about this trackID
SourceInfo::StreamInfo* theStreamInfo = theSession->GetSourceInfo()->GetStreamInfoByTrackID(theTrackID);
// If theStreamInfo is NULL, we don't have a legit track, so return an error
if (theStreamInfo == NULL)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
sReflectorBadTrackIDErr);
StrPtrLen* thePayloadName = &theStreamInfo->fPayloadName;
QTSS_RTPPayloadType thePayloadType = theStreamInfo->fPayloadType;
StringParser parser(thePayloadName);
parser.GetThru(NULL, '/');
theStreamInfo->fTimeScale = parser.ConsumeInteger(NULL);
if (theStreamInfo->fTimeScale == 0)
theStreamInfo->fTimeScale = 90000;
QTSS_RTPStreamObject newStream = NULL;
{
// Ok, this is completely crazy but I can't think of a better way to do this that's
// safe so we'll do it this way for now. Because the ReflectorStreams use this session's
// stream queue, we need to make sure that each ReflectorStream is not reflecting to this
// session while we call QTSS_AddRTPStream. One brutal way to do this is to grab each
// ReflectorStream's mutex, which will stop every reflector stream from running.
for (UInt32 x = 0; x < theSession->GetNumStreams(); x++)
theSession->GetStreamByIndex(x)->GetMutex()->Lock();
theErr = QTSS_AddRTPStream(inParams->inClientSession, inParams->inRTSPRequest, &newStream, 0);
for (UInt32 y = 0; y < theSession->GetNumStreams(); y++)
theSession->GetStreamByIndex(y)->GetMutex()->Unlock();
if (theErr != QTSS_NoErr)
return theErr;
}
// Set up dictionary items for this stream
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadName, 0, thePayloadName->Ptr, thePayloadName->Len);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrPayloadType, 0, &thePayloadType, sizeof(thePayloadType));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrTrackID, 0, &theTrackID, sizeof(theTrackID));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(newStream, qtssRTPStrTimescale, 0, &theStreamInfo->fTimeScale, sizeof(theStreamInfo->fTimeScale));
Assert(theErr == QTSS_NoErr);
// We only want to allow over buffering to dynamic rate clients
SInt32 canDynamicRate = -1;
theLen = sizeof(canDynamicRate);
(void) QTSS_GetValue(inParams->inRTSPRequest, qtssRTSPReqDynamicRateState, 0, (void*) &canDynamicRate, &theLen);
if (canDynamicRate < 1) // -1 no rate field, 0 off
(void)QTSS_SetValue(inParams->inClientSession, qtssCliSesOverBufferEnabled, 0, &sFalse, sizeof(sFalse));
// Place the stream cookie in this stream for future reference
void* theStreamCookie = theSession->GetStreamCookie(theTrackID);
Assert(theStreamCookie != NULL);
theErr = QTSS_SetValue(newStream, sStreamCookieAttr, 0, &theStreamCookie, sizeof(theStreamCookie));
Assert(theErr == QTSS_NoErr);
// Set the number of quality levels.
static UInt32 sNumQualityLevels = ReflectorSession::kNumQualityLevels;
theErr = QTSS_SetValue(newStream, qtssRTPStrNumQualityLevels, 0, &sNumQualityLevels, sizeof(sNumQualityLevels));
Assert(theErr == QTSS_NoErr);
//send the setup response
(void)QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssCacheControlHeader,
kCacheControlHeader.Ptr, kCacheControlHeader.Len);
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, newStream, qtssSetupRespDontWriteSSRC);
return QTSS_NoErr;
}
Bool16 HaveStreamBuffers(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
{
if (inSession == NULL || inParams == NULL)
return false;
UInt32 firstTimeStamp = 0;
UInt16 firstSeqNum = 0;
ReflectorSender* theSender = NULL;
ReflectorStream* theReflectorStream = NULL;
QTSS_RTPStreamObject* theRef = NULL;
UInt32 theStreamIndex = 0;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_NoErr;
Bool16 haveBufferedStreams = true; // set to false and return if we can't set the packets
UInt32 y = 0;
SInt64 packetArrivalTime = 0;
//lock all streams
for (y = 0; y < inSession->GetNumStreams(); y++)
inSession->GetStreamByIndex(y)->GetMutex()->Lock();
for ( theStreamIndex = 0;
QTSS_GetValuePtr(inParams->inClientSession, qtssCliSesStreamObjects, theStreamIndex, (void**)&theRef, &theLen) == QTSS_NoErr;
theStreamIndex++)
{
theReflectorStream = inSession->GetStreamByIndex(theStreamIndex);
// if (!theReflectorStream->HasFirstRTCP())
// printf("theStreamIndex =%"_U32BITARG_" no rtcp\n", theStreamIndex);
// if (!theReflectorStream->HasFirstRTP())
// printf("theStreamIndex = %"_U32BITARG_" no rtp\n", theStreamIndex);
if ((theReflectorStream == NULL) || (false == theReflectorStream->HasFirstRTP()) )
{
haveBufferedStreams = false;
//printf("1 breaking no buffered streams\n");
break;
}
theSender = theReflectorStream->GetRTPSender();
haveBufferedStreams = theSender->GetFirstPacketInfo(&firstSeqNum, &firstTimeStamp, &packetArrivalTime);
//printf("theStreamIndex= %"_U32BITARG_" haveBufferedStreams=%d, seqnum=%d, timestamp=%"_U32BITARG_"\n", theStreamIndex, haveBufferedStreams, firstSeqNum, firstTimeStamp);
if (!haveBufferedStreams)
{
//printf("2 breaking no buffered streams\n");
break;
}
theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstSeqNumber, 0, &firstSeqNum, sizeof(firstSeqNum));
Assert(theErr == QTSS_NoErr);
theErr = QTSS_SetValue(*theRef, qtssRTPStrFirstTimestamp, 0, &firstTimeStamp, sizeof(firstTimeStamp));
Assert(theErr == QTSS_NoErr);
}
//unlock all streams
for (y = 0; y < inSession->GetNumStreams(); y++)
inSession->GetStreamByIndex(y)->GetMutex()->Unlock();
return haveBufferedStreams;
}
QTSS_Error DoPlay(QTSS_StandardRTSP_Params* inParams, ReflectorSession* inSession)
{
QTSS_Error theErr = QTSS_NoErr;
UInt32 flags = 0;
UInt32 theLen = 0;
Bool16 rtpInfoEnabled = false;
if (inSession == NULL) // it is a broadcast session so store the broadcast session.
{ if (!sDefaultBroadcastPushEnabled)
return QTSS_RequestFailed;
theLen = sizeof(inSession);
theErr = QTSS_GetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &inSession, &theLen);
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
theErr = QTSS_SetValue(inParams->inClientSession, sKillClientsEnabledAttr, 0, &sTearDownClientsOnDisconnect, sizeof(sTearDownClientsOnDisconnect));
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
Assert(inSession != NULL);
theErr = QTSS_SetValue(inParams->inRTSPSession, sRTSPBroadcastSessionAttr, 0, &inSession, sizeof(inSession));
if (theErr != QTSS_NoErr)
return QTSS_RequestFailed;
//qtss_printf("QTSSReflectorModule:SET for att err=%"_S32BITARG_" id=%"_S32BITARG_"\n",theErr,inParams->inRTSPSession);
// this code needs to be cleaned up
// Check and see if the full path to this file matches an existing ReflectorSession
StrPtrLen thePathPtr;
OSCharArrayDeleter sdpPath(QTSSModuleUtils::GetFullPath( inParams->inRTSPRequest,
qtssRTSPReqFilePath,
&thePathPtr.Len, &sSDPSuffix));
thePathPtr.Ptr = sdpPath.GetObject();
// remove trackID designation from the path if it is there
char *trackStr = thePathPtr.FindString("/trackID=");
if (trackStr != NULL && *trackStr != 0)
{
*trackStr = 0; // terminate the string.
thePathPtr.Len = ::strlen(thePathPtr.Ptr);
}
// If the actual file path has a .sdp in it, first look for the URL without the extra .sdp
if (thePathPtr.Len > (sSDPSuffix.Len * 2))
{
// Check and see if there is a .sdp in the file path.
// If there is, truncate off our extra ".sdp", cuz it isn't needed
StrPtrLen endOfPath(&sdpPath.GetObject()[thePathPtr.Len - (sSDPSuffix.Len * 2)], sSDPSuffix.Len);
if (endOfPath.Equal(sSDPSuffix))
{
sdpPath.GetObject()[thePathPtr.Len - sSDPSuffix.Len] = '\0';
thePathPtr.Len -= sSDPSuffix.Len;
}
}
// do all above so we can add the session to the map with Resolve here.
// we must only do this once.
OSRef* debug = sSessionMap->Resolve(&thePathPtr);
if (debug != inSession->GetRef())
{
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0);
}
KeepSession(inParams->inRTSPRequest,true);
//qtss_printf("QTSSReflectorModule.cpp:DoPlay (PUSH) inRTSPSession=%"_U32BITARG_" inClientSession=%"_U32BITARG_"\n",(UInt32)inParams->inRTSPSession,(UInt32)inParams->inClientSession);
}
else// it is NOT a broadcaster session
{
RTPSessionOutput** theOutput = NULL;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)) || (theOutput == NULL))
return QTSS_RequestFailed;
(*theOutput)->InitializeStreams();
// Tell the session what the bitrate of this reflection is. This is nice for logging,
// it also allows the server to scale the TCP buffer size appropriately if we are
// interleaving the data over TCP. This must be set before calling QTSS_Play so the
// server can use it from within QTSS_Play
UInt32 bitsPerSecond = inSession->GetBitRate();
(void)QTSS_SetValue(inParams->inClientSession, qtssCliSesMovieAverageBitRate, 0, &bitsPerSecond, sizeof(bitsPerSecond));
if (sPlayResponseRangeHeader)
{
StrPtrLen temp;
theErr = QTSS_GetValuePtr(inParams->inClientSession, sRTPInfoWaitTimeAttr, 0, (void**) &temp.Ptr, &temp.Len);
if (theErr != QTSS_NoErr)
QTSS_AppendRTSPHeader(inParams->inRTSPRequest, qtssRangeHeader,sTheNowRangeHeader.Ptr, sTheNowRangeHeader.Len);
}
if (sPlayerCompatibility )
rtpInfoEnabled = QTSSModuleUtils::HavePlayerProfile(sServerPrefs,inParams, QTSSModuleUtils::kRequiresRTPInfoSeqAndTime);
if (sForceRTPInfoSeqAndTime)
rtpInfoEnabled = true;
if (sRTPInfoDisabled )
rtpInfoEnabled = false;
if (rtpInfoEnabled)
{
flags = qtssPlayRespWriteTrackInfo; //write first timestampe and seq num to rtpinfo
Bool16 haveBufferedStreams = HaveStreamBuffers(inParams,inSession);
if (haveBufferedStreams) // send the cached rtp time and seq number in the response.
{
QTSS_Error theErr = QTSS_Play(inParams->inClientSession, inParams->inRTSPRequest, qtssPlayRespWriteTrackInfo);
if (theErr != QTSS_NoErr)
return theErr;
}
else
{
SInt32 waitTimeLoopCount = 0;
theLen = sizeof(waitTimeLoopCount);
theErr = QTSS_GetValue(inParams->inClientSession, sRTPInfoWaitTimeAttr, 0, &waitTimeLoopCount, &theLen);
if (theErr != QTSS_NoErr)
(void)QTSS_SetValue(inParams->inClientSession, sRTPInfoWaitTimeAttr, 0, &sWaitTimeLoopCount, sizeof(sWaitTimeLoopCount));
else
{
if (waitTimeLoopCount < 1)
return QTSSModuleUtils::SendErrorResponseWithMessage(inParams->inRTSPRequest, qtssClientNotFound, &sBroadcastNotActive);
waitTimeLoopCount --;
(void)QTSS_SetValue(inParams->inClientSession, sRTPInfoWaitTimeAttr, 0, &waitTimeLoopCount, sizeof(waitTimeLoopCount));
}
//qtss_printf("QTSSReflectorModule:DoPlay wait 100ms waitTimeLoopCount=%ld\n", waitTimeLoopCount);
SInt64 interval = 1 * 100; // 100 millisecond
QTSS_SetIdleTimer( interval );
return QTSS_NoErr;
}
}
else
{
QTSS_Error theErr = QTSS_Play(inParams->inClientSession, inParams->inRTSPRequest, qtssPlayFlagsAppendServerInfo);
if (theErr != QTSS_NoErr)
return theErr;
}
}
(void)QTSS_SendStandardRTSPResponse(inParams->inRTSPRequest, inParams->inClientSession, flags);
return QTSS_NoErr;
}
Bool16 KillSession(StrPtrLen *sdpPathStr, Bool16 killClients)
{
OSRef* theSessionRef = sSessionMap->Resolve(sdpPathStr);
if (theSessionRef != NULL)
{
ReflectorSession* theSession = (ReflectorSession*)theSessionRef->GetObject();
RemoveOutput(NULL, theSession, killClients);
(void)QTSS_Teardown(theSession->GetBroadcasterSession());
return true;
}
return false;
}
void KillCommandPathInList()
{
char filePath[128] = "";
ResizeableStringFormatter commandPath( (char*) filePath, sizeof(filePath)); // ResizeableStringFormatter is safer and more efficient than StringFormatter for most paths.
OSMutexLocker locker (sSessionMap->GetMutex());
for (OSRefHashTableIter theIter(sSessionMap->GetHashTable()); !theIter.IsDone(); theIter.Next())
{
OSRef* theRef = theIter.GetCurrent();
if (theRef == NULL)
continue;
commandPath.Reset();
commandPath.Put(*(theRef->GetString()));
commandPath.Put(sSDPKillSuffix);
commandPath.PutTerminator();
char *theCommandPath = commandPath.GetBufPtr();
QTSS_Object outFileObject;
QTSS_Error err = QTSS_OpenFileObject(theCommandPath, qtssOpenFileNoFlags, &outFileObject);
if (err == QTSS_NoErr)
{
(void) QTSS_CloseFileObject(outFileObject);
::unlink(theCommandPath);
KillSession(theRef->GetString(), true);
}
}
}
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
RTPSessionOutput** theOutput = NULL;
ReflectorOutput* outputPtr = NULL;
ReflectorSession* theSession = NULL;
OSMutexLocker locker (sSessionMap->GetMutex());
UInt32 theLen = sizeof(theSession);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, &theLen);
//qtss_printf("QTSSReflectorModule.cpp:DestroySession sClientBroadcastSessionAttr=%"_U32BITARG_" theSession=%"_U32BITARG_" err=%"_S32BITARG_" \n",(UInt32)sClientBroadcastSessionAttr, (UInt32)theSession,theErr);
if (theSession != NULL) // it is a broadcaster session
{
ReflectorSession* deletedSession = NULL;
theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &deletedSession, sizeof(deletedSession));
SourceInfo* theSoureInfo = theSession->GetSourceInfo();
if (theSoureInfo == NULL)
return QTSS_NoErr;
UInt32 numStreams = theSession->GetNumStreams();
SourceInfo::StreamInfo* theStreamInfo = NULL;
for (UInt32 index = 0; index < numStreams; index++)
{ theStreamInfo = theSoureInfo->GetStreamInfo(index);
if (theStreamInfo != NULL)
theStreamInfo->fSetupToReceive = false;
}
Bool16 killClients = false; // the pref as the default
UInt32 theLen = sizeof(killClients);
(void) QTSS_GetValue(inParams->inClientSession, sKillClientsEnabledAttr, 0, &killClients, &theLen);
//qtss_printf("QTSSReflectorModule.cpp:DestroySession broadcaster theSession=%"_U32BITARG_"\n", (UInt32) theSession);
theSession->RemoveSessionFromOutput(inParams->inClientSession);
RemoveOutput(NULL, theSession, killClients);
}
else
{
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)) || (theOutput == NULL) || (*theOutput == NULL))
return QTSS_RequestFailed;
theSession = (*theOutput)->GetReflectorSession();
if (theOutput != NULL)
outputPtr = (ReflectorOutput*) *theOutput;
if (outputPtr != NULL)
{
RemoveOutput(outputPtr, theSession, false);
RTPSessionOutput* theOutput = NULL;
(void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theOutput, sizeof(theOutput));
}
}
return QTSS_NoErr;
}
void RemoveOutput(ReflectorOutput* inOutput, ReflectorSession* inSession, Bool16 killClients)
{
//qtss_printf("QTSSReflectorModule.cpp:RemoveOutput\n");
//This function removes the output from the ReflectorSession, then
Assert(inSession);
if (inSession != NULL)
{
if (inOutput != NULL)
{
inSession->RemoveOutput(inOutput,true);
}
else
{ // it is a Broadcaster session
//qtss_printf("QTSSReflectorModule.cpp:RemoveOutput it is a broadcaster session\n");
SourceInfo* theInfo = inSession->GetSourceInfo();
Assert(theInfo);
if (theInfo->IsRTSPControlled())
{
FileDeleter(inSession->GetSourcePath());
}
if (killClients || sTearDownClientsOnDisconnect)
{
inSession->TearDownAllOutputs();
}
}
//qtss_printf("QTSSReflectorModule.cpp:RemoveOutput refcount =%"_U32BITARG_"\n", inSession->GetRef()->GetRefCount() );
//check if the ReflectorSession should be deleted
//it should if its ref count has dropped to 0
OSRef* theSessionRef = inSession->GetRef();
if (theSessionRef != NULL)
{
//qtss_printf("QTSSReflectorModule.cpp:RemoveOutput UnRegister session =%p refcount=%"_U32BITARG_"\n", theSessionRef, theSessionRef->GetRefCount() ) ;
for (UInt32 x = 0; x < inSession->GetNumStreams(); x++)
{
if (inSession->GetStreamByIndex(x) == NULL)
continue;
Assert(theSessionRef->GetRefCount() > 0) //this shouldn't happen.
if (theSessionRef->GetRefCount() > 0)
sSessionMap->Release(theSessionRef); // one of the sessions on the ref is ending so decrement the count for each valid stream
}
if (theSessionRef->GetRefCount() == 0)
{
//qtss_printf("QTSSReflectorModule.cpp:RemoveOutput UnRegister and delete session =%p refcount=%"_U32BITARG_"\n", theSessionRef, theSessionRef->GetRefCount() ) ;
sSessionMap->UnRegister(theSessionRef);
delete inSession;
}
}
}
delete inOutput;
}
Bool16 AcceptSession(QTSS_StandardRTSP_Params* inParams)
{
QTSS_RTSPSessionObject inRTSPSession = inParams->inRTSPSession;
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
QTSS_ActionFlags action = QTSSModuleUtils::GetRequestActions(theRTSPRequest);
if(action != qtssActionFlagsWrite)
return false;
if (QTSSModuleUtils::UserInGroup(QTSSModuleUtils::GetUserProfileObject(theRTSPRequest), sBroadcasterGroup.Ptr, sBroadcasterGroup.Len))
return true; // ok we are allowing this broadcaster user
char remoteAddress[20] = {0};
StrPtrLen theClientIPAddressStr(remoteAddress,sizeof(remoteAddress));
QTSS_Error err = QTSS_GetValue(inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void*)theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len);
if (err != QTSS_NoErr)
return false;
if (IPComponentStr(&theClientIPAddressStr).IsLocal())
{
if (sAuthenticateLocalBroadcast)
return false;
else
return true;
}
if (QTSSModuleUtils::AddressInList(sPrefs, sIPAllowListID, &theClientIPAddressStr))
return true;
return false;
}
QTSS_Error ReflectorAuthorizeRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
if ( AcceptSession(inParams) )
{
Bool16 allowed = true;
QTSS_RTSPRequestObject request = inParams->inRTSPRequest;
(void) QTSSModuleUtils::AuthorizeRequest(request, &allowed, &allowed, &allowed);
return QTSS_NoErr;
}
Bool16 allowNoAccessFiles = false;
QTSS_ActionFlags noAction = ~qtssActionFlagsWrite; //no action anything but a write
QTSS_ActionFlags authorizeAction = QTSSModuleUtils::GetRequestActions(inParams->inRTSPRequest);
//printf("ReflectorAuthorizeRTSPRequest authorizeAction=%d qtssActionFlagsWrite=%d\n", authorizeAction, qtssActionFlagsWrite);
Bool16 outAllowAnyUser = false;
Bool16 outAuthorized = false;
QTAccessFile accessFile;
accessFile.AuthorizeRequest(inParams,allowNoAccessFiles, noAction, authorizeAction, &outAuthorized, &outAllowAnyUser);
if( (outAuthorized == false) && (authorizeAction & qtssActionFlagsWrite) ) //handle it
{
//printf("ReflectorAuthorizeRTSPRequest SET not allowed\n");
Bool16 allowed = false;
(void) QTSSModuleUtils::AuthorizeRequest(inParams->inRTSPRequest, &allowed, &allowed, &allowed);
}
return QTSS_NoErr;
}
QTSS_Error RedirectBroadcast(QTSS_StandardRTSP_Params* inParams)
{
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
char* requestPathStr;
(void)QTSS_GetValueAsString(theRequest, qtssRTSPReqFilePath, 0, &requestPathStr);
QTSSCharArrayDeleter requestPathStrDeleter(requestPathStr);
StrPtrLen theRequestPath(requestPathStr);
StringParser theRequestPathParser(&theRequestPath);
// request path begins with a '/' for ex. /mysample.mov or /redirect_broadcast_keyword/mysample.mov
theRequestPathParser.Expect(kPathDelimiterChar);
StrPtrLen theFirstPath;
theRequestPathParser.ConsumeUntil(&theFirstPath, kPathDelimiterChar);
Assert (theFirstPath.Len != 0);
// If the redirect_broadcast_keyword and redirect_broadcast_dir prefs are set & the first part of the path matches the keyword
if ( (sRedirectBroadcastsKeyword && sRedirectBroadcastsKeyword[0] != 0)
&& (sBroadcastsRedirectDir && sBroadcastsRedirectDir[0] != 0)
&& theFirstPath.EqualIgnoreCase(sRedirectBroadcastsKeyword, ::strlen(sRedirectBroadcastsKeyword)) )
{
// set qtssRTSPReqRootDir
(void)QTSS_SetValue(theRequest, qtssRTSPReqRootDir, 0, sBroadcastsRedirectDir, ::strlen(sBroadcastsRedirectDir));
// set the request file path to the new path with the keyword stripped
StrPtrLen theStrippedRequestPath;
theRequestPathParser.ConsumeLength(&theStrippedRequestPath, theRequestPathParser.GetDataRemaining());
(void) QTSS_SetValue(theRequest, qtssRTSPReqFilePath, 0, theStrippedRequestPath.Ptr, theStrippedRequestPath.Len);
}
return QTSS_NoErr;
}
Bool16 AllowBroadcast(QTSS_RTSPRequestObject inRTSPRequest)
{
// If reflection of broadcasts is disabled, return false
if (!sReflectBroadcasts)
return false;
// If request path is not in any of the broadcast_dir paths, return false
if (!InBroadcastDirList(inRTSPRequest))
return false;
return true;
}
Bool16 InBroadcastDirList(QTSS_RTSPRequestObject inRTSPRequest)
{
Bool16 allowed = false;
char* theURIPathStr;
(void)QTSS_GetValueAsString(inRTSPRequest, qtssRTSPReqFilePath, 0, &theURIPathStr);
QTSSCharArrayDeleter requestPathStrDeleter(theURIPathStr);
char* theLocalPathStr;
(void)QTSS_GetValueAsString(inRTSPRequest, qtssRTSPReqLocalPath, 0, &theLocalPathStr);
StrPtrLenDel requestPath(theLocalPathStr);
char* theRequestPathStr = NULL;
char* theBroadcastDirStr = NULL;
Bool16 isURI = true;
UInt32 index = 0;
UInt32 numValues = 0;
(void) QTSS_GetNumValues(sPrefs, sBroadcastDirListID, &numValues);
if (numValues == 0)
return true;
while (!allowed && (index < numValues))
{
(void) QTSS_GetValueAsString(sPrefs, sBroadcastDirListID, index, &theBroadcastDirStr);
StrPtrLen theBroadcastDir(theBroadcastDirStr);
if (theBroadcastDir.Len == 0) // an empty dir matches all
return true;
if (IsAbsolutePath(&theBroadcastDir))
{
theRequestPathStr = theLocalPathStr;
isURI = false;
}
else
theRequestPathStr = theURIPathStr;
StrPtrLen requestPath(theRequestPathStr);
StringParser requestPathParser(&requestPath);
StrPtrLen pathPrefix;
if (isURI)
requestPathParser.Expect(kPathDelimiterChar);
requestPathParser.ConsumeLength(&pathPrefix, theBroadcastDir.Len);
// if the first part of the request path matches the broadcast_dir path, return true
if (pathPrefix.Equal(theBroadcastDir))
allowed = true;
(void) QTSS_Delete(theBroadcastDirStr);
index ++;
}
return allowed;
}
Bool16 IsAbsolutePath(StrPtrLen *inPathPtr)
{
StringParser thePathParser(inPathPtr);
#ifdef __Win32__
if ((thePathParser[1] == ':') && (thePathParser[2] == kPathDelimiterChar))
#else
if (thePathParser.PeekFast() == kPathDelimiterChar)
#endif
return true;
return false;
}