00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
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
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
00117
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
00139
00140
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
00153
00154
00155 sort(locQualityList.begin(), locQualityList.end(), qualityPairComparator);
00156
00157
00158
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
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
00193 string locFilename = inRequest->filename;
00194 string locCachedSeekTableFilename = locFilename + ".seektable";
00195
00196
00197 apr_table_t* locCGITable = make_cgi_table (inRequest, locURI->query);
00198
00199
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
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
00221
00222
00223
00224 if (!fileExists(locFilename)) {
00225 delete locOutputMIMETypes;
00226 return HTTP_NOT_FOUND;
00227 }
00228
00229
00230
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
00239 } else if (isCMMLFile(locFilename)) {
00240 locRecomposer = new CMMLRecomposer(locFilename, httpDataSender, inRequest);
00241 } else {
00242
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
00252 delete locRecomposer;
00253 delete locOutputMIMETypes;
00254
00255
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
00276 module AP_MODULE_DECLARE_DATA oggchef_module = {
00277 STANDARD20_MODULE_STUFF,
00278 NULL,
00279 NULL,
00280 NULL,
00281 NULL,
00282 NULL,
00283 AP_REGISTER_HOOK_FUNCTION(oggchef_register_hooks)
00284 };
00285
00286 }
00287
00288
00289