source: trunk/MatroskaImport.h @ 1181

Revision 1181, 10.7 KB checked in by astrange, 4 years ago (diff)

Support tracks with start time delays. Fixes #424.

Line 
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
45using namespace libmatroska;
46using 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
54struct 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
64struct MatroskaSeekContext {
65        EbmlElement             *el_l1;
66        uint64_t                position;
67};
68
69// a list of level one elements and their offsets in the segment
70class MatroskaSeek {
71public:
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
85class MatroskaTrack {
86public:
87        MatroskaTrack();
88       
89        // retains the sampleTable if it exists, allocates a new SampleDescriptionHandle
90        // and copies it if necessary
91        MatroskaTrack(const MatroskaTrack &copy);
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       
124private:
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
169class MatroskaImport {
170public:
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       
204private:
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
Note: See TracBrowser for help on using the repository browser.