ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

common/quicktime.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 //
00024 // Heavily based on ffmpeg code.
00025 //
00026 // Copyright (c) 2001 Fabrice Bellard.
00027 // First version by Francois Revol revol@free.fr
00028 // Seek function by Gael Chardon gael.dev@4now.net
00029 //
00030 
00031 #include "common/debug.h"
00032 #include "common/endian.h"
00033 #include "common/macresman.h"
00034 #include "common/memstream.h"
00035 #include "common/quicktime.h"
00036 #include "common/textconsole.h"
00037 #include "common/util.h"
00038 #include "common/zlib.h"
00039 
00040 namespace Common {
00041 
00043 // QuickTimeParser
00045 
00046 QuickTimeParser::QuickTimeParser() {
00047     _beginOffset = 0;
00048     _fd = nullptr;
00049     _scaleFactorX = 1;
00050     _scaleFactorY = 1;
00051     _resFork = new MacResManager();
00052     _disposeFileHandle = DisposeAfterUse::YES;
00053 
00054     initParseTable();
00055 }
00056 
00057 QuickTimeParser::~QuickTimeParser() {
00058     close();
00059     delete _resFork;
00060 }
00061 
00062 bool QuickTimeParser::parseFile(const String &filename) {
00063     if (!_resFork->open(filename) || !_resFork->hasDataFork())
00064         return false;
00065 
00066     _foundMOOV = false;
00067     _disposeFileHandle = DisposeAfterUse::YES;
00068 
00069     Atom atom = { 0, 0, 0 };
00070 
00071     if (_resFork->hasResFork()) {
00072         // Search for a 'moov' resource
00073         MacResIDArray idArray = _resFork->getResIDArray(MKTAG('m', 'o', 'o', 'v'));
00074 
00075         if (!idArray.empty())
00076             _fd = _resFork->getResource(MKTAG('m', 'o', 'o', 'v'), idArray[0]);
00077 
00078         if (_fd) {
00079             atom.size = _fd->size();
00080             if (readDefault(atom) < 0 || !_foundMOOV)
00081                 return false;
00082         }
00083 
00084         delete _fd;
00085     }
00086 
00087     _fd = _resFork->getDataFork();
00088     atom.size = _fd->size();
00089 
00090     if (readDefault(atom) < 0 || !_foundMOOV)
00091         return false;
00092 
00093     init();
00094     return true;
00095 }
00096 
00097 bool QuickTimeParser::parseStream(SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) {
00098     _fd = stream;
00099     _foundMOOV = false;
00100     _disposeFileHandle = disposeFileHandle;
00101 
00102     Atom atom = { 0, 0, 0xffffffff };
00103 
00104     if (readDefault(atom) < 0 || !_foundMOOV) {
00105         close();
00106         return false;
00107     }
00108 
00109     init();
00110     return true;
00111 }
00112 
00113 void QuickTimeParser::init() {
00114     for (uint32 i = 0; i < _tracks.size(); i++) {
00115         // Remove unknown/unhandled tracks
00116         if (_tracks[i]->codecType == CODEC_TYPE_MOV_OTHER) {
00117             delete _tracks[i];
00118             _tracks.remove_at(i);
00119             i--;
00120         } else {
00121             // If this track doesn't have a declared scale, use the movie scale
00122             if (_tracks[i]->timeScale == 0)
00123                 _tracks[i]->timeScale = _timeScale;
00124 
00125             // If this track doesn't have an edit list (like in MPEG-4 files),
00126             // fake an entry of one edit that takes up the entire sample
00127             if (_tracks[i]->editList.size() == 0) {
00128                 _tracks[i]->editList.resize(1);
00129                 _tracks[i]->editList[0].trackDuration = _tracks[i]->duration;
00130                 _tracks[i]->editList[0].timeOffset = 0;
00131                 _tracks[i]->editList[0].mediaTime = 0;
00132                 _tracks[i]->editList[0].mediaRate = 1;
00133             }
00134         }
00135     }
00136 }
00137 
00138 void QuickTimeParser::initParseTable() {
00139     static const ParseTable p[] = {
00140         { &QuickTimeParser::readDefault, MKTAG('d', 'i', 'n', 'f') },
00141         { &QuickTimeParser::readLeaf,    MKTAG('d', 'r', 'e', 'f') },
00142         { &QuickTimeParser::readDefault, MKTAG('e', 'd', 't', 's') },
00143         { &QuickTimeParser::readELST,    MKTAG('e', 'l', 's', 't') },
00144         { &QuickTimeParser::readHDLR,    MKTAG('h', 'd', 'l', 'r') },
00145         { &QuickTimeParser::readLeaf,    MKTAG('m', 'd', 'a', 't') },
00146         { &QuickTimeParser::readMDHD,    MKTAG('m', 'd', 'h', 'd') },
00147         { &QuickTimeParser::readDefault, MKTAG('m', 'd', 'i', 'a') },
00148         { &QuickTimeParser::readDefault, MKTAG('m', 'i', 'n', 'f') },
00149         { &QuickTimeParser::readMOOV,    MKTAG('m', 'o', 'o', 'v') },
00150         { &QuickTimeParser::readMVHD,    MKTAG('m', 'v', 'h', 'd') },
00151         { &QuickTimeParser::readLeaf,    MKTAG('s', 'm', 'h', 'd') },
00152         { &QuickTimeParser::readDefault, MKTAG('s', 't', 'b', 'l') },
00153         { &QuickTimeParser::readSTCO,    MKTAG('s', 't', 'c', 'o') },
00154         { &QuickTimeParser::readSTSC,    MKTAG('s', 't', 's', 'c') },
00155         { &QuickTimeParser::readSTSD,    MKTAG('s', 't', 's', 'd') },
00156         { &QuickTimeParser::readSTSS,    MKTAG('s', 't', 's', 's') },
00157         { &QuickTimeParser::readSTSZ,    MKTAG('s', 't', 's', 'z') },
00158         { &QuickTimeParser::readSTTS,    MKTAG('s', 't', 't', 's') },
00159         { &QuickTimeParser::readTKHD,    MKTAG('t', 'k', 'h', 'd') },
00160         { &QuickTimeParser::readTRAK,    MKTAG('t', 'r', 'a', 'k') },
00161         { &QuickTimeParser::readLeaf,    MKTAG('u', 'd', 't', 'a') },
00162         { &QuickTimeParser::readLeaf,    MKTAG('v', 'm', 'h', 'd') },
00163         { &QuickTimeParser::readCMOV,    MKTAG('c', 'm', 'o', 'v') },
00164         { &QuickTimeParser::readWAVE,    MKTAG('w', 'a', 'v', 'e') },
00165         { &QuickTimeParser::readESDS,    MKTAG('e', 's', 'd', 's') },
00166         { &QuickTimeParser::readSMI,     MKTAG('S', 'M', 'I', ' ') },
00167         { &QuickTimeParser::readDefault, MKTAG('g', 'm', 'h', 'd') },
00168         { &QuickTimeParser::readLeaf,    MKTAG('g', 'm', 'i', 'n') },
00169         { nullptr, 0 }
00170     };
00171 
00172     _parseTable = p;
00173 }
00174 
00175 int QuickTimeParser::readDefault(Atom atom) {
00176     uint32 total_size = 0;
00177     Atom a;
00178     int err = 0;
00179 
00180     a.offset = atom.offset;
00181 
00182     while(((total_size + 8) < atom.size) && !_fd->eos() && _fd->pos() < _fd->size() && !err) {
00183         a.size = atom.size;
00184         a.type = 0;
00185 
00186         if (atom.size >= 8) {
00187             a.size = _fd->readUint32BE();
00188             a.type = _fd->readUint32BE();
00189 
00190             // Some QuickTime videos with resource forks have mdat chunks
00191             // that are of size 0. Adjust it so it's the correct size.
00192             if (a.type == MKTAG('m', 'd', 'a', 't') && a.size == 0)
00193                 a.size = _fd->size();
00194         }
00195 
00196         total_size += 8;
00197         a.offset += 8;
00198         debug(4, "type: %08x  %.4s  sz: %x %x %x", a.type, tag2str(a.type), a.size, atom.size, total_size);
00199 
00200         if (a.size == 1) { // 64 bit extended size
00201             warning("64 bit extended size is not supported in QuickTime");
00202             return -1;
00203         }
00204 
00205         if (a.size == 0) {
00206             a.size = atom.size - total_size;
00207             if (a.size <= 8)
00208                 break;
00209         }
00210 
00211         uint32 i = 0;
00212 
00213         for (; _parseTable[i].type != 0 && _parseTable[i].type != a.type; i++)
00214             ; // Empty
00215 
00216         if (a.size < 8)
00217             break;
00218 
00219         a.size -= 8;
00220 
00221         if (a.size + (uint32)_fd->pos() > (uint32)_fd->size()) {
00222             _fd->seek(_fd->size());
00223             debug(0, "Skipping junk found at the end of the QuickTime file");
00224             return 0;
00225         } else if (_parseTable[i].type == 0) { // skip leaf atom data
00226             debug(0, ">>> Skipped [%s]", tag2str(a.type));
00227 
00228             _fd->seek(a.size, SEEK_CUR);
00229         } else {
00230             uint32 start_pos = _fd->pos();
00231             err = (this->*_parseTable[i].func)(a);
00232 
00233             uint32 left = a.size - _fd->pos() + start_pos;
00234 
00235             if (left > 0) // skip garbage at atom end
00236                 _fd->seek(left, SEEK_CUR);
00237         }
00238 
00239         a.offset += a.size;
00240         total_size += a.size;
00241     }
00242 
00243     if (!err && total_size < atom.size)
00244         _fd->seek(atom.size - total_size, SEEK_SET);
00245 
00246     return err;
00247 }
00248 
00249 int QuickTimeParser::readLeaf(Atom atom) {
00250     if (atom.size > 1)
00251         _fd->seek(atom.size, SEEK_SET);
00252 
00253     return 0;
00254 }
00255 
00256 int QuickTimeParser::readMOOV(Atom atom) {
00257     if (readDefault(atom) < 0)
00258         return -1;
00259 
00260     // We parsed the 'moov' atom, so we don't need anything else
00261     _foundMOOV = true;
00262     return 1;
00263 }
00264 
00265 int QuickTimeParser::readCMOV(Atom atom) {
00266 #ifdef USE_ZLIB
00267     // Read in the dcom atom
00268     _fd->readUint32BE();
00269     if (_fd->readUint32BE() != MKTAG('d', 'c', 'o', 'm'))
00270         return -1;
00271     if (_fd->readUint32BE() != MKTAG('z', 'l', 'i', 'b')) {
00272         warning("Unknown cmov compression type");
00273         return -1;
00274     }
00275 
00276     // Read in the cmvd atom
00277     uint32 compressedSize = _fd->readUint32BE() - 12;
00278     if (_fd->readUint32BE() != MKTAG('c', 'm', 'v', 'd'))
00279         return -1;
00280     uint32 uncompressedSize = _fd->readUint32BE();
00281 
00282     // Read in data
00283     byte *compressedData = (byte *)malloc(compressedSize);
00284     _fd->read(compressedData, compressedSize);
00285 
00286     // Create uncompressed stream
00287     byte *uncompressedData = (byte *)malloc(uncompressedSize);
00288 
00289     // Uncompress the data
00290     unsigned long dstLen = uncompressedSize;
00291     if (!uncompress(uncompressedData, &dstLen, compressedData, compressedSize)) {
00292         warning ("Could not uncompress cmov chunk");
00293         free(compressedData);
00294         free(uncompressedData);
00295         return -1;
00296     }
00297 
00298     // Load data into a new MemoryReadStream and assign _fd to be that
00299     SeekableReadStream *oldStream = _fd;
00300     _fd = new MemoryReadStream(uncompressedData, uncompressedSize, DisposeAfterUse::YES);
00301 
00302     // Read the contents of the uncompressed data
00303     Atom a = { MKTAG('m', 'o', 'o', 'v'), 0, uncompressedSize };
00304     int err = readDefault(a);
00305 
00306     // Assign the file handle back to the original handle
00307     free(compressedData);
00308     delete _fd;
00309     _fd = oldStream;
00310 
00311     return err;
00312 #else
00313     warning ("zlib not found, cannot read QuickTime cmov atom");
00314     return -1;
00315 #endif
00316 }
00317 
00318 int QuickTimeParser::readMVHD(Atom atom) {
00319     byte version = _fd->readByte(); // version
00320     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00321 
00322     if (version == 1) {
00323         warning("QuickTime version 1");
00324         _fd->readUint32BE(); _fd->readUint32BE();
00325         _fd->readUint32BE(); _fd->readUint32BE();
00326     } else {
00327         _fd->readUint32BE(); // creation time
00328         _fd->readUint32BE(); // modification time
00329     }
00330 
00331     _timeScale = _fd->readUint32BE(); // time scale
00332     debug(0, "time scale = %i\n", _timeScale);
00333 
00334     // duration
00335     _duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE();
00336     _fd->readUint32BE(); // preferred scale
00337 
00338     _fd->readUint16BE(); // preferred volume
00339 
00340     _fd->seek(10, SEEK_CUR); // reserved
00341 
00342     // We only need two values from the movie display matrix. Most of the values are just
00343     // skipped. xMod and yMod are 16:16 fixed point numbers, the last part of the 3x3 matrix
00344     // is 2:30.
00345     uint32 xMod = _fd->readUint32BE();
00346     _fd->skip(12);
00347     uint32 yMod = _fd->readUint32BE();
00348     _fd->skip(16);
00349 
00350     _scaleFactorX = Rational(0x10000, xMod);
00351     _scaleFactorY = Rational(0x10000, yMod);
00352 
00353     _scaleFactorX.debugPrint(1, "readMVHD(): scaleFactorX =");
00354     _scaleFactorY.debugPrint(1, "readMVHD(): scaleFactorY =");
00355 
00356     _fd->readUint32BE(); // preview time
00357     _fd->readUint32BE(); // preview duration
00358     _fd->readUint32BE(); // poster time
00359     _fd->readUint32BE(); // selection time
00360     _fd->readUint32BE(); // selection duration
00361     _fd->readUint32BE(); // current time
00362     _fd->readUint32BE(); // next track ID
00363 
00364     return 0;
00365 }
00366 
00367 int QuickTimeParser::readTRAK(Atom atom) {
00368     Track *track = new Track();
00369 
00370     track->codecType = CODEC_TYPE_MOV_OTHER;
00371     track->startTime = 0; // XXX: check
00372     _tracks.push_back(track);
00373 
00374     return readDefault(atom);
00375 }
00376 
00377 int QuickTimeParser::readTKHD(Atom atom) {
00378     Track *track = _tracks.back();
00379     byte version = _fd->readByte();
00380 
00381     _fd->readByte(); _fd->readByte();
00382     _fd->readByte(); // flags
00383     //
00384     //MOV_TRACK_ENABLED 0x0001
00385     //MOV_TRACK_IN_MOVIE 0x0002
00386     //MOV_TRACK_IN_PREVIEW 0x0004
00387     //MOV_TRACK_IN_POSTER 0x0008
00388     //
00389 
00390     if (version == 1) {
00391         _fd->readUint32BE(); _fd->readUint32BE();
00392         _fd->readUint32BE(); _fd->readUint32BE();
00393     } else {
00394         _fd->readUint32BE(); // creation time
00395         _fd->readUint32BE(); // modification time
00396     }
00397 
00398     /* track->id = */_fd->readUint32BE(); // track id (NOT 0 !)
00399     _fd->readUint32BE(); // reserved
00400     track->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase
00401     _fd->readUint32BE(); // reserved
00402     _fd->readUint32BE(); // reserved
00403 
00404     _fd->readUint16BE(); // layer
00405     _fd->readUint16BE(); // alternate group
00406     _fd->readUint16BE(); // volume
00407     _fd->readUint16BE(); // reserved
00408 
00409     // We only need the two values from the displacement matrix for a track.
00410     // See readMVHD() for more information.
00411     uint32 xMod = _fd->readUint32BE();
00412     _fd->skip(12);
00413     uint32 yMod = _fd->readUint32BE();
00414     _fd->skip(16);
00415 
00416     track->scaleFactorX = Rational(0x10000, xMod);
00417     track->scaleFactorY = Rational(0x10000, yMod);
00418 
00419     track->scaleFactorX.debugPrint(1, "readTKHD(): scaleFactorX =");
00420     track->scaleFactorY.debugPrint(1, "readTKHD(): scaleFactorY =");
00421 
00422     // these are fixed-point, 16:16
00423     //_fd->readUint32BE() >> 16; // track width
00424     //_fd->readUint32BE() >> 16; // track height
00425 
00426     return 0;
00427 }
00428 
00429 // edit list atom
00430 int QuickTimeParser::readELST(Atom atom) {
00431     Track *track = _tracks.back();
00432 
00433     _fd->readByte(); // version
00434     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00435 
00436     uint32 editCount = _fd->readUint32BE();
00437     track->editList.resize(editCount);
00438 
00439     debug(2, "Track %d edit list count: %d", _tracks.size() - 1, editCount);
00440 
00441     uint32 offset = 0;
00442 
00443     for (uint32 i = 0; i < editCount; i++) {
00444         track->editList[i].trackDuration = _fd->readUint32BE();
00445         track->editList[i].mediaTime = _fd->readSint32BE();
00446         track->editList[i].mediaRate = Rational(_fd->readUint32BE(), 0x10000);
00447         track->editList[i].timeOffset = offset;
00448         debugN(3, "\tDuration = %d (Offset = %d), Media Time = %d, ", track->editList[i].trackDuration, offset, track->editList[i].mediaTime);
00449         track->editList[i].mediaRate.debugPrint(3, "Media Rate =");
00450         offset += track->editList[i].trackDuration;
00451     }
00452 
00453     return 0;
00454 }
00455 
00456 int QuickTimeParser::readHDLR(Atom atom) {
00457     Track *track = _tracks.back();
00458 
00459     _fd->readByte(); // version
00460     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00461 
00462     // component type
00463     uint32 ctype = _fd->readUint32BE();
00464     uint32 type = _fd->readUint32BE(); // component subtype
00465 
00466     debug(0, "ctype= %s (0x%08lx)", tag2str(ctype), (long)ctype);
00467     debug(0, "stype= %s", tag2str(type));
00468 
00469     if (ctype == MKTAG('m', 'h', 'l', 'r')) // MOV
00470         debug(0, "MOV detected");
00471     else if (ctype == 0)
00472         debug(0, "MPEG-4 detected");
00473 
00474     if (type == MKTAG('v', 'i', 'd', 'e'))
00475         track->codecType = CODEC_TYPE_VIDEO;
00476     else if (type == MKTAG('s', 'o', 'u', 'n'))
00477         track->codecType = CODEC_TYPE_AUDIO;
00478     else if (type == MKTAG('m', 'u', 's', 'i'))
00479         track->codecType = CODEC_TYPE_MIDI;
00480 
00481     _fd->readUint32BE(); // component manufacture
00482     _fd->readUint32BE(); // component flags
00483     _fd->readUint32BE(); // component flags mask
00484 
00485     if (atom.size <= 24)
00486         return 0; // nothing left to read
00487 
00488     // .mov: PASCAL string
00489     byte len = _fd->readByte();
00490     _fd->seek(len, SEEK_CUR);
00491 
00492     _fd->seek(atom.size - (_fd->pos() - atom.offset), SEEK_CUR);
00493 
00494     return 0;
00495 }
00496 
00497 int QuickTimeParser::readMDHD(Atom atom) {
00498     Track *track = _tracks.back();
00499     byte version = _fd->readByte();
00500 
00501     if (version > 1)
00502         return 1; // unsupported
00503 
00504     _fd->readByte(); _fd->readByte();
00505     _fd->readByte(); // flags
00506 
00507     if (version == 1) {
00508         _fd->readUint32BE(); _fd->readUint32BE();
00509         _fd->readUint32BE(); _fd->readUint32BE();
00510     } else {
00511         _fd->readUint32BE(); // creation time
00512         _fd->readUint32BE(); // modification time
00513     }
00514 
00515     track->timeScale = _fd->readUint32BE();
00516     track->mediaDuration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration
00517 
00518     _fd->readUint16BE(); // language
00519     _fd->readUint16BE(); // quality
00520 
00521     return 0;
00522 }
00523 
00524 int QuickTimeParser::readSTSD(Atom atom) {
00525     Track *track = _tracks.back();
00526 
00527     _fd->readByte(); // version
00528     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00529 
00530     uint32 entryCount = _fd->readUint32BE();
00531     track->sampleDescs.reserve(entryCount);
00532 
00533     for (uint32 i = 0; i < entryCount; i++) { // Parsing Sample description table
00534         Atom a = { 0, 0, 0 };
00535         uint32 start_pos = _fd->pos();
00536         int size = _fd->readUint32BE(); // size
00537         uint32 format = _fd->readUint32BE(); // data format
00538 
00539         _fd->readUint32BE(); // reserved
00540         _fd->readUint16BE(); // reserved
00541         _fd->readUint16BE(); // index
00542 
00543         track->sampleDescs.push_back(readSampleDesc(track, format, size - 16));
00544 
00545         debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), track->codecType);
00546 
00547         if (!track->sampleDescs[i]) {
00548             // other codec type, just skip (rtp, mp4s, tmcd ...)
00549             _fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR);
00550         }
00551 
00552         // this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...)
00553         a.size = size - (_fd->pos() - start_pos);
00554         if (a.size > 8)
00555             readDefault(a);
00556         else if (a.size > 0)
00557             _fd->seek(a.size, SEEK_CUR);
00558     }
00559 
00560     return 0;
00561 }
00562 
00563 int QuickTimeParser::readSTSC(Atom atom) {
00564     Track *track = _tracks.back();
00565 
00566     _fd->readByte(); // version
00567     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00568 
00569     track->sampleToChunkCount = _fd->readUint32BE();
00570 
00571     debug(0, "track[%i].stsc.entries = %i", _tracks.size() - 1, track->sampleToChunkCount);
00572 
00573     track->sampleToChunk = new SampleToChunkEntry[track->sampleToChunkCount];
00574 
00575     if (!track->sampleToChunk)
00576         return -1;
00577 
00578     for (uint32 i = 0; i < track->sampleToChunkCount; i++) {
00579         track->sampleToChunk[i].first = _fd->readUint32BE() - 1;
00580         track->sampleToChunk[i].count = _fd->readUint32BE();
00581         track->sampleToChunk[i].id = _fd->readUint32BE();
00582         //warning("Sample to Chunk[%d]: First = %d, Count = %d", i, track->sampleToChunk[i].first, track->sampleToChunk[i].count);
00583     }
00584 
00585     return 0;
00586 }
00587 
00588 int QuickTimeParser::readSTSS(Atom atom) {
00589     Track *track = _tracks.back();
00590 
00591     _fd->readByte(); // version
00592     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00593 
00594     track->keyframeCount = _fd->readUint32BE();
00595 
00596     debug(0, "keyframeCount = %d", track->keyframeCount);
00597 
00598     track->keyframes = new uint32[track->keyframeCount];
00599 
00600     if (!track->keyframes)
00601         return -1;
00602 
00603     for (uint32 i = 0; i < track->keyframeCount; i++) {
00604         track->keyframes[i] = _fd->readUint32BE() - 1; // Adjust here, the frames are based on 1
00605         debug(6, "keyframes[%d] = %d", i, track->keyframes[i]);
00606 
00607     }
00608     return 0;
00609 }
00610 
00611 int QuickTimeParser::readSTSZ(Atom atom) {
00612     Track *track = _tracks.back();
00613 
00614     _fd->readByte(); // version
00615     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00616 
00617     track->sampleSize = _fd->readUint32BE();
00618     track->sampleCount = _fd->readUint32BE();
00619 
00620     debug(5, "sampleSize = %d sampleCount = %d", track->sampleSize, track->sampleCount);
00621 
00622     if (track->sampleSize)
00623         return 0; // there isn't any table following
00624 
00625     track->sampleSizes = new uint32[track->sampleCount];
00626 
00627     if (!track->sampleSizes)
00628         return -1;
00629 
00630     for(uint32 i = 0; i < track->sampleCount; i++) {
00631         track->sampleSizes[i] = _fd->readUint32BE();
00632         debug(6, "sampleSizes[%d] = %d", i, track->sampleSizes[i]);
00633     }
00634 
00635     return 0;
00636 }
00637 
00638 int QuickTimeParser::readSTTS(Atom atom) {
00639     Track *track = _tracks.back();
00640     uint32 totalSampleCount = 0;
00641 
00642     _fd->readByte(); // version
00643     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00644 
00645     track->timeToSampleCount = _fd->readUint32BE();
00646     track->timeToSample = new TimeToSampleEntry[track->timeToSampleCount];
00647 
00648     debug(0, "track[%d].stts.entries = %d", _tracks.size() - 1, track->timeToSampleCount);
00649 
00650     for (int32 i = 0; i < track->timeToSampleCount; i++) {
00651         track->timeToSample[i].count = _fd->readUint32BE();
00652         track->timeToSample[i].duration = _fd->readUint32BE();
00653 
00654         debug(1, "\tCount = %d, Duration = %d", track->timeToSample[i].count, track->timeToSample[i].duration);
00655 
00656         totalSampleCount += track->timeToSample[i].count;
00657     }
00658 
00659     track->frameCount = totalSampleCount;
00660     return 0;
00661 }
00662 
00663 int QuickTimeParser::readSTCO(Atom atom) {
00664     Track *track = _tracks.back();
00665 
00666     _fd->readByte(); // version
00667     _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags
00668 
00669     track->chunkCount = _fd->readUint32BE();
00670     track->chunkOffsets = new uint32[track->chunkCount];
00671 
00672     if (!track->chunkOffsets)
00673         return -1;
00674 
00675     for (uint32 i = 0; i < track->chunkCount; i++) {
00676         // WORKAROUND/HACK: The offsets in Riven videos (ones inside the Mohawk archives themselves)
00677         // have offsets relative to the archive and not the video. This is quite nasty. We subtract
00678         // the initial offset of the stream to get the correct value inside of the stream.
00679         track->chunkOffsets[i] = _fd->readUint32BE() - _beginOffset;
00680     }
00681 
00682     return 0;
00683 }
00684 
00685 int QuickTimeParser::readWAVE(Atom atom) {
00686     if (_tracks.empty())
00687         return 0;
00688 
00689     Track *track = _tracks.back();
00690 
00691     if (atom.size > (1 << 30))
00692         return -1;
00693 
00694     // We should only get here within an stsd atom
00695     if (track->sampleDescs.empty())
00696         return -1;
00697 
00698     SampleDesc *sampleDesc = track->sampleDescs.back();
00699 
00700     if (sampleDesc->getCodecTag() == MKTAG('Q', 'D', 'M', '2')) // Read extra data for QDM2
00701         sampleDesc->_extraData = _fd->readStream(atom.size);
00702     else if (atom.size > 8)
00703         return readDefault(atom);
00704     else
00705         _fd->skip(atom.size);
00706 
00707     return 0;
00708 }
00709 
00710 enum {
00711     kMP4IODescTag          = 2,
00712     kMP4ESDescTag          = 3,
00713     kMP4DecConfigDescTag   = 4,
00714     kMP4DecSpecificDescTag = 5
00715 };
00716 
00717 static int readMP4DescLength(SeekableReadStream *stream) {
00718     int length = 0;
00719     int count = 4;
00720 
00721     while (count--) {
00722         byte c = stream->readByte();
00723         length = (length << 7) | (c & 0x7f);
00724 
00725         if (!(c & 0x80))
00726             break;
00727     }
00728 
00729     return length;
00730 }
00731 
00732 static void readMP4Desc(SeekableReadStream *stream, byte &tag, int &length) {
00733     tag = stream->readByte();
00734     length = readMP4DescLength(stream);
00735 }
00736 
00737 int QuickTimeParser::readESDS(Atom atom) {
00738     if (_tracks.empty())
00739         return 0;
00740 
00741     Track *track = _tracks.back();
00742 
00743     // We should only get here within an stsd atom
00744     if (track->sampleDescs.empty())
00745         return -1;
00746 
00747     SampleDesc *sampleDesc = track->sampleDescs.back();
00748 
00749     _fd->readUint32BE(); // version + flags
00750 
00751     byte tag;
00752     int length;
00753 
00754     readMP4Desc(_fd, tag, length);
00755     _fd->readUint16BE(); // id
00756     if (tag == kMP4ESDescTag)
00757         _fd->readByte(); // priority
00758 
00759     // Check if we've got the Config MPEG-4 header
00760     readMP4Desc(_fd, tag, length);
00761     if (tag != kMP4DecConfigDescTag)
00762         return 0;
00763 
00764     sampleDesc->_objectTypeMP4 = _fd->readByte();
00765     _fd->readByte();                      // stream type
00766     _fd->readUint16BE(); _fd->readByte(); // buffer size
00767     _fd->readUint32BE();                  // max bitrate
00768     _fd->readUint32BE();                  // avg bitrate
00769 
00770     // Check if we've got the Specific MPEG-4 header
00771     readMP4Desc(_fd, tag, length);
00772     if (tag != kMP4DecSpecificDescTag)
00773         return 0;
00774 
00775     sampleDesc->_extraData = _fd->readStream(length);
00776 
00777     debug(0, "MPEG-4 object type = %02x", sampleDesc->_objectTypeMP4);
00778     return 0;
00779 }
00780 
00781 int QuickTimeParser::readSMI(Atom atom) {
00782     if (_tracks.empty())
00783         return 0;
00784 
00785     Track *track = _tracks.back();
00786 
00787     // We should only get here within an stsd atom
00788     if (track->sampleDescs.empty())
00789         return -1;
00790 
00791     SampleDesc *sampleDesc = track->sampleDescs.back();
00792 
00793     // This atom just contains SVQ3 extra data
00794     sampleDesc->_extraData = _fd->readStream(atom.size);
00795 
00796     return 0;
00797 }
00798 
00799 void QuickTimeParser::close() {
00800     for (uint32 i = 0; i < _tracks.size(); i++)
00801         delete _tracks[i];
00802 
00803     _tracks.clear();
00804 
00805     if (_disposeFileHandle == DisposeAfterUse::YES)
00806         delete _fd;
00807 
00808     _fd = nullptr;
00809 }
00810 
00811 QuickTimeParser::SampleDesc::SampleDesc(Track *parentTrack, uint32 codecTag) {
00812     _parentTrack = parentTrack;
00813     _codecTag = codecTag;
00814     _extraData = nullptr;
00815     _objectTypeMP4 = 0;
00816 }
00817 
00818 QuickTimeParser::SampleDesc::~SampleDesc() {
00819     delete _extraData;
00820 }
00821 
00822 QuickTimeParser::Track::Track() {
00823     chunkCount = 0;
00824     chunkOffsets = nullptr;
00825     timeToSampleCount = 0;
00826     timeToSample = nullptr;
00827     sampleToChunkCount = 0;
00828     sampleToChunk = nullptr;
00829     sampleSize = 0;
00830     sampleCount = 0;
00831     sampleSizes = nullptr;
00832     keyframeCount = 0;
00833     keyframes = nullptr;
00834     timeScale = 0;
00835     width = 0;
00836     height = 0;
00837     codecType = CODEC_TYPE_MOV_OTHER;
00838     frameCount = 0;
00839     duration = 0;
00840     startTime = 0;
00841     mediaDuration = 0;
00842 }
00843 
00844 QuickTimeParser::Track::~Track() {
00845     delete[] chunkOffsets;
00846     delete[] timeToSample;
00847     delete[] sampleToChunk;
00848     delete[] sampleSizes;
00849     delete[] keyframes;
00850 
00851     for (uint32 i = 0; i < sampleDescs.size(); i++)
00852         delete sampleDescs[i];
00853 }
00854 
00855 } // End of namespace Video


Generated on Sat Mar 16 2019 05:01:50 for ResidualVM by doxygen 1.7.1
curved edge   curved edge