2368 lines
98 KiB
C++
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;
|
|
}
|