| 1 | /* |
|---|
| 2 | * MatroskaImport.h |
|---|
| 3 | * |
|---|
| 4 | * MatroskaImport.h - QuickTime importer interface for opening a Matroska file. |
|---|
| 5 | * |
|---|
| 6 | * |
|---|
| 7 | * Copyright (c) 2006 David Conrad |
|---|
| 8 | * |
|---|
| 9 | * This program is free software; you can redistribute it and/or |
|---|
| 10 | * modify it under the terms of the GNU Lesser General Public |
|---|
| 11 | * License as published by the Free Software Foundation; |
|---|
| 12 | * version 2.1 of the License. |
|---|
| 13 | * |
|---|
| 14 | * This program is distributed in the hope that it will be useful, |
|---|
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 17 | * Lesser General Public License for more details. |
|---|
| 18 | * |
|---|
| 19 | * You should have received a copy of the GNU Lesser General Public |
|---|
| 20 | * License along with this program; if not, write to the Free Software |
|---|
| 21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 22 | * |
|---|
| 23 | */ |
|---|
| 24 | |
|---|
| 25 | #ifndef __MATROSKAIMPORT_H__ |
|---|
| 26 | #define __MATROSKAIMPORT_H__ |
|---|
| 27 | |
|---|
| 28 | #include <vector> |
|---|
| 29 | #include <list> |
|---|
| 30 | #include <map> |
|---|
| 31 | |
|---|
| 32 | #include <QuickTime/QuickTime.h> |
|---|
| 33 | #include "DataHandlerCallback.h" |
|---|
| 34 | #include "SubImport.h" |
|---|
| 35 | |
|---|
| 36 | #include <ebml/EbmlStream.h> |
|---|
| 37 | #include <matroska/KaxSeekHead.h> |
|---|
| 38 | #include <matroska/KaxInfo.h> |
|---|
| 39 | #include <matroska/KaxTracks.h> |
|---|
| 40 | #include <matroska/KaxChapters.h> |
|---|
| 41 | #include <matroska/KaxBlock.h> |
|---|
| 42 | #include <matroska/KaxAttachments.h> |
|---|
| 43 | #include <matroska/KaxContentEncoding.h> |
|---|
| 44 | |
|---|
| 45 | using namespace libmatroska; |
|---|
| 46 | using namespace std; |
|---|
| 47 | |
|---|
| 48 | // the maximum number of frames that have to be decoded between decoding |
|---|
| 49 | // any single frame and displaying it, used to cap the maximum size of the |
|---|
| 50 | // pts -> dts reorder buffer. Set to 4 since that's what it is in FFmpeg; |
|---|
| 51 | // shouldn't be more than 2 with current codecs |
|---|
| 52 | #define MAX_DECODE_DELAY 4 |
|---|
| 53 | |
|---|
| 54 | struct MatroskaFrame { |
|---|
| 55 | TimeValue64 dts; // decode timestamp |
|---|
| 56 | TimeValue64 pts; // presentation/display timestamp |
|---|
| 57 | TimeValue duration; |
|---|
| 58 | SInt64 offset; |
|---|
| 59 | SInt64 size; |
|---|
| 60 | short flags; |
|---|
| 61 | DataBuffer *buffer; |
|---|
| 62 | }; |
|---|
| 63 | |
|---|
| 64 | struct MatroskaSeekContext { |
|---|
| 65 | EbmlElement *el_l1; |
|---|
| 66 | uint64_t position; |
|---|
| 67 | }; |
|---|
| 68 | |
|---|
| 69 | // a list of level one elements and their offsets in the segment |
|---|
| 70 | class MatroskaSeek { |
|---|
| 71 | public: |
|---|
| 72 | EbmlId GetID() const { return EbmlId(ebmlID, idLength); } |
|---|
| 73 | bool operator<(const MatroskaSeek &rhs) const { return segmentPos < rhs.segmentPos; } |
|---|
| 74 | bool operator>(const MatroskaSeek &rhs) const { return segmentPos > rhs.segmentPos; } |
|---|
| 75 | |
|---|
| 76 | MatroskaSeekContext GetSeekContext(uint64_t segmentOffset = 0) const { |
|---|
| 77 | return (MatroskaSeekContext){ NULL, segmentPos + segmentOffset }; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | uint32_t ebmlID; |
|---|
| 81 | uint8_t idLength; |
|---|
| 82 | uint64_t segmentPos; |
|---|
| 83 | }; |
|---|
| 84 | |
|---|
| 85 | class MatroskaTrack { |
|---|
| 86 | public: |
|---|
| 87 | MatroskaTrack(); |
|---|
| 88 | |
|---|
| 89 | // retains the sampleTable if it exists, allocates a new SampleDescriptionHandle |
|---|
| 90 | // and copies it if necessary |
|---|
| 91 | MatroskaTrack(const MatroskaTrack ©); |
|---|
| 92 | |
|---|
| 93 | // releases the sampleTable and sample description |
|---|
| 94 | ~MatroskaTrack(); |
|---|
| 95 | |
|---|
| 96 | // adds the all the frames in the block group to the sample table if it exists, |
|---|
| 97 | // the media otherwise. If this track type is subtitle, also inserts it into the track. |
|---|
| 98 | void AddBlock(KaxInternalBlock &block, uint32 duration, short flags); |
|---|
| 99 | |
|---|
| 100 | // this adds all the samples added through AddBlock() to the track that aren't already |
|---|
| 101 | // added, e.g. from a previous call to AddSamplesToTrack() |
|---|
| 102 | void AddSamplesToTrack(); |
|---|
| 103 | |
|---|
| 104 | void FinishTrack(); |
|---|
| 105 | |
|---|
| 106 | UInt16 number; |
|---|
| 107 | UInt8 type, is_vobsub; |
|---|
| 108 | Track theTrack; |
|---|
| 109 | Media theMedia; |
|---|
| 110 | SampleDescriptionHandle desc; |
|---|
| 111 | QTMutableSampleTableRef sampleTable; |
|---|
| 112 | QTSampleDescriptionID qtSampleDesc; |
|---|
| 113 | SInt64 timecodeScale; |
|---|
| 114 | TimeValue64 maxLoadedTime; |
|---|
| 115 | CXXSubSerializer *subtitleSerializer; |
|---|
| 116 | Handle subDataRefHandler; |
|---|
| 117 | uint8 isEnabled; |
|---|
| 118 | uint32_t defaultDuration; |
|---|
| 119 | |
|---|
| 120 | // laced tracks can have multiple frames per block, it's easier to ignore them |
|---|
| 121 | // for pts -> dts conversion (and laced tracks can't have non-keyframes anyways) |
|---|
| 122 | bool usesLacing; |
|---|
| 123 | |
|---|
| 124 | private: |
|---|
| 125 | // adds an individual frame from a block group into the sample table if it exists, |
|---|
| 126 | // the media otherwise, and into the track if the track is a subtitle track. |
|---|
| 127 | void AddFrame(MatroskaFrame &frame); |
|---|
| 128 | |
|---|
| 129 | // parses the first frame of a supported data format to determine codec parameters, |
|---|
| 130 | // which can be more correct than the codec headers. |
|---|
| 131 | void ParseFirstBlock(KaxInternalBlock &block); |
|---|
| 132 | |
|---|
| 133 | // Since the duration in Matroska files is generally rather unreliable, rely only on |
|---|
| 134 | // the difference in timestamps between two frames. Thus, AddBlock() buffers frames |
|---|
| 135 | // from one block group until the next block group is found to set the duration of the |
|---|
| 136 | // previous ones to be the difference in timestamps. |
|---|
| 137 | vector<MatroskaFrame> lastFrames; |
|---|
| 138 | int currentFrame; // the frame we're currently determining the dts for |
|---|
| 139 | |
|---|
| 140 | // insert pts values, sort, then smallest value is current dts if size > decode delay |
|---|
| 141 | list<TimeValue64> ptsReorder; |
|---|
| 142 | |
|---|
| 143 | // We calculate the duration at a given dts, and have to save it until we find a |
|---|
| 144 | // frame with the same pts. This makes it such that we are actually calculating |
|---|
| 145 | // the duration between display timestamps instead of decode timestamps. |
|---|
| 146 | map<TimeValue64, TimeValue> durationForPTS; |
|---|
| 147 | |
|---|
| 148 | bool seenFirstBlock; |
|---|
| 149 | |
|---|
| 150 | // When using a sample table, these store the range of samples that are in the |
|---|
| 151 | // sample table but not yet added to the media. |
|---|
| 152 | SInt64 firstSample; // -1 means it's not set |
|---|
| 153 | SInt64 numSamples; |
|---|
| 154 | |
|---|
| 155 | // the amount of the media that needs to be added to the track |
|---|
| 156 | TimeValue64 durationToAdd; |
|---|
| 157 | |
|---|
| 158 | // We don't want to add regions with frames that are displayed after the region. |
|---|
| 159 | // Assume that when the sum of the display offsets is zero, this is true, and |
|---|
| 160 | // update durationToAdd by adding durationSinceZeroSum. |
|---|
| 161 | int displayOffsetSum; |
|---|
| 162 | SInt64 durationSinceZeroSum; |
|---|
| 163 | |
|---|
| 164 | // the timestamp of the first block, used to set delay for the track |
|---|
| 165 | TimeValue64 lowestPTS; |
|---|
| 166 | }; |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | class MatroskaImport { |
|---|
| 170 | public: |
|---|
| 171 | // public interface functions, simply called from the C equivalents defined |
|---|
| 172 | // by ComponentDispatchHelper.c and implemented in MatroskaImport.cpp |
|---|
| 173 | |
|---|
| 174 | // MatroskaImportOpen() |
|---|
| 175 | MatroskaImport(ComponentInstance self); |
|---|
| 176 | |
|---|
| 177 | // MatrosakImportClose() |
|---|
| 178 | ~MatroskaImport(); |
|---|
| 179 | |
|---|
| 180 | // MatroskaImportDataRef() |
|---|
| 181 | ComponentResult ImportDataRef(Handle dataRef, OSType dataRefType, Movie theMovie, |
|---|
| 182 | Track targetTrack, Track *usedTrack, |
|---|
| 183 | TimeValue atTime, TimeValue *durationAdded, |
|---|
| 184 | long inFlags, long *outFlags); |
|---|
| 185 | |
|---|
| 186 | // MatroskaImportValidateDataRef() |
|---|
| 187 | ComponentResult ValidateDataRef(Handle dataRef, OSType dataRefType, UInt8 *valid); |
|---|
| 188 | |
|---|
| 189 | // MatroskaImportIdle() |
|---|
| 190 | ComponentResult Idle(long inFlags, long *outFlags); |
|---|
| 191 | |
|---|
| 192 | // MatroskaImportSetIdleManager() |
|---|
| 193 | ComponentResult SetIdleManager(IdleManager im); |
|---|
| 194 | |
|---|
| 195 | // MatroskaImportGetMaxLoadedTime() |
|---|
| 196 | ComponentResult GetMaxLoadedTime(TimeValue *time); |
|---|
| 197 | |
|---|
| 198 | // MatroskaImportGetLoadState() |
|---|
| 199 | ComponentResult GetLoadState(long *importerLoadState); |
|---|
| 200 | |
|---|
| 201 | // we need to get our component instance to get our mime type resource |
|---|
| 202 | ComponentInstance Component() { return self; } |
|---|
| 203 | |
|---|
| 204 | private: |
|---|
| 205 | // open the ioHandler and EBML stream, and read the EBML head to verify it's a matroska file |
|---|
| 206 | // returns true if it's a valid file and false otherwise |
|---|
| 207 | bool OpenFile(); |
|---|
| 208 | |
|---|
| 209 | // create all the tracks and their sample descriptions as described by the file header |
|---|
| 210 | // also create chapters if any. Leaves el_l1 pointing to the first cluster, unread. |
|---|
| 211 | ComponentResult SetupMovie(); |
|---|
| 212 | |
|---|
| 213 | // This finds the next level 1 element and both replaces the el_l1 variable with it and |
|---|
| 214 | // returns it. Does not read the data. |
|---|
| 215 | EbmlElement *NextLevel1Element(); |
|---|
| 216 | |
|---|
| 217 | // reads the current level 1 element and calls Read<...> based on its ebml id |
|---|
| 218 | ComponentResult ProcessLevel1Element(); |
|---|
| 219 | |
|---|
| 220 | // sets up timescale & file name metadata |
|---|
| 221 | ComponentResult ReadSegmentInfo(KaxInfo &segmentInfo); |
|---|
| 222 | |
|---|
| 223 | // sets up all the movie tracks and media |
|---|
| 224 | ComponentResult ReadTracks(KaxTracks &trackEntries); |
|---|
| 225 | |
|---|
| 226 | // Creates a chapter track, but doesn't actually add the chapter reference to the other |
|---|
| 227 | // enabled tracks in case some weird file has this element before the Tracks element |
|---|
| 228 | ComponentResult ReadChapters(KaxChapters &chapterEntries); |
|---|
| 229 | |
|---|
| 230 | // Activates any attached fonts, ignores other attachment types for now |
|---|
| 231 | ComponentResult ReadAttachments(KaxAttachments &attachments); |
|---|
| 232 | |
|---|
| 233 | // Fills the levelOneElements vector with the positions of the elements in the seek head |
|---|
| 234 | ComponentResult ReadMetaSeek(KaxSeekHead &seekHead); |
|---|
| 235 | |
|---|
| 236 | // Adds a sample description extension if the content is compressed |
|---|
| 237 | // should only be the case for VobSub data. |
|---|
| 238 | ComponentResult ReadContentEncodings(KaxContentEncodings &encodings, MatroskaTrack &mkvTrack); |
|---|
| 239 | |
|---|
| 240 | // These three are called from ReadTracks to set up a track of the specific type, |
|---|
| 241 | // modifying the MatroskaTrack structure to reflect the newly create track. |
|---|
| 242 | // They return an error if the track couldn't be created or noErr on success. |
|---|
| 243 | ComponentResult AddVideoTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack); |
|---|
| 244 | ComponentResult AddAudioTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack); |
|---|
| 245 | ComponentResult AddSubtitleTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack); |
|---|
| 246 | |
|---|
| 247 | // this is called recursively to add only the leaves on the chapter tree to |
|---|
| 248 | // chapter track, since QT doesn't support chapter nesting. |
|---|
| 249 | void AddChapterAtom(KaxChapterAtom *atom, Track chapterTrack); |
|---|
| 250 | |
|---|
| 251 | // assumes cluster has been read already, and cycles through the contained blocks and |
|---|
| 252 | // adds the frames to the media/sample table, and to the track if addToTrack is true |
|---|
| 253 | void ImportCluster(KaxCluster &cluster, bool addToTrack); |
|---|
| 254 | |
|---|
| 255 | // we need to save a bit of context when seeking if we're going to seek back |
|---|
| 256 | // This function saves el_l1 and the current file position to the returned context |
|---|
| 257 | // and clears el_l1 to null in preparation for a seek. |
|---|
| 258 | MatroskaSeekContext SaveContext(); |
|---|
| 259 | |
|---|
| 260 | // This function restores el_l1 to what is saved in the context, deleting the current |
|---|
| 261 | // value if not null, and seeks to the specified point in the file. |
|---|
| 262 | void SetContext(MatroskaSeekContext context); |
|---|
| 263 | |
|---|
| 264 | // After the fonts are loaded, preplay the subtitle tracks to avoid an annoying pause |
|---|
| 265 | // from unavoidably lazy rendering APIs looking them up for the first time. |
|---|
| 266 | void PrerollSubtitleTracks(); |
|---|
| 267 | |
|---|
| 268 | ComponentInstance self; |
|---|
| 269 | Handle dataRef; |
|---|
| 270 | OSType dataRefType; |
|---|
| 271 | |
|---|
| 272 | Movie theMovie; |
|---|
| 273 | Track chapterTrack; |
|---|
| 274 | Track baseTrack; // fake track created to set the duration |
|---|
| 275 | // of a movie while idle importing |
|---|
| 276 | SInt64 timecodeScale; |
|---|
| 277 | TimeValue64 movieDuration; // in the timescale of timecodeScale |
|---|
| 278 | |
|---|
| 279 | IdleManager idleManager; |
|---|
| 280 | long loadState; |
|---|
| 281 | TimeValue lastIdleTime; // the playback time of the movie when last idled |
|---|
| 282 | int idlesSinceLastAdd; // number of idles since the last time |
|---|
| 283 | // samples were added to the tracks |
|---|
| 284 | |
|---|
| 285 | DataHandlerCallback *ioHandler; |
|---|
| 286 | EbmlStream *aStream; |
|---|
| 287 | |
|---|
| 288 | EbmlElement *el_l0; |
|---|
| 289 | EbmlElement *el_l1; |
|---|
| 290 | uint64_t segmentOffset; |
|---|
| 291 | |
|---|
| 292 | vector<MatroskaTrack> tracks; |
|---|
| 293 | vector<MatroskaSeek> levelOneElements; |
|---|
| 294 | |
|---|
| 295 | bool seenInfo; |
|---|
| 296 | bool seenTracks; |
|---|
| 297 | bool seenChapters; |
|---|
| 298 | }; |
|---|
| 299 | |
|---|
| 300 | #endif |
|---|