mod_oggchef.cpp

Go to the documentation of this file.
00001 // ===========================================================================
00002 // Copyright (C) 2004-2005  Zentaro Kavanagh
00003 // Copyright (C) 2004-2005  Commonwealth Scientific and Industrial Research
00004 //                          Organisation (CSIRO) Australia
00005 // 
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions
00008 // are met:
00009 // 
00010 // - Redistributions of source code must retain the above copyright
00011 //   notice, this list of conditions and the following disclaimer.
00012 // 
00013 // - Redistributions in binary form must reproduce the above copyright
00014 //   notice, this list of conditions and the following disclaimer in the
00015 //   documentation and/or other materials provided with the distribution.
00016 // 
00017 // - Neither the name of Zentaro Kavanagh, CSIRO Australia nor the names of
00018 //   contributors may be used to endorse or promote products derived from
00019 //   this software without specific prior written permission.
00020 // 
00021 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 // ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00024 // PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
00025 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00026 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00027 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00028 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00029 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00030 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00031 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 // ===========================================================================
00033 
00034 #include "query_utils.h"
00035 #include "anx_time.h"
00036 #include "apr_stdcall.h"
00037 
00038 #include "httpd.h"
00039 #ifdef WIN32
00040 # undef strtoul /* Otherwise Visual Studio .NET 2003 complains */
00041 #endif
00042 #include "http_config.h"
00043 #include "http_core.h"
00044 #include "http_log.h"
00045 #include "http_protocol.h"
00046 #include "apr_strings.h"
00047 
00048 #include <stdio.h>
00049 #include <string.h>
00050 
00051 #include <libOOOgg/libOOOgg.h>
00052 #include <libOOOgg/dllstuff.h>
00053 #include <libOOOggSeek/AutoAnxSeekTable.h>
00054 #include <libOOOggSeek/AutoOggSeekTable.h>
00055 #include <libOOOggChef/AnnodexRecomposer.h>
00056 #include <libOOOggChef/CMMLRecomposer.h>
00057 #include <libOOOggChef/IRecomposer.h>
00058 #include <libOOOggChef/utils.h>
00059 
00060 #include <algorithm>
00061 #include <iostream>
00062 #include <fstream>
00063 #include <list>
00064 #include <string>
00065 
00066 #undef DEBUG
00067 
00068 
00069 // Higher-order functional programming 101 (bite me Zen :)
00070 
00071 typedef int (C_FUNCTION_POINTER *tIntToInt) (int);
00072 string transformString (const string &inString, tIntToInt inCFunctionToApply)
00073 {
00074         string locString = inString;
00075 
00076         transform(locString.begin(), locString.end(), locString.begin(), inCFunctionToApply);
00077 
00078         return locString;
00079 }
00080 
00081 
00082 bool isAnnodexFile (string locFilename)
00083 {
00084         string locExtension = locFilename.substr(locFilename.length() - 4);
00085         locExtension = transformString(locExtension, tolower);
00086 
00087         return (locExtension == ".anx" || locExtension == ".axv" || locExtension == ".axa");
00088 }
00089 
00090 bool isOggFile (string locFilename)
00091 {
00092         string locExtension = locFilename.substr(locFilename.length() - 4);
00093         locExtension = transformString(locExtension, tolower);
00094 
00095         return (locExtension == ".ogg" || locExtension == ".ogm");
00096 }
00097 
00098 bool isCMMLFile (string locFilename)
00099 {
00100         string locExtension = locFilename.substr(locFilename.length() - 5);
00101         locExtension = transformString(locExtension, tolower);
00102 
00103         return (locExtension == ".cmml");
00104 }
00105 
00106 typedef pair<float, char *> tQualityPair;
00107 
00108 bool qualityPairComparator (const tQualityPair &p1, const tQualityPair &p2)
00109 {
00110         return (p2.first < p1.first);
00111 }
00112 
00113 const vector<string> *preferredOutputMIMETypes(request_rec *inRequest)
00114 {
00115 
00116         // If the user requested the application/ mime type (i.e. the entire
00117         // encapsulated file), then just return everything
00118         string locFilename = inRequest->filename;
00119         if (    (               isAnnodexFile(locFilename)
00120                                 &&      get_accept_quality(inRequest, "application/x-annodex") == 1.0
00121                         )
00122                 ||      (               isOggFile(locFilename)
00123                                 && (    get_accept_quality(inRequest, "application/ogg")   == 1.0
00124                                         ||      get_accept_quality(inRequest, "application/x-ogg") == 1.0)
00125                         )
00126                 ||      (               isCMMLFile(locFilename)
00127                                 && (    get_accept_quality(inRequest, "text/x-cmml")   == 1.0)
00128                         )
00129                 )
00130         {
00131                 vector<string>* locAcceptAllMimeTypes = new vector<string>;
00132                 locAcceptAllMimeTypes->push_back("*/*");
00133                 return const_cast<const vector<string>*> (locAcceptAllMimeTypes);
00134         }
00135 
00136         vector<tQualityPair> locQualityList;
00137         
00138         // TODO: We hardcode the types in here for the moment: we should really
00139         // iterate over the list of Accept: types.  Note that this is in order
00140         // of priority!
00141 
00142 #define MIME_QUALITY_PAIR(s) ( make_pair<float, char *>(get_accept_quality(inRequest, s), s) )
00143         locQualityList.push_back( MIME_QUALITY_PAIR("application/x-annodex") );
00144         locQualityList.push_back( MIME_QUALITY_PAIR("application/ogg") );
00145         locQualityList.push_back( MIME_QUALITY_PAIR("application/x-ogg") );
00146         locQualityList.push_back( MIME_QUALITY_PAIR("audio/x-speex") );
00147         locQualityList.push_back( MIME_QUALITY_PAIR("audio/x-vorbis") );
00148         locQualityList.push_back( MIME_QUALITY_PAIR("text/x-cmml") );
00149         locQualityList.push_back( MIME_QUALITY_PAIR("video/x-theora") );
00150 #undef MIME_QUALITY_PAIR
00151 
00152         // TODO: Implement wanted MIME types given the above list and qualities
00153 
00154         // Sort the list in order of Accept: quality (so 1.0 is first, 0.0 is last)
00155         sort(locQualityList.begin(), locQualityList.end(), qualityPairComparator);
00156 
00157         // Since we have a quality rating now, output only the first (preferred)
00158         // MIME type that we want
00159         vector<string>* locMIMETypes = new vector<string>;
00160         tQualityPair locElement = locQualityList[0];
00161         string locMIMEType = locElement.second;
00162         locMIMETypes->push_back(locMIMEType);
00163 
00164         return const_cast<const vector<string>*> (locMIMETypes);
00165 }
00166 
00167 bool httpDataSender (unsigned char *inBuffer, unsigned long inBufferLength, void *inUserData)
00168 {
00169         request_rec *locRequest = (request_rec *) inUserData;
00170 
00171         ap_rwrite(inBuffer, inBufferLength, locRequest);
00172 
00173         return true;
00174 }
00175 
00176 extern "C" {
00177 
00178 static int AP_MODULE_ENTRY_POINT oggchef_handler(request_rec *inRequest)
00179 {
00180         apr_uri_t *locURI = &(inRequest->parsed_uri);
00181 
00182         // Ignore the request if it's not directed at this module
00183         if (    strcmp(inRequest->handler, "mod_oggchef") != 0
00184                 &&      strcmp(inRequest->handler, "oggchef") != 0) {
00185 #ifdef DEBUG
00186                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, inRequest,
00187                         "mod_oggchef ignoring request: %s", inRequest->handler);
00188 #endif
00189                 return DECLINED;
00190         }
00191 
00192         // Grab the local filename (which is determined by the requested URL)
00193         string locFilename = inRequest->filename;
00194         string locCachedSeekTableFilename = locFilename + ".seektable";
00195 
00196         // Make a name=value table of the CGI query parameters
00197         apr_table_t* locCGITable = make_cgi_table (inRequest, locURI->query);
00198 
00199         // Find out what time we're meant to start serving stuff out at
00200         const char* locRequestedStartTimeAsCString =
00201                 (const char *) apr_table_get (locCGITable, "t");
00202         double locRequestedStartTime;
00203         if (locRequestedStartTimeAsCString) {
00204                 locRequestedStartTime = anx_parse_time(locRequestedStartTimeAsCString);
00205         } else {
00206                 locRequestedStartTime = 0;
00207         }
00208 
00209         // What's the output MIME type requested?
00210         const vector<string>* locOutputMIMETypes = preferredOutputMIMETypes(inRequest);
00211 
00212 #ifdef DEBUG
00213         for (unsigned int i = 0; i < locOutputMIMETypes->size(); i++) {
00214                 string locMIMEType = locOutputMIMETypes->at(i);
00215                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, inRequest,
00216                         "Wanted MIMEs %d: %s", i, locMIMEType.c_str());
00217         }
00218 #endif
00219 
00220         // Check to see if the filename exists; if it doesn't, return a 404 error.
00221         // Note that we should remove this check later when e.g. we want to be able
00222         // to dynamically compose an Annodex file from an existing CMML file, but
00223         // since we don't do that yet, we don't have to worry about it :).
00224         if (!fileExists(locFilename)) {
00225                 delete locOutputMIMETypes;
00226                 return HTTP_NOT_FOUND;
00227         }
00228 
00229         // Poor man's factory: create a new class to dynamically generate the Ogg
00230         // file according to the user's wishes
00231         IRecomposer *locRecomposer = NULL;
00232         if (isAnnodexFile(locFilename)) {
00233                 locRecomposer = new AnnodexRecomposer(locFilename, httpDataSender, inRequest, locCachedSeekTableFilename);
00234                 if (wantOnlyCMML(locOutputMIMETypes)) {
00235                         inRequest->content_type = "text/x-cmml";
00236                 }
00237         } else if (isOggFile(locFilename)) {
00238                 //locRecomposer = new OggRecomposer(locOutputMIMETypes);
00239         } else if (isCMMLFile(locFilename)) {
00240                 locRecomposer = new CMMLRecomposer(locFilename, httpDataSender, inRequest);
00241         } else {
00242                 // We should never get here
00243                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, inRequest,
00244                                           "Couldn't identify filename %s", locFilename.c_str());
00245         }
00246 
00247         if (locRecomposer) {
00248                 bool locRecompositionWasSuccessful = 
00249                         locRecomposer->recomposeStreamFrom(locRequestedStartTime, locOutputMIMETypes);
00250                 if (!locRecompositionWasSuccessful) {
00251                         // TODO: This really isn't the most ideal error handling, but what the hey ...
00252                         delete locRecomposer;
00253                         delete locOutputMIMETypes;
00254                         
00255                         // XXX: Is this a legal return value for this function?
00256                         return HTTP_INTERNAL_SERVER_ERROR;
00257                 }
00258                 delete locRecomposer;
00259         }
00260 
00261         delete locOutputMIMETypes;
00262 
00263         return OK;
00264 }
00265 
00266 
00267 static void AP_MODULE_ENTRY_POINT oggchef_register_hooks(apr_pool_t *)
00268 {
00269     ap_hook_handler(AP_HOOK_HANDLER_FUNCTION(oggchef_handler),
00270                             NULL,
00271                                         NULL,
00272                                         APR_HOOK_MIDDLE);
00273 }
00274 
00275 /* Dispatch list for API hooks */
00276 module AP_MODULE_DECLARE_DATA oggchef_module = {
00277     STANDARD20_MODULE_STUFF, 
00278     NULL,                  /* create per-dir    config structures */
00279     NULL,                  /* merge  per-dir    config structures */
00280     NULL,                  /* create per-server config structures */
00281     NULL,                  /* merge  per-server config structures */
00282     NULL,                  /* table of config file commands       */
00283     AP_REGISTER_HOOK_FUNCTION(oggchef_register_hooks)  /* register hooks */
00284 };
00285 
00286 } /* extern "C" */
00287 
00288 // vi:ts=4
00289 

Generated on Tue Feb 15 14:54:23 2005 for oggdsf by  doxygen 1.3.9