Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
* MatroskaImport.h
* MatroskaImport.h - QuickTime importer interface for opening a Matroska file.
* Copyright (c) 2006 David Conrad
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <vector>
#include <list>
#include <map>
#include <QuickTime/QuickTime.h>
#include "DataHandlerCallback.h"
#include "SubImport.h"
#include <ebml/EbmlStream.h>
#include <matroska/KaxSeekHead.h>
#include <matroska/KaxInfo.h>
#include <matroska/KaxTracks.h>
#include <matroska/KaxChapters.h>
#include <matroska/KaxBlock.h>
#include <matroska/KaxAttachments.h>
#include <matroska/KaxContentEncoding.h>
using namespace libmatroska;
using namespace std;
// the maximum number of frames that have to be decoded between decoding
// any single frame and displaying it, used to cap the maximum size of the
// pts -> dts reorder buffer. Set to 4 since that's what it is in FFmpeg;
// shouldn't be more than 2 with current codecs
struct MatroskaFrame {
TimeValue64 dts; // decode timestamp
TimeValue64 pts; // presentation/display timestamp
TimeValue duration;
SInt64 offset;
SInt64 size;
short flags;
DataBuffer *buffer;
struct MatroskaSeekContext {
EbmlElement *el_l1;
uint64_t position;
// a list of level one elements and their offsets in the segment
class MatroskaSeek {
EbmlId GetID() const { return EbmlId(ebmlID, idLength); }
bool operator<(const MatroskaSeek &rhs) const { return segmentPos < rhs.segmentPos; }
bool operator>(const MatroskaSeek &rhs) const { return segmentPos > rhs.segmentPos; }
MatroskaSeekContext GetSeekContext(uint64_t segmentOffset = 0) const {
return (MatroskaSeekContext){ NULL, segmentPos + segmentOffset };
uint32_t ebmlID;
uint8_t idLength;
uint64_t segmentPos;
class MatroskaTrack {
// retains the sampleTable if it exists, allocates a new SampleDescriptionHandle
// and copies it if necessary
MatroskaTrack(const MatroskaTrack &copy);
// releases the sampleTable and sample description
// adds the all the frames in the block group to the sample table if it exists,
// the media otherwise. If this track type is subtitle, also inserts it into the track.
void AddBlock(KaxInternalBlock &block, uint32 frameDuration, short flags);
// this adds all the samples added through AddBlock() to the track that aren't already
// added, e.g. from a previous call to AddSamplesToTrack()
void AddSamplesToTrack();
void FinishTrack();
UInt16 number;
UInt8 type, is_vobsub;
Track theTrack;
Media theMedia;
SampleDescriptionHandle desc;
QTMutableSampleTableRef sampleTable;
QTSampleDescriptionID qtSampleDesc;
SInt64 timecodeScale;
TimeValue64 maxLoadedTime;
CXXSubSerializer *subtitleSerializer;
Handle subDataRefHandler;
uint8 isEnabled;
uint32_t defaultDuration;
// laced tracks can have multiple frames per block, it's easier to ignore them
// for pts -> dts conversion (and laced tracks can't have non-keyframes anyways)
bool usesLacing;
// adds an individual frame from a block group into the sample table if it exists,
// the media otherwise, and into the track if the track is a subtitle track.
void AddFrame(MatroskaFrame &frame);
// parses the first frame of a supported data format to determine codec parameters,
// which can be more correct than the codec headers.
void ParseFirstBlock(KaxInternalBlock &block);
// Since the duration in Matroska files is generally rather unreliable, rely only on
// the difference in timestamps between two frames. Thus, AddBlock() buffers frames
// from one block group until the next block group is found to set the duration of the
// previous ones to be the difference in timestamps.
vector<MatroskaFrame> lastFrames;
int currentFrame; // the frame we're currently determining the dts for
// insert pts values, sort, then smallest value is current dts if size > decode delay
list<TimeValue64> ptsReorder;
// We calculate the duration at a given dts, and have to save it until we find a
// frame with the same pts. This makes it such that we are actually calculating
// the duration between display timestamps instead of decode timestamps.
map<TimeValue64, TimeValue> durationForPTS;
bool seenFirstBlock;
// When using a sample table, these store the range of samples that are in the
// sample table but not yet added to the media.
SInt64 firstSample; // -1 means it's not set
SInt64 numSamples;
// the amount of the media that needs to be added to the track
TimeValue64 durationToAdd;
// We don't want to add regions with frames that are displayed after the region.
// Assume that when the sum of the display offsets is zero, this is true, and
// update durationToAdd by adding durationSinceZeroSum.
int displayOffsetSum;
SInt64 durationSinceZeroSum;
// the timestamp of the first block, used to set delay for the track
TimeValue64 lowestPTS;
class MatroskaImport {
// public interface functions, simply called from the C equivalents defined
// by ComponentDispatchHelper.c and implemented in MatroskaImport.cpp
// MatroskaImportOpen()
MatroskaImport(ComponentInstance self);
// MatrosakImportClose()
// MatroskaImportDataRef()
ComponentResult ImportDataRef(Handle dataRef, OSType dataRefType, Movie theMovie,
Track targetTrack, Track *usedTrack,
TimeValue atTime, TimeValue *durationAdded,
long inFlags, long *outFlags);
// MatroskaImportValidateDataRef()
ComponentResult ValidateDataRef(Handle dataRef, OSType dataRefType, UInt8 *valid);
// MatroskaImportIdle()
ComponentResult Idle(long inFlags, long *outFlags);
// MatroskaImportSetIdleManager()
ComponentResult SetIdleManager(IdleManager im);
// MatroskaImportGetMaxLoadedTime()
ComponentResult GetMaxLoadedTime(TimeValue *time);
// MatroskaImportGetLoadState()
ComponentResult GetLoadState(long *importerLoadState);
// we need to get our component instance to get our mime type resource
ComponentInstance Component() { return self; }
// open the ioHandler and EBML stream, and read the EBML head to verify it's a matroska file
// returns true if it's a valid file and false otherwise
bool OpenFile();
// create all the tracks and their sample descriptions as described by the file header
// also create chapters if any. Leaves el_l1 pointing to the first cluster, unread.
ComponentResult SetupMovie();
// This finds the next level 1 element and both replaces the el_l1 variable with it and
// returns it. Does not read the data.
EbmlElement *NextLevel1Element();
// reads the current level 1 element and calls Read<...> based on its ebml id
ComponentResult ProcessLevel1Element();
// sets up timescale & file name metadata
ComponentResult ReadSegmentInfo(KaxInfo &segmentInfo);
// sets up all the movie tracks and media
ComponentResult ReadTracks(KaxTracks &trackEntries);
// Creates a chapter track, but doesn't actually add the chapter reference to the other
// enabled tracks in case some weird file has this element before the Tracks element
ComponentResult ReadChapters(KaxChapters &chapterEntries);
// Activates any attached fonts, ignores other attachment types for now
ComponentResult ReadAttachments(KaxAttachments &attachments);
// Fills the levelOneElements vector with the positions of the elements in the seek head
ComponentResult ReadMetaSeek(KaxSeekHead &seekHead);
// Adds a sample description extension if the content is compressed
// should only be the case for VobSub data.
ComponentResult ReadVobSubContentEncodings(KaxContentEncodings *encodings, MatroskaTrack &mkvTrack);
// These three are called from ReadTracks to set up a track of the specific type,
// modifying the MatroskaTrack structure to reflect the newly create track.
// They return an error if the track couldn't be created or noErr on success.
ComponentResult AddVideoTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack, KaxContentEncodings *encodings);
ComponentResult AddAudioTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack, KaxContentEncodings *encodings);
ComponentResult AddSubtitleTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack, KaxContentEncodings *encodings);
// this is called recursively to add only the leaves on the chapter tree to
// chapter track, since QT doesn't support chapter nesting.
void AddChapterAtom(KaxChapterAtom *atom);
// assumes cluster has been read already, and cycles through the contained blocks and
// adds the frames to the media/sample table, and to the track if addToTrack is true
void ImportCluster(KaxCluster &cluster, bool addToTrack);
// we need to save a bit of context when seeking if we're going to seek back
// This function saves el_l1 and the current file position to the returned context
// and clears el_l1 to null in preparation for a seek.
MatroskaSeekContext SaveContext();
// This function restores el_l1 to what is saved in the context, deleting the current
// value if not null, and seeks to the specified point in the file.
void SetContext(MatroskaSeekContext context);
// After the fonts are loaded, preplay the subtitle tracks to avoid an annoying pause
// from unavoidably lazy rendering APIs looking them up for the first time.
void PrerollSubtitleTracks();
ComponentInstance self;
Handle dataRef;
OSType dataRefType;
Movie theMovie;
Track chapterTrack;
Track baseTrack; // fake track created to set the duration
// of a movie while idle importing
SInt64 timecodeScale;
TimeValue64 movieDuration; // in the timescale of timecodeScale
IdleManager idleManager;
long loadState;
TimeValue lastIdleTime; // the playback time of the movie when last idled
int idlesSinceLastAdd; // number of idles since the last time
// samples were added to the tracks
DataHandlerCallback *ioHandler;
EbmlStream *aStream;
EbmlElement *el_l0;
EbmlElement *el_l1;
uint64_t segmentOffset;
vector<MatroskaTrack> tracks;
vector<MatroskaSeek> levelOneElements;
bool seenInfo;
bool seenTracks;
bool seenChapters;