00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "MatroskaFileParser.hh"
00022 #include "MatroskaDemuxedTrack.hh"
00023 #include <ByteStreamFileSource.hh>
00024
00026
00027 class CuePoint {
00028 public:
00029 CuePoint(double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster);
00030 virtual ~CuePoint();
00031
00032 static void addCuePoint(CuePoint*& root, double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster,
00033 Boolean& needToReviseBalanceOfParent);
00034
00035
00036
00037 Boolean lookup(double& cueTime, u_int64_t& resultClusterOffsetInFile, unsigned& resultBlockNumWithinCluster);
00038
00039 static void fprintf(FILE* fid, CuePoint* cuePoint);
00040
00041 private:
00042
00043 CuePoint* fSubTree[2];
00044 CuePoint* left() const { return fSubTree[0]; }
00045 CuePoint* right() const { return fSubTree[1]; }
00046 char fBalance;
00047
00048 static void rotate(unsigned direction, CuePoint*& root);
00049
00050 double fCueTime;
00051 u_int64_t fClusterOffsetInFile;
00052 unsigned fBlockNumWithinCluster;
00053 };
00054
00055 UsageEnvironment& operator<<(UsageEnvironment& env, const CuePoint* cuePoint);
00056
00057
00059
00060 void MatroskaFile
00061 ::createNew(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData,
00062 char const* preferredLanguage) {
00063 new MatroskaFile(env, fileName, onCreation, onCreationClientData, preferredLanguage);
00064 }
00065
00066 MatroskaFile::MatroskaFile(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData,
00067 char const* preferredLanguage)
00068 : Medium(env),
00069 fFileName(strDup(fileName)), fOnCreation(onCreation), fOnCreationClientData(onCreationClientData),
00070 fPreferredLanguage(strDup(preferredLanguage)),
00071 fTimecodeScale(1000000), fSegmentDuration(0.0), fSegmentDataOffset(0), fClusterOffset(0), fCuesOffset(0), fCuePoints(NULL),
00072 fChosenVideoTrackNumber(0), fChosenAudioTrackNumber(0), fChosenSubtitleTrackNumber(0) {
00073 fDemuxesTable = HashTable::create(ONE_WORD_HASH_KEYS);
00074
00075
00076 fParserForInitialization
00077 = new MatroskaFileParser(*this, ByteStreamFileSource::createNew(envir(), fileName),
00078 handleEndOfTrackHeaderParsing, this, NULL);
00079 }
00080
00081 MatroskaFile::~MatroskaFile() {
00082 delete fParserForInitialization;
00083 delete fCuePoints;
00084
00085
00086 MatroskaDemux* demux;
00087 while ((demux = (MatroskaDemux*)fDemuxesTable->RemoveNext()) != NULL) {
00088 delete demux;
00089 }
00090 delete fDemuxesTable;
00091
00092 delete[] (char*)fPreferredLanguage;
00093 delete[] (char*)fFileName;
00094 }
00095
00096 void MatroskaFile::handleEndOfTrackHeaderParsing(void* clientData) {
00097 ((MatroskaFile*)clientData)->handleEndOfTrackHeaderParsing();
00098 }
00099
00100 class TrackChoiceRecord {
00101 public:
00102 unsigned trackNumber;
00103 u_int8_t trackType;
00104 unsigned choiceFlags;
00105 };
00106
00107 void MatroskaFile::handleEndOfTrackHeaderParsing() {
00108
00109
00110
00111
00112
00113
00114
00115
00116 unsigned numTracks = fTracks.numTracks();
00117 if (numTracks > 0) {
00118 TrackChoiceRecord* trackChoice = new TrackChoiceRecord[numTracks];
00119 unsigned numEnabledTracks = 0;
00120 TrackTable::Iterator iter(fTracks);
00121 MatroskaTrack* track;
00122 while ((track = iter.next()) != NULL) {
00123 if (!track->isEnabled || track->trackType == 0 || track->codecID == NULL) continue;
00124
00125 trackChoice[numEnabledTracks].trackNumber = track->trackNumber;
00126 trackChoice[numEnabledTracks].trackType = track->trackType;
00127
00128
00129 unsigned choiceFlags = 0;
00130 if (fPreferredLanguage != NULL && track->language != NULL && strcmp(fPreferredLanguage, track->language) == 0) {
00131
00132 choiceFlags |= 1;
00133 }
00134 if (track->isForced) {
00135 choiceFlags |= 4;
00136 } else if (track->isDefault) {
00137 choiceFlags |= 2;
00138 }
00139 trackChoice[numEnabledTracks].choiceFlags = choiceFlags;
00140
00141 ++numEnabledTracks;
00142 }
00143
00144
00145 for (u_int8_t trackType = 0x01; trackType != MATROSKA_TRACK_TYPE_OTHER; trackType <<= 1) {
00146 int bestNum = -1;
00147 int bestChoiceFlags = -1;
00148 for (unsigned i = 0; i < numEnabledTracks; ++i) {
00149 if (trackChoice[i].trackType == trackType && (int)trackChoice[i].choiceFlags > bestChoiceFlags) {
00150 bestNum = i;
00151 bestChoiceFlags = (int)trackChoice[i].choiceFlags;
00152 }
00153 }
00154 if (bestChoiceFlags >= 0) {
00155 if (trackType == MATROSKA_TRACK_TYPE_VIDEO) fChosenVideoTrackNumber = trackChoice[bestNum].trackNumber;
00156 else if (trackType == MATROSKA_TRACK_TYPE_AUDIO) fChosenAudioTrackNumber = trackChoice[bestNum].trackNumber;
00157 else fChosenSubtitleTrackNumber = trackChoice[bestNum].trackNumber;
00158 }
00159 }
00160
00161 delete[] trackChoice;
00162 }
00163
00164 #ifdef DEBUG
00165 if (fChosenVideoTrackNumber > 0) fprintf(stderr, "Chosen video track: #%d\n", fChosenVideoTrackNumber); else fprintf(stderr, "No chosen video track\n");
00166 if (fChosenAudioTrackNumber > 0) fprintf(stderr, "Chosen audio track: #%d\n", fChosenAudioTrackNumber); else fprintf(stderr, "No chosen audio track\n");
00167 if (fChosenSubtitleTrackNumber > 0) fprintf(stderr, "Chosen subtitle track: #%d\n", fChosenSubtitleTrackNumber); else fprintf(stderr, "No chosen subtitle track\n");
00168 #endif
00169
00170
00171 delete fParserForInitialization; fParserForInitialization = NULL;
00172
00173
00174 if (fOnCreation != NULL) (*fOnCreation)(this, fOnCreationClientData);
00175 }
00176
00177 MatroskaDemux* MatroskaFile::newDemux() {
00178 MatroskaDemux* demux = new MatroskaDemux(*this);
00179 fDemuxesTable->Add((char const*)demux, demux);
00180
00181 return demux;
00182 }
00183
00184 void MatroskaFile::removeDemux(MatroskaDemux* demux) {
00185 fDemuxesTable->Remove((char const*)demux);
00186 }
00187
00188 float MatroskaFile::fileDuration() {
00189 if (fCuePoints == NULL) return 0.0;
00190
00191 return segmentDuration()*(timecodeScale()/1000000000.0f);
00192 }
00193
00194 void MatroskaFile::addCuePoint(double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster) {
00195 Boolean dummy = False;
00196 CuePoint::addCuePoint(fCuePoints, cueTime, clusterOffsetInFile, blockNumWithinCluster, dummy);
00197 }
00198
00199 Boolean MatroskaFile::lookupCuePoint(double& cueTime, u_int64_t& resultClusterOffsetInFile, unsigned& resultBlockNumWithinCluster) {
00200 if (fCuePoints == NULL) return False;
00201
00202 (void)fCuePoints->lookup(cueTime, resultClusterOffsetInFile, resultBlockNumWithinCluster);
00203 return True;
00204 }
00205
00206 void MatroskaFile::printCuePoints(FILE* fid) {
00207 CuePoint::fprintf(fid, fCuePoints);
00208 }
00209
00210
00212
00213 MatroskaFile::TrackTable::TrackTable()
00214 : fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
00215 }
00216
00217 MatroskaFile::TrackTable::~TrackTable() {
00218
00219 MatroskaTrack* track;
00220 while ((track = (MatroskaTrack*)fTable->RemoveNext()) != NULL) {
00221 delete track;
00222 }
00223 delete fTable;
00224 }
00225
00226 void MatroskaFile::TrackTable::add(MatroskaTrack* newTrack, unsigned trackNumber) {
00227 if (newTrack != NULL && newTrack->trackNumber != 0) fTable->Remove((char const*)newTrack->trackNumber);
00228 MatroskaTrack* existingTrack = (MatroskaTrack*)fTable->Add((char const*)trackNumber, newTrack);
00229 delete existingTrack;
00230 }
00231
00232 MatroskaTrack* MatroskaFile::TrackTable::lookup(unsigned trackNumber) {
00233 return (MatroskaTrack*)fTable->Lookup((char const*)trackNumber);
00234 }
00235
00236 unsigned MatroskaFile::TrackTable::numTracks() const { return fTable->numEntries(); }
00237
00238 MatroskaFile::TrackTable::Iterator::Iterator(MatroskaFile::TrackTable& ourTable)
00239 : fOurTable(ourTable) {
00240 fIter = HashTable::Iterator::create(*(ourTable.fTable));
00241 }
00242
00243 MatroskaFile::TrackTable::Iterator::~Iterator() {
00244 delete fIter;
00245 }
00246
00247 MatroskaTrack* MatroskaFile::TrackTable::Iterator::next() {
00248 char const* key;
00249 return (MatroskaTrack*)fIter->next(key);
00250 }
00251
00252
00254
00255 MatroskaTrack::MatroskaTrack()
00256 : trackNumber(0), trackType(0),
00257 isEnabled(True), isDefault(True), isForced(False),
00258 defaultDuration(0),
00259 name(NULL), language(NULL), codecID(NULL),
00260 samplingFrequency(0), numChannels(2), mimeType(""),
00261 codecPrivateSize(0), codecPrivate(NULL), headerStrippedBytesSize(0), headerStrippedBytes(NULL),
00262 subframeSizeSize(0), durationImbalance(0) {
00263 prevPresentationTime.tv_sec = 0; prevPresentationTime.tv_usec = 0;
00264 }
00265
00266 MatroskaTrack::~MatroskaTrack() {
00267 delete[] name; delete[] language; delete[] codecID;
00268 delete[] codecPrivate;
00269 delete[] headerStrippedBytes;
00270 }
00271
00272
00274
00275 MatroskaDemux::MatroskaDemux(MatroskaFile& ourFile)
00276 : Medium(ourFile.envir()),
00277 fOurFile(ourFile), fDemuxedTracksTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
00278 fOurParser = new MatroskaFileParser(ourFile, ByteStreamFileSource::createNew(envir(), ourFile.fileName()),
00279 handleEndOfFile, this, this);
00280 }
00281
00282 MatroskaDemux::~MatroskaDemux() {
00283
00284 handleEndOfFile();
00285
00286
00287
00288 delete fDemuxedTracksTable;
00289
00290 delete fOurParser;
00291 fOurFile.removeDemux(this);
00292 }
00293
00294 FramedSource* MatroskaDemux::newDemuxedTrack(unsigned trackNumber) {
00295 FramedSource* track = new MatroskaDemuxedTrack(envir(), trackNumber, *this);
00296 fDemuxedTracksTable->Add((char const*)trackNumber, track);
00297 return track;
00298 }
00299
00300 MatroskaDemuxedTrack* MatroskaDemux::lookupDemuxedTrack(unsigned trackNumber) {
00301 return (MatroskaDemuxedTrack*)fDemuxedTracksTable->Lookup((char const*)trackNumber);
00302 }
00303
00304 void MatroskaDemux::removeTrack(unsigned trackNumber) {
00305 fDemuxedTracksTable->Remove((char const*)trackNumber);
00306 if (fDemuxedTracksTable->numEntries() == 0) {
00307
00308 delete this;
00309 }
00310 }
00311
00312 void MatroskaDemux::continueReading() {
00313 fOurParser->continueParsing();
00314 }
00315
00316 void MatroskaDemux::seekToTime(double& seekNPT) {
00317 if (fOurParser != NULL) fOurParser->seekToTime(seekNPT);
00318 }
00319
00320 void MatroskaDemux::handleEndOfFile(void* clientData) {
00321 ((MatroskaDemux*)clientData)->handleEndOfFile();
00322 }
00323
00324 void MatroskaDemux::handleEndOfFile() {
00325
00326
00327
00328 unsigned numTracks = fDemuxedTracksTable->numEntries();
00329 if (numTracks == 0) return;
00330 MatroskaDemuxedTrack** tracks = new MatroskaDemuxedTrack*[numTracks];
00331
00332 HashTable::Iterator* iter = HashTable::Iterator::create(*fDemuxedTracksTable);
00333 unsigned i, trackNumber;
00334 for (i = 0; i < numTracks; ++i) {
00335 tracks[i] = (MatroskaDemuxedTrack*)iter->next((char const*&)trackNumber);
00336 }
00337 delete iter;
00338
00339 for (i = 0; i < numTracks; ++i) {
00340 if (tracks[i] == NULL) continue;
00341 FramedSource::handleClosure(tracks[i]);
00342 }
00343
00344 delete[] tracks;
00345 }
00346
00347
00349
00350 CuePoint::CuePoint(double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster)
00351 : fBalance(0),
00352 fCueTime(cueTime), fClusterOffsetInFile(clusterOffsetInFile), fBlockNumWithinCluster(blockNumWithinCluster - 1) {
00353 fSubTree[0] = fSubTree[1] = NULL;
00354 }
00355
00356 CuePoint::~CuePoint() {
00357 delete fSubTree[0]; delete fSubTree[1];
00358 }
00359
00360 #ifndef ABS
00361 #define ABS(x) (x)<0 ? -(x) : (x)
00362 #endif
00363
00364 void CuePoint::addCuePoint(CuePoint*& root, double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster,
00365 Boolean& needToReviseBalanceOfParent) {
00366 needToReviseBalanceOfParent = False;
00367
00368 if (root == NULL) {
00369 root = new CuePoint(cueTime, clusterOffsetInFile, blockNumWithinCluster);
00370 needToReviseBalanceOfParent = True;
00371 } else if (cueTime == root->fCueTime) {
00372
00373 root->fClusterOffsetInFile = clusterOffsetInFile;
00374 root->fBlockNumWithinCluster = blockNumWithinCluster - 1;
00375 } else {
00376
00377 int direction = cueTime > root->fCueTime;
00378 Boolean needToReviseOurBalance = False;
00379 addCuePoint(root->fSubTree[direction], cueTime, clusterOffsetInFile, blockNumWithinCluster, needToReviseOurBalance);
00380
00381 if (needToReviseOurBalance) {
00382
00383 if (root->fBalance == 0) {
00384
00385 root->fBalance = -1 + 2*direction;
00386 needToReviseBalanceOfParent = True;
00387 } else if (root->fBalance == 1 - 2*direction) {
00388
00389 root->fBalance = 0;
00390 } else {
00391
00392 if (root->fSubTree[direction]->fBalance == -1 + 2*direction) {
00393
00394 root->fBalance = root->fSubTree[direction]->fBalance = 0;
00395 rotate(1-direction, root);
00396 } else {
00397
00398 char newParentCurBalance = root->fSubTree[direction]->fSubTree[1-direction]->fBalance;
00399 if (newParentCurBalance == 1 - 2*direction) {
00400 root->fBalance = 0;
00401 root->fSubTree[direction]->fBalance = -1 + 2*direction;
00402 } else if (newParentCurBalance == 0) {
00403 root->fBalance = 0;
00404 root->fSubTree[direction]->fBalance = 0;
00405 } else {
00406 root->fBalance = 1 - 2*direction;
00407 root->fSubTree[direction]->fBalance = 0;
00408 }
00409 rotate(direction, root->fSubTree[direction]);
00410
00411 root->fSubTree[direction]->fBalance = 0;
00412 rotate(1-direction, root);
00413 }
00414 }
00415 }
00416 }
00417 }
00418
00419 Boolean CuePoint::lookup(double& cueTime, u_int64_t& resultClusterOffsetInFile, unsigned& resultBlockNumWithinCluster) {
00420 if (cueTime < fCueTime) {
00421 if (left() == NULL) {
00422 resultClusterOffsetInFile = 0;
00423 resultBlockNumWithinCluster = 0;
00424 return False;
00425 } else {
00426 return left()->lookup(cueTime, resultClusterOffsetInFile, resultBlockNumWithinCluster);
00427 }
00428 } else {
00429 if (right() == NULL || !right()->lookup(cueTime, resultClusterOffsetInFile, resultBlockNumWithinCluster)) {
00430
00431 cueTime = fCueTime;
00432 resultClusterOffsetInFile = fClusterOffsetInFile;
00433 resultBlockNumWithinCluster = fBlockNumWithinCluster;
00434 }
00435 return True;
00436 }
00437 }
00438
00439 void CuePoint::fprintf(FILE* fid, CuePoint* cuePoint) {
00440 if (cuePoint != NULL) {
00441 ::fprintf(fid, "[");
00442 fprintf(fid, cuePoint->left());
00443
00444 ::fprintf(fid, ",%.1f{%d},", cuePoint->fCueTime, cuePoint->fBalance);
00445
00446 fprintf(fid, cuePoint->right());
00447 ::fprintf(fid, "]");
00448 }
00449 }
00450
00451 void CuePoint::rotate(unsigned direction, CuePoint*& root) {
00452 CuePoint* pivot = root->fSubTree[1-direction];
00453 root->fSubTree[1-direction] = pivot->fSubTree[direction];
00454 pivot->fSubTree[direction] = root;
00455 root = pivot;
00456 }