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