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
00035 #include "stdafx.h"
00036
00037 #include <libOOOggChef/AnnodexRecomposer.h>
00038 #include <libOOOggChef/CMMLRecomposer.h>
00039 #include <libOOOggChef/utils.h>
00040
00041 #include <libOOOgg/libOOOgg.h>
00042 #include <libOOOggSeek/AutoAnxSeekTable.h>
00043
00044 #include <assert.h>
00045
00046 #include <fstream>
00047 #include <iostream>
00048 #include <string>
00049 #include <vector>
00050
00051 using namespace std;
00052
00053
00054 #undef DEBUG
00055
00064 AnnodexRecomposer::AnnodexRecomposer(string inFilename,
00065 BufferWriter inBufferWriter,
00066 void* inBufferWriterUserData,
00067 string inCachedSeekTableFilename)
00068 : mDemuxState(SEEN_NOTHING)
00069 , mDemuxParserState(LOOK_FOR_HEADERS)
00070 , mBufferWriter(inBufferWriter)
00071 , mBufferWriterUserData(inBufferWriterUserData)
00072 {
00073
00074
00075
00076 mFilename = inFilename;
00077 mCachedSeekTableFilename = inCachedSeekTableFilename;
00078 }
00079
00080 AnnodexRecomposer::~AnnodexRecomposer(void)
00081 {
00082 }
00083
00088 bool AnnodexRecomposer::recomposeStreamFrom(double inStartingTimeOffset,
00089 const vector<string>* inWantedMIMETypes)
00090 {
00091 mWantedMIMETypes = inWantedMIMETypes;
00092
00093 #ifdef DEBUG
00094 mDebugFile.open("G:\\Logs\\AnnodexRecomposer.log", ios_base::out);
00095 mDebugFile << "AnnodexRecomposer 1 " << endl;
00096 #endif
00097
00098 static const size_t BUFF_SIZE = 8192;
00099
00100
00101
00102
00103 string locCMMLFilename = mFilename + ".cmml";
00104 if (wantOnlyCMML(mWantedMIMETypes) && fileExists(locCMMLFilename)) {
00105 #ifdef DEBUG
00106 mDebugFile << "Client wants CMML: " + locCMMLFilename + " exists" << endl;
00107 #endif
00108 CMMLRecomposer *locCMMLRecomposer = new CMMLRecomposer(locCMMLFilename,
00109 mBufferWriter, mBufferWriterUserData);
00110 return locCMMLRecomposer->recomposeStreamFrom(inStartingTimeOffset,
00111 inWantedMIMETypes);
00112 }
00113
00114
00115 mRequestedStartTime = (LOOG_UINT64) inStartingTimeOffset * 10000000;
00116
00117
00118 fstream locFile;
00119 locFile.open(mFilename.c_str(), ios_base::in | ios_base::binary);
00120
00121
00122
00123
00124 AutoAnxSeekTable *locSeekTable = new AutoAnxSeekTable(mFilename);
00125 if (mCachedSeekTableFilename != "" && fileExists(mCachedSeekTableFilename)) {
00126 locSeekTable->buildTableFromFile(mCachedSeekTableFilename);
00127 } else {
00128 locSeekTable->buildTable();
00129 }
00130
00131 if (mCachedSeekTableFilename != "" && !fileExists(mCachedSeekTableFilename)) {
00132 locSeekTable->serialiseInto(mCachedSeekTableFilename);
00133 }
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 unsigned long locStartOfBodyOffset = 640 * 1024;
00147
00148 #ifdef DEBUG
00149 mDebugFile << "Filename: " << mFilename << endl;
00150 mDebugFile << "locStartOfBodyOffset: " << locStartOfBodyOffset << endl;
00151 #endif
00152
00153
00154 if (wantOnlyCMML(mWantedMIMETypes)) {
00155 const string CMML_PREAMBLE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<cmml>\n";
00156 mBufferWriter((unsigned char *) CMML_PREAMBLE.c_str(), (unsigned long) CMML_PREAMBLE.length(), mBufferWriterUserData);
00157 }
00158
00159
00160 mDemuxParserState = LOOK_FOR_HEADERS;
00161 {
00162 OggDataBuffer locOggBuffer;
00163 locOggBuffer.registerVirtualCallback(this);
00164
00165 unsigned long locBytesRead = 0;
00166 char *locBuffer = new char[BUFF_SIZE];
00167 while (locBytesRead < locStartOfBodyOffset)
00168 {
00169
00170
00171 unsigned long locBytesToRead =
00172 MIN(locStartOfBodyOffset - locBytesRead, BUFF_SIZE);
00173 locFile.read(locBuffer, locBytesToRead);
00174 unsigned long locBytesReadThisIteration = locFile.gcount();
00175 if (locBytesReadThisIteration <= 0) {
00176 break;
00177 }
00178 locOggBuffer.feed((unsigned char *) locBuffer, locBytesReadThisIteration);
00179 }
00180 }
00181
00182
00183 unsigned long locRequestedStartTimeOffset =
00184 locSeekTable->getStartPos(mRequestedStartTime).second;
00185
00186
00187 locFile.clear();
00188 locFile.seekg(locRequestedStartTimeOffset);
00189
00190 #ifdef DEBUG
00191 mDebugFile << "mRequestedStartTime: " << mRequestedStartTime << endl;
00192 mDebugFile << "locRequestedStartTimeOffset: " << locRequestedStartTimeOffset << endl;
00193 mDebugFile << "Current position: " << locFile.tellg() << endl;
00194 #endif
00195
00196
00197
00198 size_t locCurrentPosition = locFile.tellg();
00199 if (locCurrentPosition == 0) {
00200 #ifdef DEBUG
00201 mDebugFile << "Resetting mDemuxState to SEEN_NOTHING bofore LOOK_FOR_BODY" << endl;
00202 #endif
00203 mDemuxState = SEEN_NOTHING;
00204 }
00205
00206 #ifdef DEBUG
00207 for (set<tSerial_HeadCountPair>::iterator i = mWantedStreamSerialNumbers.begin(); i != mWantedStreamSerialNumbers.end(); i++) {
00208 mDebugFile << "Serialno: " << i->first << endl;
00209 }
00210
00211 mDebugFile << "mDemuxState before LOOK_FOR_BODY is " << mDemuxState << endl;
00212 #endif
00213
00214 mDemuxParserState = LOOK_FOR_BODY;
00215 {
00216 OggDataBuffer locOggBuffer;
00217 locOggBuffer.registerVirtualCallback(this);
00218
00219 char *locBuffer = new char[BUFF_SIZE];
00220 for (;;)
00221 {
00222 locFile.read(locBuffer, BUFF_SIZE);
00223 unsigned long locBytesReadThisIteration = locFile.gcount();
00224 if (locBytesReadThisIteration <= 0) {
00225 break;
00226 }
00227 locOggBuffer.feed((unsigned char *) locBuffer, locBytesReadThisIteration);
00228 }
00229 }
00230
00231
00232 if (wantOnlyCMML(mWantedMIMETypes)) {
00233 const string CMML_POSTAMBLE = "\n</cmml>";
00234 mBufferWriter((unsigned char *) CMML_POSTAMBLE.c_str(), (unsigned long) CMML_POSTAMBLE.length(), mBufferWriterUserData);
00235 }
00236
00237
00238 locFile.close();
00239
00240 #ifdef DEBUG
00241 mDebugFile << "----------------" << endl;
00242 mDebugFile.close();
00243 #endif
00244
00245 return true;
00246 }
00247
00248 bool isAnnodexBOSPage (OggPage *inOggPage)
00249 {
00250 return (
00251 inOggPage->numPackets() == 1
00252 && inOggPage->header()->isBOS()
00253 && strncmp((char*)inOggPage->getPacket(0)->packetData(),
00254 "Annodex\0", 8) == 0
00255 );
00256 }
00257
00258 bool isFisheadPage (OggPage *inOggPage)
00259 {
00260 return (
00261 inOggPage->numPackets() == 1
00262 && inOggPage->header()->isBOS()
00263 && strncmp((char*)inOggPage->getPacket(0)->packetData(),
00264 "fishead\0", 8) == 0
00265 );
00266 }
00267
00268 bool isAnxDataPage (OggPage *inOggPage)
00269 {
00270 return (
00271 inOggPage->numPackets() == 1
00272 && inOggPage->header()->isBOS()
00273 && strncmp((char*)inOggPage->getPacket(0)->packetData(),
00274 "AnxData\0", 8) == 0
00275 );
00276 }
00277
00278 bool isAnnodexEOSPage (OggPage *inOggPage, unsigned long locAnnodexSerialNumber)
00279 {
00280 return (
00281 inOggPage->header()->isEOS()
00282 && inOggPage->header()->StreamSerialNo() == locAnnodexSerialNumber
00283 );
00284 }
00285
00286 unsigned long secondaryHeaders(OggPacket* inPacket, const unsigned short inAnnodexMajorVersion)
00287 {
00288 if (inAnnodexMajorVersion == 2) {
00289
00290 const unsigned short NUM_SEC_HEADERS_OFFSET = 24;
00291
00292 return iLE_Math::charArrToULong(inPacket->packetData() +
00293 NUM_SEC_HEADERS_OFFSET);
00294
00295 } else if (inAnnodexMajorVersion == 3) {
00296
00297
00298
00299
00300 unsigned long locSecondaryHeaders = 0;
00301
00302 char* locPacketData = (char*) inPacket->packetData();
00303 if (memcmp(locPacketData, "\001vorbis", 7) == 0) {
00304 locSecondaryHeaders = 3;
00305 } else if (memcmp(locPacketData, "\200theora", 7) == 0) {
00306 locSecondaryHeaders = 3;
00307 } else if (memcmp(locPacketData, "CMML\0\0\0\0", 8) == 0) {
00308 locSecondaryHeaders = 3;
00309 } else {
00310 locSecondaryHeaders = 3;
00311 }
00312
00313 return locSecondaryHeaders;
00314
00315 } else {
00316
00317 return 0;
00318 }
00319 }
00320
00321 void setPresentationTimeOnAnnodexHeaderPage (OggPage *inOggPage, LOOG_UINT64 inPresentationTime)
00322 {
00323
00324 const unsigned short V2_PRESENTATION_TIME_NUMERATOR_OFFSET = 12;
00325 const unsigned short V2_PRESENTATION_TIME_DENOMINATOR_OFFSET =
00326 V2_PRESENTATION_TIME_NUMERATOR_OFFSET + 8;
00327
00328
00329 const unsigned short V3_PRESENTATION_TIME_NUMERATOR_OFFSET = 12;
00330 const unsigned short V3_PRESENTATION_TIME_DENOMINATOR_OFFSET =
00331 V3_PRESENTATION_TIME_NUMERATOR_OFFSET + 8;
00332
00333
00334
00335 unsigned short locPresentationTimeNumeratorOffset = 0;
00336 unsigned short locPresentationTimeDenominatorOffset = 0;
00337 if (isAnnodexBOSPage(inOggPage)) {
00338
00339 locPresentationTimeNumeratorOffset = V2_PRESENTATION_TIME_NUMERATOR_OFFSET;
00340 locPresentationTimeDenominatorOffset = V2_PRESENTATION_TIME_DENOMINATOR_OFFSET;
00341 } else if (isFisheadPage(inOggPage)) {
00342
00343 locPresentationTimeNumeratorOffset = V3_PRESENTATION_TIME_NUMERATOR_OFFSET;
00344 locPresentationTimeDenominatorOffset = V3_PRESENTATION_TIME_DENOMINATOR_OFFSET;
00345 } else {
00346
00347
00348 return;
00349 }
00350
00351 unsigned char* locPacketData = inOggPage->getPacket(0)->packetData();
00352
00353
00354 unsigned char* locNumeratorPointer = locPacketData + locPresentationTimeNumeratorOffset;
00355 unsigned char* locDenominatorPointer = locPacketData + locPresentationTimeDenominatorOffset;
00356
00357
00358
00359 iLE_Math::Int64ToCharArr(inPresentationTime, locNumeratorPointer);
00360 iLE_Math::Int64ToCharArr(10000000, locDenominatorPointer);
00361
00362
00363 inOggPage->computeAndSetCRCChecksum();
00364 }
00365
00366 #ifdef WIN32
00367 # define strncasecmp _strnicmp
00368 #endif
00369 string mimeType(OggPacket* inPacket, const unsigned short inAnnodexMajorVersion)
00370 {
00371 if (inAnnodexMajorVersion == 2) {
00372 const unsigned short CONTENT_TYPE_OFFSET = 28;
00373
00374 if (strncasecmp((char *) inPacket->packetData() + CONTENT_TYPE_OFFSET,
00375 "Content-Type: ", 14) == 0)
00376 {
00377 const unsigned short MIME_TYPE_OFFSET = 28 + 14;
00378 const unsigned short MAX_MIME_TYPE_LENGTH = 256;
00379 char *locMimeType = new char[MAX_MIME_TYPE_LENGTH];
00380 sscanf((char *) inPacket->packetData() + MIME_TYPE_OFFSET, "%s\r\n", locMimeType);
00381 return locMimeType;
00382 } else {
00383 return NULL;
00384 }
00385 } else if (inAnnodexMajorVersion == 3) {
00386
00387
00388
00389
00390
00391
00392
00393 string locMimeType;
00394
00395 char* locPacketData = (char*) inPacket->packetData();
00396 if (memcmp(locPacketData, "\001vorbis", 7) == 0) {
00397 locMimeType = "audio/x-vorbis";
00398 } else if (memcmp(locPacketData, "\200theora", 7) == 0) {
00399 locMimeType = "video/x-theora";
00400 } else if (memcmp(locPacketData, "CMML\0\0\0\0", 8) == 0) {
00401 locMimeType = "text/x-cmml";
00402 } else if (memcmp(locPacketData, "fisbone\0", 8) == 0) {
00403 locMimeType = "_skeleton";
00404 }
00405
00406 return locMimeType;
00407
00408 } else {
00409
00410 return NULL;
00411 }
00412 }
00413 #ifdef WIN32
00414 # undef strcasecmp
00415 #endif
00416
00417 bool AnnodexRecomposer::acceptOggPage(OggPage* inOggPage)
00418 {
00419 if (mDemuxParserState == LOOK_FOR_HEADERS) {
00420
00421 switch (mDemuxState) {
00422
00423 case SEEN_NOTHING:
00424 if (isAnnodexBOSPage(inOggPage) || isFisheadPage(inOggPage)) {
00425 mDemuxState = SEEN_ANNODEX_BOS;
00426
00427
00428 if (isAnnodexBOSPage(inOggPage)) {
00429 #ifdef DEBUG
00430 mDebugFile << "Found Annodex v2 file" << endl;
00431 #endif
00432 mAnnodexMajorVersion = 2;
00433 } else if (isFisheadPage(inOggPage)) {
00434 #ifdef DEBUG
00435 mDebugFile << "Found Annodex v3 file" << endl;
00436 #endif
00437 mAnnodexMajorVersion = 3;
00438 }
00439
00440
00441 mAnnodexSerialNumber = inOggPage->header()->StreamSerialNo();
00442 mWantedStreamSerialNumbers.insert(make_pair<unsigned long, unsigned long>(mAnnodexSerialNumber, 0));
00443
00444
00445
00446 if (mRequestedStartTime != 0) {
00447 setPresentationTimeOnAnnodexHeaderPage(inOggPage, mRequestedStartTime);
00448 }
00449
00450 if (!wantOnlyPacketBody(mWantedMIMETypes)) {
00451
00452 unsigned char *locRawPageData = inOggPage->createRawPageData();
00453 mBufferWriter(locRawPageData,
00454 inOggPage->pageSize(), mBufferWriterUserData);
00455 delete locRawPageData;
00456 }
00457 } else {
00458
00459
00460 mDemuxState = INVALID;
00461 }
00462 break;
00463
00464 case SEEN_ANNODEX_BOS:
00465 if (isAnnodexEOSPage(inOggPage, mAnnodexSerialNumber)) {
00466 #ifdef DEBUG
00467 mDebugFile << "Seen Annodex skeleton EOS" << endl;
00468 #endif
00469 mDemuxState = SEEN_ANNODEX_EOS;
00470 if (!wantOnlyPacketBody(mWantedMIMETypes)) {
00471 unsigned char *locRawPageData = inOggPage->createRawPageData();
00472 mBufferWriter(locRawPageData,
00473 inOggPage->pageSize(), mBufferWriterUserData);
00474 delete locRawPageData;
00475 }
00476 } else if (( mAnnodexMajorVersion == 2 && isAnxDataPage(inOggPage))
00477 || mAnnodexMajorVersion == 3) {
00478 unsigned long locSerialNumber = inOggPage->header()->StreamSerialNo();
00479 string locMimeType = mimeType(inOggPage->getPacket(0), mAnnodexMajorVersion);
00480
00481 for (unsigned int i = 0; i < mWantedMIMETypes->size(); i++) {
00482 const string locWantedMIMEType = mWantedMIMETypes->at(i);
00483 if ( locWantedMIMEType == locMimeType
00484 || locWantedMIMEType == "*/*"
00485 || locWantedMIMEType == "application/x-annodex") {
00486
00487 tSerial_HeadCountPair locMap;
00488 locMap.first = locSerialNumber;
00489 locMap.second = secondaryHeaders(inOggPage->getPacket(0), mAnnodexMajorVersion);
00490
00491
00492 if ((mWantedStreamSerialNumbers.insert(locMap)).second) {
00493 #ifdef DEBUG
00494 mDebugFile << "Added " << locSerialNumber << " with " << locMap.second << " to mWantedStreamSerialNumbers" << endl;
00495 #endif
00496 }
00497
00498 if (!wantOnlyPacketBody(mWantedMIMETypes)) {
00499 unsigned char *locRawPageData = inOggPage->createRawPageData();
00500 mBufferWriter(locRawPageData,
00501 inOggPage->pageSize(), mBufferWriterUserData);
00502 delete locRawPageData;
00503 }
00504 }
00505 }
00506 } else {
00507
00508
00509 mDemuxState = INVALID;
00510 }
00511 break;
00512
00513 case SEEN_ANNODEX_EOS:
00514 if (mAnnodexMajorVersion == 2) {
00515
00516
00517 for (set<tSerial_HeadCountPair>::iterator i = mWantedStreamSerialNumbers.begin(); i != mWantedStreamSerialNumbers.end(); i++) {
00518 if (i->first == inOggPage->header()->StreamSerialNo()) {
00519 if (i->second >= 1) {
00520 i->second--;
00521 if (wantOnlyPacketBody(mWantedMIMETypes)) {
00522 OggPacket* locPacket = inOggPage->getPacket(0);
00523 mBufferWriter(locPacket->packetData(),
00524 locPacket->packetSize(), mBufferWriterUserData);
00525 } else {
00526 unsigned char *locRawPageData = inOggPage->createRawPageData();
00527 mBufferWriter(locRawPageData,
00528 inOggPage->pageSize(), mBufferWriterUserData);
00529 delete locRawPageData;
00530 }
00531 }
00532 }
00533 }
00534
00535 bool allEmpty = true;
00536 for (set<tSerial_HeadCountPair>::iterator i = mWantedStreamSerialNumbers.begin(); i != mWantedStreamSerialNumbers.end(); i++) {
00537 if (i->second != 0) {
00538 allEmpty = false;
00539 }
00540 }
00541
00542 if (allEmpty) {
00543 mDemuxState = SEEN_ALL_CODEC_HEADERS;
00544 }
00545 } else if (mAnnodexMajorVersion == 3) {
00546
00547 mDemuxState = SEEN_ALL_CODEC_HEADERS;
00548 }
00549 break;
00550 case SEEN_ALL_CODEC_HEADERS:
00551
00552 break;
00553 case INVALID:
00554 break;
00555 default:
00556 break;
00557 }
00558
00559 } else if (mDemuxParserState == LOOK_FOR_BODY) {
00560
00561 switch (mDemuxState) {
00562
00563 case SEEN_NOTHING:
00564 if (isAnnodexBOSPage(inOggPage) || isFisheadPage(inOggPage)) {
00565 mDemuxState = SEEN_ANNODEX_BOS;
00566 } else {
00567
00568
00569 mDemuxState = INVALID;
00570 }
00571 break;
00572
00573 case SEEN_ANNODEX_BOS:
00574 if (isAnnodexEOSPage(inOggPage, mAnnodexSerialNumber)) {
00575 mDemuxState = SEEN_ANNODEX_EOS;
00576 } else if ( (mAnnodexMajorVersion == 2 && isAnxDataPage(inOggPage))
00577 || mAnnodexMajorVersion == 3) {
00578
00579 } else {
00580
00581 mDemuxState = INVALID;
00582 }
00583 break;
00584
00585 case SEEN_ANNODEX_EOS:
00586 mDemuxState = SEEN_ALL_CODEC_HEADERS;
00587
00588 case SEEN_ALL_CODEC_HEADERS:
00589 {
00590
00591 if ((inOggPage->header()->HeaderFlags() & OggPageHeader::BOS) != 0) {
00592 break;
00593 }
00594
00595
00596 for (set<tSerial_HeadCountPair>::iterator i = mWantedStreamSerialNumbers.begin(); i != mWantedStreamSerialNumbers.end(); i++) {
00597 if (i->first == inOggPage->header()->StreamSerialNo()) {
00598 #ifdef DEBUG
00599 mDebugFile << "Outputting page for serialno " << i->first << endl;
00600 #endif
00601 if (wantOnlyPacketBody(mWantedMIMETypes)) {
00602 for (unsigned long j = 0; j < inOggPage->numPackets(); j++) {
00603 OggPacket* locPacket = inOggPage->getPacket(j);
00604 if (locPacket->packetSize() > 0) {
00605 mBufferWriter(locPacket->packetData(),
00606 locPacket->packetSize(), mBufferWriterUserData);
00607 }
00608 }
00609 } else {
00610 unsigned char *locRawPageData = inOggPage->createRawPageData();
00611 mBufferWriter(locRawPageData,
00612 inOggPage->pageSize(), mBufferWriterUserData);
00613 delete locRawPageData;
00614 }
00615 }
00616 }
00617 }
00618 break;
00619 case INVALID:
00620 break;
00621 default:
00622 break;
00623 }
00624
00625 } else {
00626
00627 assert(0);
00628 }
00629
00630
00631 delete inOggPage;
00632 inOggPage = NULL;
00633
00634 return true;
00635 }