source: trunk/MatroskaImportPrivate.cpp @ 1181

Revision 1181, 41.1 KB checked in by astrange, 5 years ago (diff)

Support tracks with start time delays. Fixes #424.

Line 
1/*
2 *  MatroskaImportPrivate.cpp
3 *
4 *    MatroskaImportPrivate.cpp - C++ code for interfacing with libmatroska to import.
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#include <QuickTime/QuickTime.h>
26#include <vector>
27#include <string>
28
29#include <ebml/EbmlHead.h>
30#include <ebml/EbmlSubHead.h>
31#include <matroska/KaxSegment.h>
32#include <matroska/KaxSeekHead.h>
33#include <matroska/KaxInfo.h>
34#include <matroska/KaxInfoData.h>
35#include <matroska/KaxTracks.h>
36#include <matroska/KaxTrackVideo.h>
37#include <matroska/KaxTrackAudio.h>
38#include <matroska/KaxChapters.h>
39#include <matroska/KaxCluster.h>
40#include <matroska/KaxBlock.h>
41#include <matroska/KaxAttachments.h>
42#include <matroska/KaxAttached.h>
43
44#include "MatroskaImport.h"
45#include "MatroskaCodecIDs.h"
46#include "SubImport.h"
47#include "CommonUtils.h"
48#include "Codecprintf.h"
49#include "bitstream_info.h"
50
51extern "C" {
52#include "avutil.h"
53#define CodecType AVCodecType
54#include "ff_private.h"
55#undef CodecType       
56}
57
58using namespace std;
59using namespace libmatroska;
60
61bool MatroskaImport::OpenFile()
62{
63        bool valid = true;
64        int upperLevel = 0;
65       
66        ioHandler = new DataHandlerCallback(dataRef, dataRefType, MODE_READ);
67       
68        aStream = new EbmlStream(*ioHandler);
69       
70        el_l0 = aStream->FindNextID(EbmlHead::ClassInfos, ~0);
71        if (el_l0 != NULL) {
72                EbmlElement *dummyElt = NULL;
73               
74                el_l0->Read(*aStream, EbmlHead::ClassInfos.Context, upperLevel, dummyElt, true);
75                EbmlHead *head = static_cast<EbmlHead *>(el_l0);
76               
77                EDocType docType = GetChild<EDocType>(*head);
78                if (string(docType) != "matroska") {
79                        Codecprintf(NULL, "Not a Matroska file\n");
80                        valid = false;
81                }
82               
83                EDocTypeReadVersion readVersion = GetChild<EDocTypeReadVersion>(*head);
84                if (UInt64(readVersion) > 2) {
85                        Codecprintf(NULL, "Matroska file too new to be read\n");
86                        valid = false;
87                }
88        } else {
89                Codecprintf(NULL, "Matroska file missing EBML Head\n");
90                valid = false;
91        }
92       
93        delete el_l0;
94        el_l0 = NULL;
95        return valid;
96}
97
98ComponentResult MatroskaImport::ProcessLevel1Element()
99{
100        int upperLevel = 0;
101        EbmlElement *dummyElt = NULL;
102       
103        if (EbmlId(*el_l1) == KaxInfo::ClassInfos.GlobalId) {
104                el_l1->Read(*aStream, KaxInfo::ClassInfos.Context, upperLevel, dummyElt, true);
105                return ReadSegmentInfo(*static_cast<KaxInfo *>(el_l1));
106                                                               
107        } else if (EbmlId(*el_l1) == KaxTracks::ClassInfos.GlobalId) {
108                el_l1->Read(*aStream, KaxTracks::ClassInfos.Context, upperLevel, dummyElt, true);
109                return ReadTracks(*static_cast<KaxTracks *>(el_l1));
110               
111        } else if (EbmlId(*el_l1) == KaxChapters::ClassInfos.GlobalId) {
112                el_l1->Read(*aStream, KaxChapters::ClassInfos.Context, upperLevel, dummyElt, true);
113                return ReadChapters(*static_cast<KaxChapters *>(el_l1));
114               
115        } else if (EbmlId(*el_l1) == KaxAttachments::ClassInfos.GlobalId) {
116                ComponentResult res;
117                el_l1->Read(*aStream, KaxAttachments::ClassInfos.Context, upperLevel, dummyElt, true);
118                res = ReadAttachments(*static_cast<KaxAttachments *>(el_l1));
119                PrerollSubtitleTracks();
120                return res;
121        } else if (EbmlId(*el_l1) == KaxSeekHead::ClassInfos.GlobalId) {
122                el_l1->Read(*aStream, KaxSeekHead::ClassInfos.Context, upperLevel, dummyElt, true);
123                return ReadMetaSeek(*static_cast<KaxSeekHead *>(el_l1));
124        }
125        return noErr;
126}
127
128ComponentResult MatroskaImport::SetupMovie()
129{
130        ComponentResult err = noErr;
131        // once we've read the Tracks and Segment Info elements and Chapters if it's in the seek head,
132        // we don't need to read any more of the file
133        bool done = false;
134       
135        el_l0 = aStream->FindNextID(KaxSegment::ClassInfos, ~0);
136        if (!el_l0) return err;         // nothing in the file
137       
138        segmentOffset = static_cast<KaxSegment *>(el_l0)->GetDataStart();
139       
140        SetAutoTrackAlternatesEnabled(theMovie, false);
141       
142        while (!done && NextLevel1Element()) {
143                if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
144                        // all header elements are before clusters in sane files
145                        done = true;
146                } else
147                        err = ProcessLevel1Element();
148               
149                if (err) return err;
150        }
151       
152        // some final setup of info across elements since they can come in any order
153        for (int i = 0; i < tracks.size(); i++) {
154                tracks[i].timecodeScale = timecodeScale;
155               
156                // chapter tracks have to be associated with other enabled tracks to display
157                if (chapterTrack) {
158                        AddTrackReference(tracks[i].theTrack, chapterTrack, kTrackReferenceChapterList, NULL);
159                }
160        }
161       
162        return err;
163}
164
165EbmlElement * MatroskaImport::NextLevel1Element()
166{
167        int upperLevel = 0;
168       
169        if (el_l1) {
170                el_l1->SkipData(*aStream, el_l1->Generic().Context);
171                delete el_l1;
172        }
173       
174        el_l1 = aStream->FindNextElement(el_l0->Generic().Context, upperLevel, 0xFFFFFFFFL, true);
175       
176        // dummy element -> probably corrupt file, search for next element in meta seek and continue from there
177        if (el_l1 && el_l1->IsDummy()) {
178                vector<MatroskaSeek>::iterator nextElt;
179                MatroskaSeek currElt;
180                currElt.segmentPos = el_l1->GetElementPosition();
181                currElt.idLength = currElt.ebmlID = 0;
182               
183                nextElt = find_if(levelOneElements.begin(), levelOneElements.end(), bind2nd(greater<MatroskaSeek>(), currElt));
184                if (nextElt != levelOneElements.end()) {
185                        SetContext(nextElt->GetSeekContext(segmentOffset));
186                        NextLevel1Element();
187                }
188        }
189       
190        return el_l1;
191}
192
193ComponentResult MatroskaImport::ReadSegmentInfo(KaxInfo &segmentInfo)
194{
195        if (seenInfo)
196                return noErr;
197       
198        KaxDuration & duration = GetChild<KaxDuration>(segmentInfo);
199        KaxTimecodeScale & timecodeScale = GetChild<KaxTimecodeScale>(segmentInfo);
200        KaxTitle & title = GetChild<KaxTitle>(segmentInfo);
201        KaxWritingApp & writingApp = GetChild<KaxWritingApp>(segmentInfo);
202       
203        movieDuration = Float64(duration);
204        this->timecodeScale = UInt64(timecodeScale);
205        SetMovieTimeScale(theMovie, S64Div(1000000000L, this->timecodeScale));
206       
207        QTMetaDataRef movieMetaData;
208        OSStatus err = QTCopyMovieMetaData(theMovie, &movieMetaData);
209        if (err == noErr) {
210                OSType key;
211                if (!title.IsDefaultValue()) {
212                        key = kQTMetaDataCommonKeyDisplayName;
213                        QTMetaDataAddItem(movieMetaData, 
214                                                          kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
215                                                          (UInt8 *)&key, sizeof(key), 
216                                                          (UInt8 *)UTFstring(title).GetUTF8().c_str(), 
217                                                          UTFstring(title).GetUTF8().size(), 
218                                                          kQTMetaDataTypeUTF8, NULL);
219                }
220                if (!writingApp.IsDefaultValue()) {
221                        key = kQTMetaDataCommonKeySoftware;
222                        QTMetaDataAddItem(movieMetaData, 
223                                                          kQTMetaDataStorageFormatQuickTime, kQTMetaDataKeyFormatCommon, 
224                                                          (UInt8 *)&key, sizeof(key), 
225                                                          (UInt8 *)UTFstring(writingApp).GetUTF8().c_str(), 
226                                                          UTFstring(writingApp).GetUTF8().size(), 
227                                                          kQTMetaDataTypeUTF8, NULL);
228                }
229                QTMetaDataRelease(movieMetaData);
230        }
231        seenInfo = true;
232        return noErr;
233}
234
235ComponentResult MatroskaImport::ReadTracks(KaxTracks &trackEntries)
236{
237        Track firstVideoTrack = NULL;
238        Track firstAudioTrack = NULL;
239        Track firstSubtitleTrack = NULL;
240        ComponentResult err = noErr;
241       
242        if (seenTracks)
243                return noErr;
244       
245        // Since creating a subtitle track requires a video track to have already been created
246    // (so that it can be sized to fit exactly over the video track), we go through the
247    // track entries in two passes, first to add audio/video, second to add subtitle tracks.
248    for (int pass = 1; pass <= 2; pass++) {
249                for (int i = 0; i < trackEntries.ListSize(); i++) {
250                        if (EbmlId(*trackEntries[i]) != KaxTrackEntry::ClassInfos.GlobalId)
251                                continue;
252                        KaxTrackEntry & track = *static_cast<KaxTrackEntry *>(trackEntries[i]);
253                        KaxTrackNumber & number = GetChild<KaxTrackNumber>(track);
254                        KaxTrackType & type = GetChild<KaxTrackType>(track);
255                        KaxTrackDefaultDuration & defaultDuration = GetChild<KaxTrackDefaultDuration>(track);
256                        KaxTrackFlagDefault & enabled = GetChild<KaxTrackFlagDefault>(track);
257                        KaxTrackFlagLacing & lacing = GetChild<KaxTrackFlagLacing>(track);
258                        MatroskaTrack mkvTrack;
259                       
260                        mkvTrack.number = uint16(number);
261                        mkvTrack.type = uint8(type);
262                        mkvTrack.defaultDuration = uint32(defaultDuration) / float(timecodeScale) + .5;
263                       
264                        switch (uint8(type)) {
265                                case track_video:
266                                        if (pass == 2) continue;
267                                        err = AddVideoTrack(track, mkvTrack);
268                                        if (err) return err;
269                                       
270                                        if (firstVideoTrack)
271                                                SetTrackAlternate(firstVideoTrack, mkvTrack.theTrack);
272                                        else
273                                                firstVideoTrack = mkvTrack.theTrack;
274                                        break;
275                                       
276                                case track_audio:
277                                        if (pass == 2) continue;
278                                        err = AddAudioTrack(track, mkvTrack);
279                                        if (err) return err;
280                                       
281                                        if (firstAudioTrack)
282                                                SetTrackAlternate(firstAudioTrack, mkvTrack.theTrack);
283                                        else
284                                                firstAudioTrack = mkvTrack.theTrack;
285                                        break;
286                                       
287                                case track_subtitle:
288                                        if (pass == 1) continue;
289                                        err = AddSubtitleTrack(track, mkvTrack);
290                                        if (err) return err;
291                                        if (mkvTrack.theTrack == NULL) continue;
292                                       
293                                        if (firstSubtitleTrack)
294                                                SetTrackAlternate(firstSubtitleTrack, mkvTrack.theTrack);
295                                        else
296                                                firstSubtitleTrack = mkvTrack.theTrack;
297                                        break;
298                                       
299                                case track_complex:
300                                case track_logo:
301                                case track_buttons:
302                                case track_control:
303                                        // not likely to be implemented soon
304                                default:
305                                        continue;
306                        }
307                       
308                        KaxTrackLanguage & trackLang = GetChild<KaxTrackLanguage>(track);
309                        KaxTrackName & trackName = GetChild<KaxTrackName>(track);
310                        KaxContentEncodings * encodings = FindChild<KaxContentEncodings>(track);
311                       
312                        if (encodings) {
313                                err = ReadContentEncodings(*encodings, mkvTrack);
314                                // just ignore the track if there's some problem with this element
315                                if (err) continue;
316                        }
317                       
318                        long qtLang = ISO639_2ToQTLangCode(string(trackLang).c_str());
319                        SetMediaLanguage(mkvTrack.theMedia, qtLang);
320                       
321                        mkvTrack.isEnabled = uint8(enabled);
322                        mkvTrack.usesLacing = uint8(lacing);
323                       
324                        if (!trackName.IsDefaultValue()) {
325                                QTMetaDataRef trackMetaData;
326                                err = QTCopyTrackMetaData(mkvTrack.theTrack, &trackMetaData);
327                               
328                                if (err == noErr) {
329                                        OSType key = 'name';
330                                        // QuickTime differentiates between the title of a track and its name
331                                        // so we set both
332                                        QTMetaDataAddItem(trackMetaData,
333                                                                          kQTMetaDataStorageFormatQuickTime,
334                                                                          kQTMetaDataKeyFormatCommon,
335                                                                          (UInt8 *)&key, sizeof(key),
336                                                                          (UInt8 *)UTFstring(trackName).GetUTF8().c_str(),
337                                                                          UTFstring(trackName).GetUTF8().size(),
338                                                                          kQTMetaDataTypeUTF8, NULL);
339                                       
340                                        QTMetaDataAddItem(trackMetaData,
341                                                                          kQTMetaDataStorageFormatUserData,
342                                                                          kQTMetaDataKeyFormatUserData,
343                                                                          (UInt8 *)&key, sizeof(key),
344                                                                          (UInt8 *)UTFstring(trackName).GetUTF8().c_str(),
345                                                                          UTFstring(trackName).GetUTF8().size(),
346                                                                          kQTMetaDataTypeUTF8, NULL);
347                                       
348                                        QTMetaDataRelease(trackMetaData);
349                                }
350                        }
351                        tracks.push_back(mkvTrack);
352                }
353        }
354       
355        for (int i = 0; i < tracks.size(); i++) {
356                SetTrackEnabled(tracks[i].theTrack, tracks[i].isEnabled);
357        }
358        // ensure that at least one track in each alternate group (type) is enabled
359        // ffmpeg used to write a TrackDefault of 0 for all tracks
360        for (int i = 0; i < tracks.size(); i++) {
361                if (!GetTrackEnabled(tracks[i].theTrack)) {
362                        Track alternate = GetTrackAlternate(tracks[i].theTrack);
363                        while (alternate != tracks[i].theTrack && !GetTrackEnabled(alternate))
364                                alternate = GetTrackAlternate(alternate);
365                        if (alternate == tracks[i].theTrack)
366                                SetTrackEnabled(tracks[i].theTrack, 1);
367                }
368        }
369        seenTracks = true;
370        return noErr;
371}
372
373ComponentResult MatroskaImport::ReadContentEncodings(KaxContentEncodings &encodings, MatroskaTrack &mkvTrack)
374{
375        KaxContentEncoding & encoding = GetChild<KaxContentEncoding>(encodings);
376        int scope = uint32(GetChild<KaxContentEncodingScope>(encoding));
377        int type = uint32(GetChild<KaxContentEncodingType>(encoding));
378       
379        if (scope != 1) {
380                Codecprintf(NULL, "Content encoding scope of %d not expected\n", scope);
381                return -1;
382        }
383        if (type != 0) {
384                Codecprintf(NULL, "Encrypted track\n");
385                return -2;
386        }
387       
388        KaxContentCompression & comp = GetChild<KaxContentCompression>(encoding);
389        int algo = uint32(GetChild<KaxContentCompAlgo>(comp));
390       
391        if (algo != 0)
392                Codecprintf(NULL, "MKV: warning, track compression algorithm %d not zlib\n", algo);
393       
394        if ((*mkvTrack.desc)->dataFormat != kSubFormatVobSub)
395                Codecprintf(NULL, "MKV: warning, compressed track %4.4s probably won't work (not VobSub)\n", &(*mkvTrack.desc)->dataFormat);
396       
397        Handle ext = NewHandle(1);
398        **ext = algo;
399       
400        if (mkvTrack.type == track_audio)
401                AddSoundDescriptionExtension((SoundDescriptionHandle)mkvTrack.desc, ext, kMKVCompressionExtension);
402        else
403                AddImageDescriptionExtension((ImageDescriptionHandle)mkvTrack.desc, ext, kMKVCompressionExtension);
404       
405        return noErr;
406}
407
408ComponentResult MatroskaImport::AddVideoTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
409{
410        ComponentResult err = noErr;
411        ImageDescriptionHandle imgDesc;
412        Fixed width, height;
413       
414        KaxTrackVideo &videoTrack = GetChild<KaxTrackVideo>(kaxTrack);
415        KaxVideoDisplayWidth & disp_width = GetChild<KaxVideoDisplayWidth>(videoTrack);
416        KaxVideoDisplayHeight & disp_height = GetChild<KaxVideoDisplayHeight>(videoTrack);
417        KaxVideoPixelWidth & pxl_width = GetChild<KaxVideoPixelWidth>(videoTrack);
418        KaxVideoPixelHeight & pxl_height = GetChild<KaxVideoPixelHeight>(videoTrack);
419       
420        // Use the PixelWidth if the DisplayWidth is not set
421        if (disp_width.ValueIsSet() && disp_height.ValueIsSet()) {
422                // some files ignore the spec and treat display width/height as a ratio, not as pixels
423                // so scale the display size to be at least as large as the pixel size here
424                // but don't let it be bigger in both dimensions
425                float horizRatio = float(uint32(pxl_width)) / uint32(disp_width);
426                float vertRatio = float(uint32(pxl_height)) / uint32(disp_height);
427               
428                if (vertRatio > horizRatio && vertRatio > 1) {
429                        width = FloatToFixed(uint32(disp_width) * vertRatio);
430                        height = FloatToFixed(uint32(disp_height) * vertRatio);
431                } else if (horizRatio > 1) {
432                        width = FloatToFixed(uint32(disp_width) * horizRatio);
433                        height = FloatToFixed(uint32(disp_height) * horizRatio);
434                } else {
435                        float dar = uint32(disp_width) / (float)uint32(disp_height);
436                        float p_ratio = uint32(pxl_width) / (float)uint32(pxl_height);
437                       
438                        if (dar > p_ratio) {
439                                width  = FloatToFixed(uint32(pxl_height) * dar);
440                                height = IntToFixed(uint32(pxl_height));
441                        } else {
442                                width  = IntToFixed(uint32(pxl_width));
443                                height = FloatToFixed(uint32(pxl_width) / dar);
444                        }
445                }                               
446        } else if (pxl_width.ValueIsSet() && pxl_height.ValueIsSet()) {
447                width = IntToFixed(uint32(pxl_width));
448                height = IntToFixed(uint32(pxl_height));
449        } else {
450                Codecprintf(NULL, "MKV: Video has unknown dimensions.\n");
451                return invalidTrack;
452        }
453               
454        mkvTrack.theTrack = NewMovieTrack(theMovie, width, height, kNoVolume);
455        if (mkvTrack.theTrack == NULL)
456                return GetMoviesError();
457       
458        mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'vide', GetMovieTimeScale(theMovie), dataRef, dataRefType);
459        if (mkvTrack.theMedia == NULL) {
460                DisposeMovieTrack(mkvTrack.theTrack);
461                return GetMoviesError();
462        }
463       
464        imgDesc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
465        (*imgDesc)->idSize = sizeof(ImageDescription);
466    (*imgDesc)->width = uint16(pxl_width);
467    (*imgDesc)->height = uint16(pxl_height);
468    (*imgDesc)->frameCount = 1;
469        (*imgDesc)->cType = MkvGetFourCC(&kaxTrack);
470    (*imgDesc)->depth = 24;
471    (*imgDesc)->clutID = -1;
472       
473        set_track_clean_aperture_ext(imgDesc, width, height, IntToFixed(uint32(pxl_width)), IntToFixed(uint32(pxl_height)));
474        set_track_colorspace_ext(imgDesc, width, height);
475        mkvTrack.desc = (SampleDescriptionHandle) imgDesc;
476       
477        // this sets up anything else needed in the description for the specific codec.
478        err = MkvFinishSampleDescription(&kaxTrack, (SampleDescriptionHandle) imgDesc, kToSampleDescription);
479        if (err) return err;
480       
481        // video tracks can have display offsets, so create a sample table
482        err = QTSampleTableCreateMutable(NULL, GetMovieTimeScale(theMovie), NULL, &mkvTrack.sampleTable);
483        if (err) return err;
484       
485        err = QTSampleTableAddSampleDescription(mkvTrack.sampleTable, mkvTrack.desc, 0, &mkvTrack.qtSampleDesc);
486               
487        return err;
488}
489
490ComponentResult MatroskaImport::AddAudioTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
491{
492        SoundDescriptionHandle sndDesc = NULL;
493        AudioStreamBasicDescription asbd = {0};
494        AudioChannelLayout acl = {0};
495    AudioChannelLayout *pacl = &acl;
496    ByteCount acl_size = sizeof(acl);
497        ByteCount ioSize = sizeof(asbd);
498        ByteCount cookieSize = 0;
499        Handle cookieH = NULL;
500        Ptr cookie = NULL;
501       
502        mkvTrack.theTrack = NewMovieTrack(theMovie, 0, 0, kFullVolume);
503        if (mkvTrack.theTrack == NULL)
504                return GetMoviesError();
505       
506        mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'soun', GetMovieTimeScale(theMovie), dataRef, dataRefType);
507        if (mkvTrack.theMedia == NULL) {
508                DisposeMovieTrack(mkvTrack.theTrack);
509                return GetMoviesError();
510        }
511       
512        KaxTrackAudio & audioTrack = GetChild<KaxTrackAudio>(kaxTrack);
513        KaxAudioSamplingFreq & sampleFreq = GetChild<KaxAudioSamplingFreq>(audioTrack);
514        KaxAudioChannels & numChannels = GetChild<KaxAudioChannels>(audioTrack);
515        KaxAudioBitDepth & bitDepth = GetChild<KaxAudioBitDepth>(audioTrack);
516       
517        if (bitDepth.ValueIsSet()) asbd.mBitsPerChannel = uint32(bitDepth);
518        asbd.mFormatID = MkvGetFourCC(&kaxTrack);
519        asbd.mSampleRate = Float64(sampleFreq);
520        asbd.mChannelsPerFrame = uint32(numChannels);
521       
522        MkvFinishAudioDescription(&kaxTrack, &cookieH, &asbd, &acl);
523        if (cookieH) {
524                cookie = *cookieH;
525                cookieSize = GetHandleSize(cookieH);
526        }
527       
528        // get more info about the codec
529        AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, cookieSize, cookie, &ioSize, &asbd);
530        if(asbd.mChannelsPerFrame == 0)
531                asbd.mChannelsPerFrame = 1;             // avoid a div by zero
532       
533        // FIXME mChannelLayoutTag == 0 is valid
534        // but we don't use channel position lists (yet) so it's safe for now
535        if (acl.mChannelLayoutTag == 0) acl = GetDefaultChannelLayout(&asbd);
536        if (acl.mChannelLayoutTag == 0) {
537                pacl = NULL;
538                acl_size = 0;
539        }
540       
541        OSStatus err = QTSoundDescriptionCreate(&asbd, pacl, acl_size, cookie, cookieSize, 
542                                                                                        kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndDesc);
543        DisposeHandle(cookieH);
544        if (err) {
545                Codecprintf(NULL, "Borked audio track entry, hoping we can parse the track for asbd\n");
546                DisposeHandle((Handle)sndDesc);
547                return noErr;
548        }
549       
550        mkvTrack.desc = (SampleDescriptionHandle) sndDesc;
551       
552        err = QTSampleTableCreateMutable(NULL, GetMovieTimeScale(theMovie), NULL, &mkvTrack.sampleTable);
553        if (err) return err;
554       
555        err = QTSampleTableAddSampleDescription(mkvTrack.sampleTable, mkvTrack.desc, 0, &mkvTrack.qtSampleDesc);
556        return err;
557}
558
559ComponentResult MatroskaImport::AddSubtitleTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
560{
561        Fixed trackWidth, trackHeight;
562        Rect movieBox;
563        MediaHandler mh;
564        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
565        mkvTrack.desc = (SampleDescriptionHandle) imgDesc;
566       
567        // we assume that a video track has already been created, so we can set the subtitle track
568        // to have the same dimensions as it. Note that this doesn't work so well with multiple
569        // video tracks with different dimensions; but QuickTime doesn't expect that; how should we handle them?
570        GetMovieBox(theMovie, &movieBox);
571        trackWidth = IntToFixed(movieBox.right - movieBox.left);
572        trackHeight = IntToFixed(movieBox.bottom - movieBox.top);
573       
574        (*imgDesc)->idSize = sizeof(ImageDescription);
575        (*imgDesc)->cType = MkvGetFourCC(&kaxTrack);
576        (*imgDesc)->frameCount = 1;
577        (*imgDesc)->depth = 32;
578        (*imgDesc)->clutID = -1;
579       
580        if ((*imgDesc)->cType == kSubFormatVobSub) {
581                int width, height;
582               
583                // bitmap width & height is in the codec private in text format
584                KaxCodecPrivate & idxFile = GetChild<KaxCodecPrivate>(kaxTrack);
585                string idxFileStr((char *)idxFile.GetBuffer(), idxFile.GetSize());
586                string::size_type loc = idxFileStr.find("size:", 0);
587                if (loc == string::npos)
588                        return -1;
589               
590                sscanf(&idxFileStr.c_str()[loc], "size: %dx%d", &width, &height);
591                (*imgDesc)->width = width;
592                (*imgDesc)->height = height;
593               
594                if (trackWidth == 0 || trackHeight == 0) {
595                        trackWidth = IntToFixed(width);
596                        trackHeight = IntToFixed(height);
597                }
598               
599                mkvTrack.theTrack = NewMovieTrack(theMovie, trackWidth, trackHeight, kNoVolume);
600                if (mkvTrack.theTrack == NULL)
601                        return GetMoviesError();
602               
603                mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'vide', GetMovieTimeScale(theMovie), dataRef, dataRefType);
604                if (mkvTrack.theMedia == NULL)
605                        return GetMoviesError();
606               
607                // finally, say that we're transparent
608                mh = GetMediaHandler(mkvTrack.theMedia);
609                MediaSetGraphicsMode(mh, graphicsModePreBlackAlpha, NULL);
610               
611                // subtitle tracks should be above the video track, which should be layer 0
612                SetTrackLayer(mkvTrack.theTrack, -1);
613               
614                mkvTrack.is_vobsub = true;
615               
616        } else if ((*imgDesc)->cType == kSubFormatUTF8 || (*imgDesc)->cType == kSubFormatSSA || (*imgDesc)->cType == kSubFormatASS) {
617                if ((*imgDesc)->cType == kSubFormatASS) (*imgDesc)->cType = kSubFormatSSA; // no real reason to treat them differently
618                UInt32 emptyDataRefExtension[2]; // XXX the various uses of this bit of code should be unified
619                mkvTrack.subDataRefHandler = NewHandleClear(sizeof(Handle) + 1);
620                emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2);
621                emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
622               
623                PtrAndHand(&emptyDataRefExtension[0], mkvTrack.subDataRefHandler, sizeof(emptyDataRefExtension));
624               
625                mkvTrack.theTrack = CreatePlaintextSubTrack(theMovie, imgDesc, GetMovieTimeScale(theMovie), mkvTrack.subDataRefHandler, HandleDataHandlerSubType, (*imgDesc)->cType, NULL, movieBox);
626                if (mkvTrack.theTrack == NULL)
627                        return GetMoviesError();
628               
629                mkvTrack.theMedia = GetTrackMedia(mkvTrack.theTrack);
630                mkvTrack.is_vobsub = false;
631
632                BeginMediaEdits(mkvTrack.theMedia);
633        } else {
634                Codecprintf(NULL, "MKV: Unsupported subtitle type\n");
635                return noErr;
636        }
637       
638        // this sets up anything else needed in the description for the specific codec.
639        return MkvFinishSampleDescription(&kaxTrack, (SampleDescriptionHandle) imgDesc, kToSampleDescription);
640}
641
642ComponentResult MatroskaImport::ReadChapters(KaxChapters &chapterEntries)
643{
644        KaxEditionEntry & edition = GetChild<KaxEditionEntry>(chapterEntries);
645        UInt32 emptyDataRefExtension[2];
646       
647        if (seenChapters)
648                return noErr;
649       
650        chapterTrack = NewMovieTrack(theMovie, 0, 0, kNoVolume);
651        if (chapterTrack == NULL) {
652                Codecprintf(NULL, "MKV: Error creating chapter track %d\n", GetMoviesError());
653                return GetMoviesError();
654        }
655       
656        // we use a handle data reference here because I don't see any way to add textual
657        // sample references (TextMediaAddTextSample() will behave the same as AddSample()
658        // in that it modifies the original file if that's the data reference of the media)
659        Handle dataRef = NewHandleClear(sizeof(Handle) + 1);
660
661        emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2);
662        emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
663       
664        PtrAndHand(&emptyDataRefExtension[0], dataRef, sizeof(emptyDataRefExtension));
665       
666        Media chapterMedia = NewTrackMedia(chapterTrack, TextMediaType, GetMovieTimeScale(theMovie), 
667                                                                           dataRef, HandleDataHandlerSubType);
668        if (chapterMedia == NULL) {
669                OSErr err = GetMoviesError();
670                Codecprintf(NULL, "MKV: Error creating chapter media %d\n", err);
671                DisposeMovieTrack(chapterTrack);
672                return err;
673        }
674       
675        // Name the chapter track "Chapters" for easy distinguishing
676        QTMetaDataRef trackMetaData;
677        OSErr err = QTCopyTrackMetaData(chapterTrack, &trackMetaData);
678        if (err == noErr) {
679                OSType key = kUserDataName;
680                string chapterName("Chapters");
681                QTMetaDataAddItem(trackMetaData, kQTMetaDataStorageFormatUserData,
682                                                  kQTMetaDataKeyFormatUserData, (UInt8 *)&key, sizeof(key),
683                                                  (UInt8 *)chapterName.c_str(), chapterName.size(),
684                                                  kQTMetaDataTypeUTF8, NULL);
685                QTMetaDataRelease(trackMetaData);
686        }
687       
688        BeginMediaEdits(chapterMedia);
689       
690        // tell the text media handler the upcoming text samples are
691        // encoded in Unicode with a byte order mark (BOM)
692        MediaHandler mediaHandler = GetMediaHandler(chapterMedia);
693        SInt32 dataPtr = kTextEncodingUnicodeDefault;
694        TextMediaSetTextSampleData(mediaHandler, &dataPtr, kTXNTextEncodingAttribute);
695       
696        KaxChapterAtom *chapterAtom = FindChild<KaxChapterAtom>(edition);
697        while (chapterAtom && chapterAtom->GetSize() > 0) {
698                AddChapterAtom(chapterAtom, chapterTrack);
699                chapterAtom = &GetNextChild<KaxChapterAtom>(edition, *chapterAtom);
700        }
701       
702        EndMediaEdits(chapterMedia);
703        SetTrackEnabled(chapterTrack, false);
704        seenChapters = true;
705        return noErr;
706}
707
708void MatroskaImport::AddChapterAtom(KaxChapterAtom *atom, Track chapterTrack)
709{
710        KaxChapterAtom *subChapter = FindChild<KaxChapterAtom>(*atom);
711        bool addThisChapter = true;
712       
713        // since QuickTime only supports linear chapter tracks (no nesting), only add chapter leaves
714        if (subChapter && subChapter->GetSize() > 0) {
715                while (subChapter && subChapter->GetSize() > 0) {
716                        KaxChapterFlagHidden &hideChapter = GetChild<KaxChapterFlagHidden>(*subChapter);
717                       
718                        if (!uint8_t(hideChapter)) {
719                                AddChapterAtom(subChapter, chapterTrack);
720                                addThisChapter = false;
721                        }
722                        subChapter = &GetNextChild(*atom, *subChapter);
723                }
724        } 
725        if (addThisChapter) {
726                // add the chapter to the track if it has no children
727                KaxChapterTimeStart & startTime = GetChild<KaxChapterTimeStart>(*atom);
728                KaxChapterDisplay & chapDisplay = GetChild<KaxChapterDisplay>(*atom);
729                KaxChapterString & chapString = GetChild<KaxChapterString>(chapDisplay);
730                MediaHandler mh = GetMediaHandler(GetTrackMedia(chapterTrack));
731                TimeValue start = UInt64(startTime) / timecodeScale;
732
733                if (start > movieDuration) {
734                        Codecprintf(NULL, "MKV: Chapter time is beyond the end of the file\n");
735                        return;
736                }
737               
738                Rect bounds = {0, 0, 0, 0};
739                TimeValue inserted;
740                OSErr err = TextMediaAddTextSample(mh, 
741                                                                                   const_cast<Ptr>(UTFstring(chapString).GetUTF8().c_str()), 
742                                                                                   UTFstring(chapString).GetUTF8().size(), 
743                                                                                   0, 0, 0, NULL, NULL, 
744                                                                                   teCenter, &bounds, dfClipToTextBox, 
745                                                                                   0, 0, 0, NULL, 1, &inserted);
746                if (err)
747                        Codecprintf(NULL, "MKV: Error adding text sample %d\n", err);
748                else {
749                        InsertMediaIntoTrack(chapterTrack, start, inserted, 1, fixed1);
750                }
751        }
752}
753
754ComponentResult MatroskaImport::ReadAttachments(KaxAttachments &attachments)
755{
756        KaxAttached *attachedFile = FindChild<KaxAttached>(attachments);
757       
758        while (attachedFile && attachedFile->GetSize() > 0) {
759                string fileMimeType = GetChild<KaxMimeType>(*attachedFile);
760                string fileName = UTFstring(GetChild<KaxFileName>(*attachedFile)).GetUTF8();
761               
762                /* The only attachments handled here are fonts, which currently can be truetype or opentype.
763                   application/x-* is probably not a permanent MIME type, but it is current practice... */
764                if (fileMimeType == "application/x-truetype-font" || fileMimeType == "application/x-font-otf") {
765                        KaxFileData & fontData = GetChild<KaxFileData>(*attachedFile);
766                       
767                        if (fontData.GetSize()) {
768                                ATSFontContainerRef container;
769                                ATSFontActivateFromMemory(fontData.GetBuffer(), fontData.GetSize(), kATSFontContextLocal, 
770                                                          kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &container);
771                        }
772                }
773               
774                bool isCoverArt = false, isJPEG;
775               
776                if (fileName == "cover.jpg") {
777                        isCoverArt = isJPEG = true;
778                } else if (fileName == "cover.png") {
779                        isCoverArt = true;
780                        isJPEG = false;
781                }
782               
783                if (isCoverArt) {
784                        KaxFileData & fileData = GetChild<KaxFileData>(*attachedFile);
785                        FourCharCode key = 'covr'; //iTunes cover art tag
786                        QTMetaDataRef movieMetaData;
787                        OSStatus err = QTCopyMovieMetaData(theMovie, &movieMetaData);
788
789                        err = QTMetaDataAddItem(movieMetaData, 
790                                                          kQTMetaDataStorageFormatiTunes, kQTMetaDataKeyFormatiTunesShortForm, 
791                                                          (UInt8 *)&key, sizeof(key),
792                                                          fileData.GetBuffer(), 
793                                                          fileData.GetSize(), 
794                                                          isJPEG ? kQTMetaDataTypeJPEGImage : kQTMetaDataTypePNGImage, NULL);
795                        if (err)
796                                Codecprintf(NULL, "MKV: Error adding cover art %d\n", err);
797                       
798                        QTMetaDataRelease(movieMetaData);
799                }
800               
801                attachedFile = &GetNextChild<KaxAttached>(attachments, *attachedFile);
802        }
803        return noErr;
804}
805
806ComponentResult MatroskaImport::ReadMetaSeek(KaxSeekHead &seekHead)
807{
808        ComponentResult err = noErr;
809        KaxSeek *seekEntry = FindChild<KaxSeek>(seekHead);
810       
811        // don't re-read a seek head that's already been read
812        uint64_t currPos = seekHead.GetElementPosition();
813        vector<MatroskaSeek>::iterator itr = levelOneElements.begin();
814        for (; itr != levelOneElements.end(); itr++) {
815                if (itr->GetID() == KaxSeekHead::ClassInfos.GlobalId && 
816                        itr->segmentPos + segmentOffset == currPos)
817                        return noErr;
818        }
819       
820        while (seekEntry && seekEntry->GetSize() > 0) {
821                MatroskaSeek newSeekEntry;
822                KaxSeekID & seekID = GetChild<KaxSeekID>(*seekEntry);
823                KaxSeekPosition & position = GetChild<KaxSeekPosition>(*seekEntry);
824                EbmlId elementID = EbmlId(seekID.GetBuffer(), seekID.GetSize());
825               
826                newSeekEntry.ebmlID = elementID.Value;
827                newSeekEntry.idLength = elementID.Length;
828                newSeekEntry.segmentPos = position;
829               
830                // recursively read seek heads that are pointed to by the current one
831                // as well as the level one elements we care about
832                if (elementID == KaxInfo::ClassInfos.GlobalId || 
833                        elementID == KaxTracks::ClassInfos.GlobalId || 
834                        elementID == KaxChapters::ClassInfos.GlobalId || 
835                        elementID == KaxAttachments::ClassInfos.GlobalId || 
836                        elementID == KaxSeekHead::ClassInfos.GlobalId) {
837                       
838                        MatroskaSeekContext savedContext = SaveContext();
839                        SetContext(newSeekEntry.GetSeekContext(segmentOffset));
840                        if (NextLevel1Element())
841                                err = ProcessLevel1Element();
842                       
843                        SetContext(savedContext);
844                        if (err) return err;
845                }
846               
847                levelOneElements.push_back(newSeekEntry);
848                seekEntry = &GetNextChild<KaxSeek>(seekHead, *seekEntry);
849        }
850       
851        sort(levelOneElements.begin(), levelOneElements.end());
852       
853        return noErr;
854}
855
856void MatroskaImport::ImportCluster(KaxCluster &cluster, bool addToTrack)
857{
858        KaxSegment & segment = *static_cast<KaxSegment *>(el_l0);
859        KaxClusterTimecode & clusterTime = GetChild<KaxClusterTimecode>(cluster);
860        CXXAutoreleasePool pool;
861                               
862        cluster.SetParent(segment);
863        cluster.InitTimecode(uint64(clusterTime), timecodeScale);
864       
865        for (int i = 0; i < cluster.ListSize(); i++) {
866                const EbmlId & elementID = EbmlId(*cluster[i]);
867                KaxInternalBlock *block = NULL;
868                uint32_t duration = 0;          // set to track's default duration in AddBlock if 0
869                short flags = 0;
870               
871                if (elementID == KaxBlockGroup::ClassInfos.GlobalId) {
872                        KaxBlockGroup & blockGroup = *static_cast<KaxBlockGroup *>(cluster[i]);
873                        KaxBlockDuration & blkDuration = GetChild<KaxBlockDuration>(blockGroup);
874                        block = &GetChild<KaxBlock>(blockGroup);
875                        if (blkDuration.ValueIsSet())
876                                duration = uint32(blkDuration);
877                        flags = blockGroup.ReferenceCount() > 0 ? mediaSampleNotSync : 0;
878                       
879                } else if (elementID == KaxSimpleBlock::ClassInfos.GlobalId) {
880                        KaxSimpleBlock & simpleBlock = *static_cast<KaxSimpleBlock *>(cluster[i]);
881                        block = &simpleBlock;
882                        if (!simpleBlock.IsKeyframe())
883                                flags |= mediaSampleNotSync;
884                        if (simpleBlock.IsDiscardable() && IsFrameDroppingEnabled())
885                                flags |= mediaSampleDroppable;
886                }
887               
888                if (block) {
889                        block->SetParent(cluster);
890                       
891                        for (int i = 0; i < tracks.size(); i++) {
892                                if (tracks[i].number == block->TrackNum()) {
893                                        tracks[i].AddBlock(*block, duration, flags);
894                                        break;
895                                }
896                        }
897                }
898        }
899       
900        if (addToTrack) {
901                for (int i = 0; i < tracks.size(); i++)
902                        tracks[i].AddSamplesToTrack();
903               
904                loadState = kMovieLoadStatePlayable;
905        }
906}
907
908MatroskaSeekContext MatroskaImport::SaveContext()
909{
910        MatroskaSeekContext ret = { el_l1, ioHandler->getFilePointer() };
911        el_l1 = NULL;
912        return ret;
913}
914
915void MatroskaImport::SetContext(MatroskaSeekContext context)
916{
917        if (el_l1)
918                delete el_l1;
919       
920        el_l1 = context.el_l1;
921        ioHandler->setFilePointer(context.position);
922}
923
924void MatroskaImport::PrerollSubtitleTracks()
925{
926        if (!seenTracks) return;
927               
928        for (int i = 0; i < tracks.size(); i++) {
929                MatroskaTrack *track = &tracks[i];
930               
931                if (track->type == track_subtitle) {
932                        Handle subtitleDescriptionExt;
933                        OSErr err = GetImageDescriptionExtension((ImageDescriptionHandle)track->desc, &subtitleDescriptionExt, kSubFormatSSA, 1);
934                       
935                        if (err || !subtitleDescriptionExt) continue;
936                       
937                        SubPrerollFromHeader(*subtitleDescriptionExt, GetHandleSize(subtitleDescriptionExt));
938                }
939        }
940}
941
942MatroskaTrack::MatroskaTrack()
943{
944        number = 0;
945        type = -1;
946        theTrack = NULL;
947        theMedia = NULL;
948        desc = NULL;
949        sampleTable = NULL;
950        qtSampleDesc = 0;
951        timecodeScale = 1000000;
952        maxLoadedTime = 0;
953        seenFirstBlock = false;
954        firstSample = -1;
955        numSamples = 0;
956        durationToAdd = 0;
957        displayOffsetSum = 0;
958        durationSinceZeroSum = 0;
959        subtitleSerializer = new CXXSubSerializer;
960        subDataRefHandler = NULL;
961        is_vobsub = false;
962        isEnabled = true;
963        defaultDuration = 0;
964        usesLacing = true;
965        currentFrame = 0;
966}
967
968MatroskaTrack::MatroskaTrack(const MatroskaTrack &copy)
969{
970        number = copy.number;
971        type = copy.type;
972        theTrack = copy.theTrack;
973        theMedia = copy.theMedia;
974       
975        if (copy.desc) {
976                desc = (SampleDescriptionHandle) NewHandle((*copy.desc)->descSize);
977                memcpy(*desc, *copy.desc, (*copy.desc)->descSize);
978        } else
979                desc = NULL;
980       
981        sampleTable = copy.sampleTable;
982        if (sampleTable)
983                QTSampleTableRetain(sampleTable);
984       
985        qtSampleDesc = copy.qtSampleDesc;
986        timecodeScale = copy.timecodeScale;
987        maxLoadedTime = copy.maxLoadedTime;
988       
989        for (int i = 0; i < copy.lastFrames.size(); i++)
990                lastFrames.push_back(copy.lastFrames[i]);
991       
992        seenFirstBlock = copy.seenFirstBlock;
993        firstSample = copy.firstSample;
994        numSamples = copy.numSamples;
995        durationToAdd = copy.durationToAdd;
996        displayOffsetSum = copy.displayOffsetSum;
997        durationSinceZeroSum = copy.durationSinceZeroSum;
998       
999        subtitleSerializer = copy.subtitleSerializer;
1000        subtitleSerializer->retain();
1001               
1002        subDataRefHandler = copy.subDataRefHandler;
1003       
1004        is_vobsub = copy.is_vobsub;
1005        isEnabled = copy.isEnabled;
1006        defaultDuration = copy.defaultDuration;
1007        usesLacing = copy.usesLacing;
1008        currentFrame = copy.currentFrame;
1009}
1010
1011MatroskaTrack::~MatroskaTrack()
1012{
1013        if (desc)
1014                DisposeHandle((Handle) desc);
1015       
1016        if (sampleTable)
1017                QTSampleTableRelease(sampleTable);
1018       
1019        if (subtitleSerializer)
1020                subtitleSerializer->release();
1021}
1022
1023void MatroskaTrack::ParseFirstBlock(KaxInternalBlock &block)
1024{
1025        AudioStreamBasicDescription asbd = {0};
1026        AudioChannelLayout acl = {0};
1027        bool replaceSoundDesc = false;
1028       
1029        lowestPTS = block.GlobalTimecode();
1030       
1031        if (desc) {
1032                switch ((*desc)->dataFormat) {
1033                        case kAudioFormatAC3:
1034                                replaceSoundDesc = parse_ac3_bitstream(&asbd, &acl, block.GetBuffer(0).Buffer(), block.GetFrameSize(0));
1035                                break;
1036                }
1037        }
1038       
1039        if (replaceSoundDesc) {
1040                // successful in parsing, so the acl and asbd are more correct than what we generated in
1041                // AddAudioTrack() so replace our sound description
1042                SoundDescriptionHandle sndDesc = NULL;
1043               
1044                OSStatus err = QTSoundDescriptionCreate(&asbd, &acl, sizeof(AudioChannelLayout), NULL, 0, 
1045                                                        kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndDesc);
1046                if (err == noErr) {
1047                        DisposeHandle((Handle) desc);
1048                        desc = (SampleDescriptionHandle) sndDesc;
1049                        err = QTSampleTableAddSampleDescription(sampleTable, desc, 0, &qtSampleDesc);
1050                }
1051        }
1052}
1053
1054void MatroskaTrack::AddBlock(KaxInternalBlock &block, uint32 duration, short flags)
1055{
1056        if (!seenFirstBlock) {
1057                ParseFirstBlock(block);
1058                seenFirstBlock = true;
1059        }
1060       
1061        // tracks w/ lacing can't have b-frames, and neither can any known audio codec
1062        if (usesLacing || type != track_video) {
1063                // don't add the blocks until we get one with a new timecode
1064                TimeValue64 duration = 0;
1065                if (lastFrames.size() > 0)
1066                        duration = block.GlobalTimecode() / timecodeScale - lastFrames[0].pts;
1067               
1068                if (duration > 0) {
1069                        for (int i = 0; i < lastFrames.size(); i++) {
1070                                // since there can be multiple frames in one block, split the duration evenly between them
1071                                // giving the remainder to the latter blocks
1072                                int remainder = duration % lastFrames.size() >= lastFrames.size() - i ? 1 : 0;
1073                               
1074                                lastFrames[i].duration = duration / lastFrames.size() + remainder;
1075                               
1076                                AddFrame(lastFrames[i]);
1077                        }
1078                        lastFrames.clear();
1079                }
1080        } else if (ptsReorder.size() - currentFrame > MAX_DECODE_DELAY + 1) {
1081                map<TimeValue64, TimeValue>::iterator duration;
1082                MatroskaFrame &curr = lastFrames[currentFrame];
1083                MatroskaFrame &next = lastFrames[currentFrame+1];
1084                currentFrame++;
1085               
1086                // pts -> dts works this way: we assume that the first frame has correct dts
1087                // (we start at a keyframe, so pts = dts), and then we fill up a buffer with
1088                // frames until we have the frame whose pts is equal to the next dts
1089                // Then, we sort this buffer, extract the pts as dts, and calculate the duration.
1090               
1091                ptsReorder.sort();
1092                next.dts = *(++ptsReorder.begin());
1093                ptsReorder.pop_front();
1094               
1095                // Duration calculation has to be done between consecutive pts. Since we reorder
1096                // the pts into the dts, which have to be in order, we calculate the duration then
1097                // from the dts, then save it for the frame with the same pts.
1098               
1099                durationForPTS[curr.dts] = next.dts - curr.dts;
1100               
1101                duration = durationForPTS.find(lastFrames[0].pts);
1102                if (duration != durationForPTS.end()) {
1103                        lastFrames[0].duration = duration->second;
1104                        AddFrame(lastFrames[0]);
1105                        lastFrames.erase(lastFrames.begin());
1106                        durationForPTS.erase(duration);
1107                        currentFrame--;
1108                }
1109        }
1110       
1111        for (int i = 0; i < block.NumberFrames(); i++) {
1112                MatroskaFrame newFrame;
1113                newFrame.pts = block.GlobalTimecode() / timecodeScale;
1114                newFrame.dts = newFrame.pts;
1115                if (duration > 0)
1116                        newFrame.duration = duration;
1117                else
1118                        newFrame.duration = defaultDuration;
1119                newFrame.offset = block.GetDataPosition(i);
1120                newFrame.size = block.GetFrameSize(i);
1121                newFrame.flags = flags;
1122
1123                if (type == track_subtitle) {
1124                        newFrame.buffer = &block.GetBuffer(i);
1125                        AddFrame(newFrame);
1126                }
1127                else {
1128                        lastFrames.push_back(newFrame);
1129                        if (!usesLacing && type == track_video)
1130                                ptsReorder.push_back(newFrame.pts);
1131                }
1132               
1133                newFrame.buffer = NULL;
1134        }
1135}
1136
1137void MatroskaTrack::AddFrame(MatroskaFrame &frame)
1138{
1139        ComponentResult err = noErr;
1140        TimeValue sampleTime;
1141        TimeValue64 displayOffset = frame.pts - frame.dts;
1142       
1143        if (desc == NULL) return;
1144       
1145        if (type == track_subtitle && !is_vobsub) {
1146                const char *packet=NULL; size_t size=0; unsigned start=0, end=0;
1147               
1148                if (frame.size > 0)
1149                        subtitleSerializer->pushLine((const char*)frame.buffer->Buffer(), frame.buffer->Size(), frame.pts, frame.pts + frame.duration);
1150
1151                packet = subtitleSerializer->popPacket(&size, &start, &end);
1152                if (packet) {
1153                        Handle sampleH;
1154                        PtrToHand(packet, &sampleH, size);
1155                        err = AddMediaSample(theMedia, sampleH, 0, size, end - start, desc, 1, 0, &sampleTime);
1156                        if (err) {
1157                                Codecprintf(NULL, "MKV: error adding subtitle sample %d\n", err);
1158                                return;
1159                        }
1160                        DisposeHandle(sampleH);
1161                        frame.pts = start;
1162                        frame.duration = end - start;
1163                } else return;
1164        } else if (sampleTable) {
1165                SInt64 sampleNum;
1166               
1167                err = QTSampleTableAddSampleReferences(sampleTable, frame.offset, frame.size, frame.duration, 
1168                                                                                           displayOffset, 1, frame.flags, qtSampleDesc, &sampleNum);
1169                if (err) {
1170                        Codecprintf(NULL, "MKV: error adding sample reference to table %d\n", err);
1171                        return;
1172                }
1173               
1174                if (firstSample == -1)
1175                        firstSample = sampleNum;
1176                numSamples++;
1177        } else {
1178                SampleReference64Record sample;
1179                sample.dataOffset = SInt64ToWide(frame.offset);
1180                sample.dataSize = frame.size;
1181                sample.durationPerSample = frame.duration;
1182                sample.numberOfSamples = 1;
1183                sample.sampleFlags = frame.flags;
1184               
1185                err = AddMediaSampleReferences64(theMedia, desc, 1, &sample, &sampleTime);
1186                if (err) {
1187                        Codecprintf(NULL, "MKV: error adding sample reference to media %d\n", err);
1188                        return;
1189                }
1190        }
1191       
1192        // add to track immediately if subtitle, otherwise we let it be added elsewhere when we can do several at once
1193        if (type == track_subtitle) {
1194                err = InsertMediaIntoTrack(theTrack, frame.pts, sampleTime, frame.duration, fixed1);
1195                if (err) {
1196                        Codecprintf(NULL, "MKV: error adding subtitle media into track %d\n", err);
1197                        return;
1198                }
1199        } else {
1200                durationSinceZeroSum += frame.duration;
1201                displayOffsetSum += displayOffset;
1202                if (displayOffsetSum == 0) {
1203                        durationToAdd += durationSinceZeroSum;
1204                        durationSinceZeroSum = 0;
1205                }
1206        }
1207}
1208
1209void MatroskaTrack::AddSamplesToTrack()
1210{
1211        OSStatus err = noErr;
1212       
1213        if (type == track_subtitle)
1214                return;                 // handled in AddFrame()
1215
1216        if (durationToAdd == 0 && numSamples == 0)
1217                // nothing to add
1218                return;
1219       
1220        if (sampleTable) {
1221                if (firstSample == -1)
1222                        return;         // nothing to add
1223               
1224                err = AddSampleTableToMedia(theMedia, sampleTable, firstSample, numSamples, NULL);
1225                firstSample = -1;
1226                numSamples = 0;
1227                if (err) {
1228                        Codecprintf(NULL, "MKV: error adding sample table to the media %d\n", err);
1229                        durationToAdd = 0;
1230                        return;
1231                }
1232        }
1233       
1234        err = InsertMediaIntoTrack(theTrack, -1, maxLoadedTime, durationToAdd, fixed1);
1235        if (err)
1236                Codecprintf(NULL, "MKV: error inserting media into track %d\n", err);
1237       
1238        if (!err) {
1239                if (!maxLoadedTime && lowestPTS)
1240                        SetTrackOffset(theTrack, lowestPTS / timecodeScale);
1241               
1242                maxLoadedTime += durationToAdd;
1243        }
1244       
1245        durationToAdd = 0;
1246}
1247
1248void MatroskaTrack::FinishTrack()
1249{
1250        CXXAutoreleasePool pool;
1251       
1252        if (type == track_subtitle && !is_vobsub)
1253        {
1254                 subtitleSerializer->setFinished();
1255                 do {
1256                         MatroskaFrame fr = {0};
1257                         AddFrame(fr); // add empty frames to flush the subtitle packet queue
1258                 } while (!subtitleSerializer->empty());
1259                 EndMediaEdits(theMedia);
1260        } else {
1261                AddSamplesToTrack();
1262        }
1263}
Note: See TracBrowser for help on using the repository browser.