/*
*
* @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: QTSSDemoSMILModule.cpp
Contains:
*/
/*
*/
#include
#include
#include
#include "SafeStdLib.h"
#include
#include
#include
#include "QTSSDemoSMILModule.h"
#include "OSArrayObjectDeleter.h"
#include "StringParser.h"
#include "StrPtrLen.h"
#include "OSFileSource.h"
#include "OSMemory.h"
#include "OSHeaders.h"
#include "ev.h"
#include "QTFile.h"
#include "QTTrack.h"
#include "QTHintTrack.h"
#include "QTSSModuleUtils.h"
#include "OSMutex.h"
#define HTTP_FILE_ASYNC 1
#define HTTP_FILE_DEBUGGING 1
// STATIC DATA
// For processing the requests
static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS/2.0\r\nContent-Type: video/quicktime\r\n\r\n";
static QTSS_PrefsObject sPrefs = NULL;
static StrPtrLen sPathSeparator("/");
static StrPtrLen sRTSPUrlPrefix("rtsp://");
static StrPtrLen sRefMovieBufPrefix("rtsptext\r");
static StrPtrLen sRespHeaderPrefix("HTTP/1.0 200 OK\r\nServer: QTSS/2.0\r\nConnection: Close");
static StrPtrLen sContentLengthHeaderTag("\r\nContent-Length: ");
static StrPtrLen sContentTypeHeaderTag("\r\nContent-Type: ");
static StrPtrLen sConnectionKeepAliveTag("Keep-Alive");
static StrPtrLen sQuickTimeMimeType("video/quicktime");
static StrPtrLen sUnknownMimeType("application/unknown");
static StrPtrLen sGifMimeType("image/gif");
static StrPtrLen sSdpMimeType("application/sdp");
static StrPtrLen sSmilMimeType("application/smil");
static StrPtrLen sQTSuffix("qt");
static StrPtrLen sMovSuffix("mov");
static StrPtrLen sGifSuffix("gif");
static StrPtrLen sSdpSuffix("sdp");
static StrPtrLen sSmiSuffix("smi");
static StrPtrLen sSmilSuffix("smil");
// FUNCTIONS
static QTSS_Error QTSSDemoSMILDispatch(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 FilterRequest(QTSS_Filter_Params* inParams);
// For processing the requests
//Demo stuff
struct HitCount {
char url[512];
int hitcount;
};
static HitCount gHitcountArray[100] = {};
//protos
void CountHit(char* url);
void GenerateHotHitSMIL(char* buffer);
int HitCountCompare(const void * hitCount1, const void *hitCount2);
QTSS_Error CountRequest( QTSS_RTSPRequestObject inRTSPRequest, QTSS_ClientSessionObject inClientSession,
QTSS_RTSPSessionObject inRTSPSession, QTSS_CliSesClosingReason *inCloseReasonPtr );
void InitHitCountFromFile();
//funcs
void InitHitCountFromFile()
{
FILE* hitfile = fopen("hitcount.txt", "r");
if (hitfile == NULL) return;
int c = 0;
int i = 0;
while ( (c = fscanf(hitfile, "%s %d", gHitcountArray[i].url, &gHitcountArray[i].hitcount)) == 2 )
{
qtss_printf("%s %d\n", gHitcountArray[i].url, gHitcountArray[i].hitcount);
i++;
}
fclose(hitfile);
}
void WriteHitCountToFile()
{
FILE* hitfile = fopen("hitcount.txt", "w");
int i=0;
for (i=0;gHitcountArray[i].url[0] && i<512; i++)
{
qtss_fprintf(hitfile, "%s %d\n", gHitcountArray[i].url, gHitcountArray[i].hitcount);
}
fclose(hitfile);
}
void CountHit(char* url)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Counting Hit for \"%s\"\n", url);
#endif
if ( url == NULL )
return;
int i=0;
for (i=0;gHitcountArray[i].url[0] && i<512; i++)
{
if ( strcmp(url, gHitcountArray[i].url ) == 0)
{
gHitcountArray[i].hitcount++;
qsort(&gHitcountArray, i+1, sizeof(HitCount), &HitCountCompare);
return;
}
}
::strcpy(gHitcountArray[i].url, url);
gHitcountArray[i].hitcount++;
qsort(&gHitcountArray, i+1, sizeof(HitCount), &HitCountCompare);
}
int HitCountCompare(const void * hitCount1, const void *hitCount2)
{
return ((const HitCount*)hitCount2)->hitcount - ((const HitCount*)hitCount1)->hitcount;
}
QTSS_Error ClientSessionClosing(QTSS_ClientSessionClosing_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("ClientSessionClosing\n");
#endif
return CountRequest(NULL, inParams->inClientSession, NULL, &inParams->inReason);
}
QTSS_Error CountRequest( QTSS_RTSPRequestObject inRTSPRequest, QTSS_ClientSessionObject inClientSession,
QTSS_RTSPSessionObject inRTSPSession, QTSS_CliSesClosingReason *inCloseReasonPtr )
{
char urlBuf[256] = { 0 };
StrPtrLen url(urlBuf, 256 -1);
(void)QTSS_GetValue(inClientSession, qtssCliSesFullURL, 0, url.Ptr, &url.Len);
CountHit( url.Ptr );
WriteHitCountToFile();
return QTSS_NoErr;
}
void GenerateHotHitSMIL(char* buffer)
{
char smilTemplate[8192] = {};
char* templateCursor = smilTemplate;
char* bufferCursor = buffer;
FILE* smilTemplateFile = fopen("template.smil", "r");
if (smilTemplateFile != NULL)
{
int len = fread(smilTemplate, sizeof(char), sizeof(smilTemplate), smilTemplateFile);
smilTemplate[len] = '\0';
fclose(smilTemplateFile);
}
else
{
strcpy(smilTemplate, "\n"
" \n"
" \n"
" \n"
" \n"
" \n"
" \n"
" \n"
" \n"
" \n"
"\n");
}
int hitNum = 0;
char* p = NULL;
while ( (p = strstr(templateCursor, "%s")) != 0 )
{
char saveCh = p[2];
p[2] = '\0';
int len = qtss_sprintf(bufferCursor, templateCursor, gHitcountArray[hitNum++].url);
p[2] = saveCh;
bufferCursor += len;
templateCursor = &p[2];
}
strcat(bufferCursor, templateCursor);
#if HTTP_FILE_DEBUGGING
qtss_printf("smil generated:\n%s\n", buffer);
#endif
}
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSDemoSMILModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSDemoSMILDispatch);
}
QTSS_Error QTSSDemoSMILDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("QTSSDemoSMILDispatch\n");
#endif
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPFilter_Role:
return FilterRequest(&inParams->rtspFilterParams);
case QTSS_ClientSessionClosing_Role:
return ClientSessionClosing(&inParams->clientSessionClosingParams);
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Register\n");
#endif
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTSPFilter_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
// Tell the server our name!
static char* sModuleName = "QTSSDemoSMILModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("Initialize\n");
#endif
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
// Get prefs object
sPrefs = inParams->inPrefs;
InitHitCountFromFile();
return QTSS_NoErr;
}
QTSS_Error FilterRequest(QTSS_Filter_Params* inParams)
{
#if HTTP_FILE_DEBUGGING
qtss_printf("FilterRequest\n");
#endif
static Bool16 sFalse = false;
QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest;
// Initial state.
StrPtrLen theFullRequest;
StrPtrLen reqMethod;
StrPtrLen reqStr;
StrPtrLen httpVersion;
(void)QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len);
StringParser fullRequest(&theFullRequest);
// Parsing the HTTP request
fullRequest.ConsumeWord(&reqMethod);
if ( !(reqMethod.Equal(StrPtrLen("GET")) || reqMethod.Equal(StrPtrLen("HEAD"))) )
// It's not a "Get" or a "Head" request
return QTSS_NoErr;
fullRequest.ConsumeWhitespace();
if ( !fullRequest.Expect('/') )
// Improperly formed request
return QTSS_NoErr;
fullRequest.ConsumeUntil(&reqStr, StringParser::sEOLWhitespaceMask);
if( reqStr.Len == 0 )
//if a file or directory name is not given, return
return QTSS_NoErr;
if ( !reqStr.Equal(StrPtrLen("Popular.smil")) )
return QTSS_NoErr;
// If it's a "Head" request send the Head response header back and just return
if ( reqMethod.Equal(StrPtrLen("HEAD")) )
{
QTSS_Write(theRequest, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
return QTSS_NoErr;
}
// Create a buffer to store data.
char theFileBuffer[8192];
char contentLength[256];
// 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
(void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &sFalse, sizeof(sFalse));
#if HTTP_FILE_DEBUGGING
qtss_printf("Creating a smil file\n");
#endif
// Create a ref movie buffer for the single file. It is of the form:
// rtsptext\r
// rtsp://servername/filepath
char smilFileBuf[8192] = {0};
GenerateHotHitSMIL(smilFileBuf);
qtss_sprintf(contentLength, "%"_U32BITARG_"", (UInt32) ::strlen(smilFileBuf));
// Allocate memory for theFileBuffer
// Write the HTTP header prefix into the buffer
::strcpy(theFileBuffer, sRespHeaderPrefix.Ptr);
::strcat(theFileBuffer, sContentLengthHeaderTag.Ptr);
// Write the remaining part of the HTTP header into the file buffer
::strcat(theFileBuffer, contentLength);
::strcat(theFileBuffer, sContentTypeHeaderTag.Ptr);
::strcat(theFileBuffer, sSmilMimeType.Ptr);
::strcat(theFileBuffer, "\r\n\r\n");
// Write the smil file created above to the file buffer
::strcat(theFileBuffer, smilFileBuf);
// Write the contents of the file buffer to the request stream and return
QTSS_Write(theRequest, theFileBuffer, strlen(theFileBuffer), NULL, 0);
#if HTTP_FILE_DEBUGGING
qtss_printf("Wrote the smil file to the request stream. Successful!\n");
#endif
return QTSS_NoErr;
}