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