381 lines
14 KiB
C++
381 lines
14 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: QTSSRawFileModule.cpp
|
||
|
|
||
|
Contains: Implementation of Raw File module
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "QTSSRawFileModule.h"
|
||
|
#include "OSHeaders.h"
|
||
|
#include "StrPtrLen.h"
|
||
|
#include "OSArrayObjectDeleter.h"
|
||
|
#include "QTSSModuleUtils.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "ev.h"
|
||
|
#include "QTSSMemoryDeleter.h"
|
||
|
|
||
|
#define RAWFILE_FILE_ASYNC 1
|
||
|
#define RAW_FILE_DEBUGGING 0
|
||
|
|
||
|
|
||
|
// ATTRIBUTES IDs
|
||
|
|
||
|
static QTSS_AttributeID sStateAttr = qtssIllegalAttrID;
|
||
|
static QTSS_AttributeID sFileAttr = qtssIllegalAttrID;
|
||
|
static QTSS_AttributeID sFileBufferAttr = qtssIllegalAttrID;
|
||
|
static QTSS_AttributeID sReadOffsetAttr = qtssIllegalAttrID;
|
||
|
static QTSS_AttributeID sWriteOffsetAttr = qtssIllegalAttrID;
|
||
|
|
||
|
// STATIC DATA
|
||
|
|
||
|
static StrPtrLen sRawSuffix(".raw");
|
||
|
|
||
|
static const UInt32 kReadingBufferState = 0;
|
||
|
static const UInt32 kWritingBufferState = 1;
|
||
|
|
||
|
// FUNCTIONS
|
||
|
|
||
|
static QTSS_Error QTSSRawFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
|
||
|
static QTSS_Error Register(QTSS_Register_Params* inParams);
|
||
|
static QTSS_Error Preprocess(QTSS_StandardRTSP_Params* inParams);
|
||
|
|
||
|
|
||
|
|
||
|
QTSS_Error QTSSRawFileModule_Main(void* inPrivateArgs)
|
||
|
{
|
||
|
return _stublibrary_main(inPrivateArgs, QTSSRawFileModuleDispatch);
|
||
|
}
|
||
|
|
||
|
|
||
|
QTSS_Error QTSSRawFileModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
|
||
|
{
|
||
|
switch (inRole)
|
||
|
{
|
||
|
case QTSS_Register_Role:
|
||
|
return Register(&inParams->regParams);
|
||
|
case QTSS_RTSPPreProcessor_Role:
|
||
|
return Preprocess(&inParams->rtspPreProcessorParams);
|
||
|
}
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error Register(QTSS_Register_Params* inParams)
|
||
|
{
|
||
|
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
|
||
|
|
||
|
static char* sStateName = "QTSSRawFileModuleState";
|
||
|
static char* sFileName = "QTSSRawFileModuleFile";
|
||
|
static char* sFileBufferName = "QTSSRawFileModuleFileBuffer";
|
||
|
static char* sReadOffsetName = "QTSSRawFileModuleReadOffset";
|
||
|
static char* sWriteOffsetName = "QTSSRawFileModuleWriteOffset";
|
||
|
|
||
|
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sStateName, NULL, qtssAttrDataTypeUInt32);
|
||
|
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sStateName, &sStateAttr);
|
||
|
|
||
|
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileName, NULL, qtssAttrDataTypeUInt32);
|
||
|
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileName, &sFileAttr);
|
||
|
|
||
|
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sFileBufferName, NULL, qtssAttrDataTypeCharArray);
|
||
|
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sFileBufferName, &sFileBufferAttr);
|
||
|
|
||
|
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sReadOffsetName, NULL, qtssAttrDataTypeUInt32);
|
||
|
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sReadOffsetName, &sReadOffsetAttr);
|
||
|
|
||
|
(void)QTSS_AddStaticAttribute(qtssRTSPSessionObjectType, sWriteOffsetName, NULL, qtssAttrDataTypeUInt32);
|
||
|
(void)QTSS_IDForAttr(qtssRTSPSessionObjectType, sWriteOffsetName, &sWriteOffsetAttr);
|
||
|
|
||
|
// Tell the server our name!
|
||
|
static char* sModuleName = "QTSSRawFileModule";
|
||
|
::strcpy(inParams->outModuleName, sModuleName);
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
QTSS_Error Preprocess(QTSS_StandardRTSP_Params* inParams)
|
||
|
{
|
||
|
static UInt32 sFileBufSize = 32768;
|
||
|
static UInt32 sInitialState = kReadingBufferState;
|
||
|
static UInt32 sZero = 0;
|
||
|
|
||
|
UInt32 theLen = 0;
|
||
|
UInt32* theStateP = NULL;
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
|
||
|
QTSS_Object theFile = NULL;
|
||
|
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sStateAttr, 0, (void**)&theStateP, &theLen);
|
||
|
if ((theStateP == NULL) || (theLen != sizeof(UInt32)))
|
||
|
{
|
||
|
// Initial state. We haven't started sending the file yet, so
|
||
|
// check to see if this is our request, and if it is, set everything up.
|
||
|
|
||
|
// Only operate if this is a DESCRIBE
|
||
|
QTSS_RTSPMethod* theMethod = NULL;
|
||
|
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 != qtssDescribeMethod)
|
||
|
return QTSS_RequestFailed;
|
||
|
|
||
|
// Check to see if this is a raw file request
|
||
|
char* theFilePath = NULL;
|
||
|
(void)QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqLocalPath, 0, &theFilePath);
|
||
|
QTSSCharArrayDeleter theFilePathDeleter(theFilePath);
|
||
|
theLen = ::strlen(theFilePath);
|
||
|
|
||
|
// Copy the full path, and append a ".raw"
|
||
|
OSCharArrayDeleter rawPath(NEW char[theLen + sRawSuffix.Len + 4]);
|
||
|
::memcpy(rawPath.GetObject(), theFilePath, theLen);
|
||
|
::strcpy(rawPath.GetObject() + theLen, sRawSuffix.Ptr);
|
||
|
|
||
|
#if RAWFILE_FILE_ASYNC
|
||
|
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
|
||
|
#else
|
||
|
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
|
||
|
#endif
|
||
|
|
||
|
// If the file doesn't exist, and if this is a path with a '.raw' at the end,
|
||
|
// check to see if the path without the extra .raw exists
|
||
|
if (theErr != QTSS_NoErr)
|
||
|
{
|
||
|
theFile = NULL;
|
||
|
rawPath.GetObject()[theLen] = '\0';
|
||
|
|
||
|
if (theLen > sRawSuffix.Len)
|
||
|
{
|
||
|
StrPtrLen comparer((theFilePath + theLen) - sRawSuffix.Len, sRawSuffix.Len);
|
||
|
if (comparer.Equal(sRawSuffix))
|
||
|
{
|
||
|
#if RAWFILE_FILE_ASYNC
|
||
|
theErr = QTSS_OpenFileObject(rawPath.GetObject(), qtssOpenFileAsync, &theFile);
|
||
|
#else
|
||
|
theErr = QTSS_OpenFileObject(rawPath.GetObject(), kOpenFileNoFlags, &theFile);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the file doesn't exist, we should probably return a 404 not found.
|
||
|
if (theErr != QTSS_NoErr)
|
||
|
return QTSS_RequestFailed;
|
||
|
|
||
|
// Before sending any response, set keep alive to off for this connection
|
||
|
// Regardless of what the client sends, the server always closes the connection after sending the file
|
||
|
static Bool16 sFalse = false;
|
||
|
(void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
|
||
|
|
||
|
// We have a real file. Setup all the dictionary values we need
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sFileAttr, 0, &theFile, sizeof(theFile));
|
||
|
|
||
|
// Create a buffer to store data.
|
||
|
char* theFileBuffer = NEW char[sFileBufSize];
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sFileBufferAttr, 0, &theFileBuffer, sizeof(theFileBuffer));
|
||
|
|
||
|
// Store our initial state
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sStateAttr, 0, &sInitialState, sizeof(sInitialState));
|
||
|
theStateP = &sInitialState; // so we can proceed normally
|
||
|
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sReadOffsetAttr, 0, &sZero, sizeof(sZero));
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sWriteOffsetAttr, 0, &sZero, sizeof(sZero));
|
||
|
}
|
||
|
|
||
|
// Get our attributes
|
||
|
char** theFileBufferP = NULL;
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
|
||
|
Assert(theFileBufferP != NULL);
|
||
|
Assert(theLen == sizeof(char*));
|
||
|
|
||
|
QTSS_Object* theFileP = NULL;
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileAttr, 0, (void**)&theFileP, &theLen);
|
||
|
Assert(theFileP != NULL);
|
||
|
Assert(theLen == sizeof(QTSS_Object));
|
||
|
|
||
|
UInt32* theReadOffsetP = NULL;
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sReadOffsetAttr, 0, (void**)&theReadOffsetP, &theLen);
|
||
|
Assert(theReadOffsetP != NULL);
|
||
|
Assert(theLen == sizeof(UInt32));
|
||
|
|
||
|
UInt32* theWriteOffsetP = NULL;
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sWriteOffsetAttr, 0, (void**)&theWriteOffsetP, &theLen);
|
||
|
Assert(theWriteOffsetP != NULL);
|
||
|
Assert(theLen == sizeof(UInt32));
|
||
|
|
||
|
UInt32 theReadOffset = *theReadOffsetP;
|
||
|
UInt32 theWriteOffset = *theWriteOffsetP;
|
||
|
UInt32 theState = *theStateP;
|
||
|
|
||
|
Bool16 isBlocked = false;
|
||
|
|
||
|
// Get the length of the file onto the stack
|
||
|
theLen = sizeof(UInt64);
|
||
|
UInt64 theFileLength = 0;
|
||
|
theErr = QTSS_GetValue(*theFileP, qtssFlObjLength, 0, (void*)&theFileLength, &theLen);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLen == sizeof(UInt64));
|
||
|
|
||
|
// Get the offset in the file onto the stack
|
||
|
theLen = sizeof(UInt64);
|
||
|
UInt64 theOffset = 0;
|
||
|
theErr = QTSS_GetValue(*theFileP, qtssFlObjPosition, 0, (void*)&theOffset, &theLen);
|
||
|
Assert(theErr == QTSS_NoErr);
|
||
|
Assert(theLen == sizeof(UInt64));
|
||
|
|
||
|
while (!isBlocked)
|
||
|
{
|
||
|
// If we have less than the full buffer size left to go in the file,
|
||
|
// down adjust our buffer size to be the amount of data remaining in the file
|
||
|
UInt32 theBufferSize = sFileBufSize;
|
||
|
if ((theFileLength - theOffset) < sFileBufSize)
|
||
|
theBufferSize = (UInt32) (theFileLength - theOffset);
|
||
|
|
||
|
switch (theState)
|
||
|
{
|
||
|
case kReadingBufferState:
|
||
|
{
|
||
|
// Read as much data as possible out of the file
|
||
|
UInt32 theRecvLen = 0;
|
||
|
(void)QTSS_Read(*theFileP,
|
||
|
(*theFileBufferP) + theReadOffset,
|
||
|
theBufferSize - theReadOffset,
|
||
|
&theRecvLen);
|
||
|
theReadOffset += theRecvLen;
|
||
|
theOffset += theRecvLen;
|
||
|
#if RAW_FILE_DEBUGGING
|
||
|
qtss_printf("Got %"_U32BITARG_" bytes back from file read. Now at: %"_64BITARG_"u\n", theRecvLen, theOffset);
|
||
|
#endif
|
||
|
if (theReadOffset < theBufferSize)
|
||
|
{
|
||
|
#if RAW_FILE_DEBUGGING
|
||
|
qtss_printf("Flow controlled on file. Waiting for read event\n");
|
||
|
#endif
|
||
|
isBlocked = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
theReadOffset = 0;
|
||
|
Assert(theWriteOffset == 0);
|
||
|
theState = kWritingBufferState;
|
||
|
}
|
||
|
case kWritingBufferState:
|
||
|
{
|
||
|
UInt32 theWrittenLen = 0;
|
||
|
|
||
|
// for debugging purposes, construct an IOVec out of this data
|
||
|
iovec theVec[5];
|
||
|
UInt32 units = (theBufferSize - theWriteOffset) /4;
|
||
|
UInt32 offset = theWriteOffset;
|
||
|
theVec[1].iov_base = (*theFileBufferP) + offset;
|
||
|
theVec[1].iov_len = units;
|
||
|
offset += units;
|
||
|
theVec[2].iov_base = (*theFileBufferP) + offset;
|
||
|
theVec[2].iov_len = units;
|
||
|
offset += units;
|
||
|
theVec[3].iov_base = (*theFileBufferP) + offset;
|
||
|
theVec[3].iov_len = units;
|
||
|
offset += units;
|
||
|
theVec[4].iov_base = (*theFileBufferP) + offset;
|
||
|
theVec[4].iov_len = theBufferSize - offset;
|
||
|
|
||
|
(void)QTSS_WriteV( inParams->inRTSPSession,
|
||
|
theVec, 5,
|
||
|
theBufferSize - theWriteOffset,
|
||
|
&theWrittenLen);
|
||
|
theWriteOffset += theWrittenLen;
|
||
|
#if RAW_FILE_DEBUGGING
|
||
|
qtss_printf("Got %"_U32BITARG_" bytes back from socket write.\n", theWrittenLen);
|
||
|
#endif
|
||
|
if (theWriteOffset < theBufferSize)
|
||
|
{
|
||
|
#if RAW_FILE_DEBUGGING
|
||
|
qtss_printf("Flow controlled on socket. Waiting for write event.\n");
|
||
|
#endif
|
||
|
isBlocked = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Check to see if we're done. If we are, delete stuff and return
|
||
|
if (theOffset == theFileLength)
|
||
|
{
|
||
|
#if RAW_FILE_DEBUGGING
|
||
|
qtss_printf("File transfer complete\n");
|
||
|
#endif
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
theWriteOffset = 0;
|
||
|
Assert(theReadOffset == 0);
|
||
|
theState = kReadingBufferState;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Assert(isBlocked);
|
||
|
|
||
|
// We've reached a blocking condition for some reason.
|
||
|
// Save our state, request an event, and return.
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sReadOffsetAttr, 0, &theReadOffset, sizeof(theReadOffset));
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sWriteOffsetAttr, 0, &theWriteOffset, sizeof(theWriteOffset));
|
||
|
(void)QTSS_SetValue(inParams->inRTSPSession, sStateAttr, 0, &theState, sizeof(theState));
|
||
|
|
||
|
// If we're reading, wait for the file to become readable
|
||
|
if (theState == kReadingBufferState)
|
||
|
(void)QTSS_RequestEvent(*theFileP, QTSS_ReadableEvent);
|
||
|
// If we're writing, wait for the socket to become writable
|
||
|
else
|
||
|
(void)QTSS_RequestEvent(inParams->inRTSPSession, QTSS_WriteableEvent);
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error CloseRTSPSession(QTSS_RTSPSession_Params* inParams)
|
||
|
{
|
||
|
// In this role, the allocated resources are deleted before closing the RTSP session
|
||
|
UInt32 theLen = 0;
|
||
|
|
||
|
// Get our file buffer pointer and delete it
|
||
|
char** theFileBufferP = NULL; // File buffer pointer
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileBufferAttr, 0, (void**)&theFileBufferP, &theLen);
|
||
|
if (theFileBufferP != NULL)
|
||
|
delete [] *theFileBufferP;
|
||
|
|
||
|
QTSS_Object* theFileP = NULL;
|
||
|
// Get our file pointer and delete it
|
||
|
(void)QTSS_GetValuePtr(inParams->inRTSPSession, sFileAttr, 0, (void**)&theFileP, &theLen);
|
||
|
Assert(theFileP != NULL);
|
||
|
Assert(theLen == sizeof(QTSS_Object));
|
||
|
(void)QTSS_CloseFileObject(*theFileP);
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|