source: trunk/MatroskaImportPrivate.cpp @ 989

Revision 989, 39.5 KB checked in by gbooker, 6 years ago (diff)

Added a boolean for indicating whether the container is well framed. Apparently, some AVI files are framed well for the first sound frame, but not thereafter. This breaks AC3 passthrough of some files on the ATV.

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        seenTracks = true;
359        return noErr;
360}
361
362ComponentResult MatroskaImport::ReadContentEncodings(KaxContentEncodings &encodings, MatroskaTrack &mkvTrack)
363{
364        KaxContentEncoding & encoding = GetChild<KaxContentEncoding>(encodings);
365        int scope = uint32(GetChild<KaxContentEncodingScope>(encoding));
366        int type = uint32(GetChild<KaxContentEncodingType>(encoding));
367       
368        if (scope != 1) {
369                Codecprintf(NULL, "Content encoding scope of %d not expected\n", scope);
370                return -1;
371        }
372        if (type != 0) {
373                Codecprintf(NULL, "Encrypted track\n");
374                return -2;
375        }
376       
377        KaxContentCompression & comp = GetChild<KaxContentCompression>(encoding);
378        int algo = uint32(GetChild<KaxContentCompAlgo>(comp));
379       
380        if (algo != 0)
381                Codecprintf(NULL, "MKV: warning, track compression algorithm %d not zlib\n", algo);
382       
383        if ((*mkvTrack.desc)->dataFormat != kSubFormatVobSub)
384                Codecprintf(NULL, "MKV: warning, compressed track %4.4s probably won't work (not VobSub)\n", &(*mkvTrack.desc)->dataFormat);
385       
386        Handle ext = NewHandle(1);
387        **ext = algo;
388       
389        if (mkvTrack.type == track_audio)
390                AddSoundDescriptionExtension((SoundDescriptionHandle)mkvTrack.desc, ext, kMKVCompressionExtension);
391        else
392                AddImageDescriptionExtension((ImageDescriptionHandle)mkvTrack.desc, ext, kMKVCompressionExtension);
393       
394        return noErr;
395}
396
397ComponentResult MatroskaImport::AddVideoTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
398{
399        ComponentResult err = noErr;
400        ImageDescriptionHandle imgDesc;
401        Fixed width, height;
402       
403        KaxTrackVideo &videoTrack = GetChild<KaxTrackVideo>(kaxTrack);
404        KaxVideoDisplayWidth & disp_width = GetChild<KaxVideoDisplayWidth>(videoTrack);
405        KaxVideoDisplayHeight & disp_height = GetChild<KaxVideoDisplayHeight>(videoTrack);
406        KaxVideoPixelWidth & pxl_width = GetChild<KaxVideoPixelWidth>(videoTrack);
407        KaxVideoPixelHeight & pxl_height = GetChild<KaxVideoPixelHeight>(videoTrack);
408       
409        // Use the PixelWidth if the DisplayWidth is not set
410        if (disp_width.ValueIsSet() && disp_height.ValueIsSet()) {
411                // some files ignore the spec and treat display width/height as a ratio, not as pixels
412                // so scale the display size to be at least as large as the pixel size here
413                // but don't let it be bigger in both dimensions
414                float horizRatio = float(uint32(pxl_width)) / uint32(disp_width);
415                float vertRatio = float(uint32(pxl_height)) / uint32(disp_height);
416               
417                if (vertRatio > horizRatio && vertRatio > 1) {
418                        width = FloatToFixed(uint32(disp_width) * vertRatio);
419                        height = FloatToFixed(uint32(disp_height) * vertRatio);
420                } else if (horizRatio > 1) {
421                        width = FloatToFixed(uint32(disp_width) * horizRatio);
422                        height = FloatToFixed(uint32(disp_height) * horizRatio);
423                } else {
424                        float dar = uint32(disp_width) / (float)uint32(disp_height);
425                        float p_ratio = uint32(pxl_width) / (float)uint32(pxl_height);
426                       
427                        if (dar > p_ratio) {
428                                width  = FloatToFixed(uint32(pxl_height) * dar);
429                                height = IntToFixed(uint32(pxl_height));
430                        } else {
431                                width  = IntToFixed(uint32(pxl_width));
432                                height = FloatToFixed(uint32(pxl_width) / dar);
433                        }
434                }                               
435        } else if (pxl_width.ValueIsSet() && pxl_height.ValueIsSet()) {
436                width = IntToFixed(uint32(pxl_width));
437                height = IntToFixed(uint32(pxl_height));
438        } else {
439                Codecprintf(NULL, "MKV: Video has unknown dimensions.\n");
440                return invalidTrack;
441        }
442               
443        mkvTrack.theTrack = NewMovieTrack(theMovie, width, height, kNoVolume);
444        if (mkvTrack.theTrack == NULL)
445                return GetMoviesError();
446       
447        mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'vide', GetMovieTimeScale(theMovie), dataRef, dataRefType);
448        if (mkvTrack.theMedia == NULL) {
449                DisposeMovieTrack(mkvTrack.theTrack);
450                return GetMoviesError();
451        }
452       
453        imgDesc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
454        (*imgDesc)->idSize = sizeof(ImageDescription);
455    (*imgDesc)->width = uint16(pxl_width);
456    (*imgDesc)->height = uint16(pxl_height);
457    (*imgDesc)->frameCount = 1;
458        (*imgDesc)->cType = MkvGetFourCC(&kaxTrack);
459    (*imgDesc)->depth = 24;
460    (*imgDesc)->clutID = -1;
461       
462        set_track_clean_aperture_ext(imgDesc, width, height, IntToFixed(uint32(pxl_width)), IntToFixed(uint32(pxl_height)));
463        mkvTrack.desc = (SampleDescriptionHandle) imgDesc;
464       
465        // this sets up anything else needed in the description for the specific codec.
466        err = MkvFinishSampleDescription(&kaxTrack, (SampleDescriptionHandle) imgDesc, kToSampleDescription);
467        if (err) return err;
468       
469        // video tracks can have display offsets, so create a sample table
470        err = QTSampleTableCreateMutable(NULL, GetMovieTimeScale(theMovie), NULL, &mkvTrack.sampleTable);
471        if (err) return err;
472       
473        err = QTSampleTableAddSampleDescription(mkvTrack.sampleTable, mkvTrack.desc, 0, &mkvTrack.qtSampleDesc);
474               
475        return err;
476}
477
478ComponentResult MatroskaImport::AddAudioTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
479{
480        SoundDescriptionHandle sndDesc;
481        AudioStreamBasicDescription asbd = {0};
482        AudioChannelLayout acl = {0};
483    AudioChannelLayout *pacl = &acl;
484    ByteCount acl_size = sizeof(acl);
485        ByteCount ioSize = sizeof(asbd);
486        ByteCount cookieSize = 0;
487        Ptr magicCookie = NULL;
488       
489        mkvTrack.theTrack = NewMovieTrack(theMovie, 0, 0, kFullVolume);
490        if (mkvTrack.theTrack == NULL)
491                return GetMoviesError();
492       
493        mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'soun', GetMovieTimeScale(theMovie), dataRef, dataRefType);
494        if (mkvTrack.theMedia == NULL) {
495                DisposeMovieTrack(mkvTrack.theTrack);
496                return GetMoviesError();
497        }
498       
499        KaxTrackAudio & audioTrack = GetChild<KaxTrackAudio>(kaxTrack);
500        KaxAudioSamplingFreq & sampleFreq = GetChild<KaxAudioSamplingFreq>(audioTrack);
501        KaxAudioChannels & numChannels = GetChild<KaxAudioChannels>(audioTrack);
502        KaxAudioBitDepth & bitDepth = GetChild<KaxAudioBitDepth>(audioTrack);
503       
504        if (bitDepth.ValueIsSet()) asbd.mBitsPerChannel = uint32(bitDepth);
505        asbd.mFormatID = MkvGetFourCC(&kaxTrack);
506        asbd.mSampleRate = Float64(sampleFreq);
507        asbd.mChannelsPerFrame = uint32(numChannels);
508        asbd.mFramesPerPacket = 1;              // needed for mp3 and v1 SoundDescription, maybe others
509       
510        MkvFinishAudioDescriptions(&kaxTrack, &asbd, &acl);
511       
512        // get more info about the codec
513        AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &ioSize, &asbd);
514        if(asbd.mChannelsPerFrame == 0)
515                asbd.mChannelsPerFrame = 1;             // avoid a div by zero
516       
517        // FIXME mChannelLayoutTag == 0 is valid
518        // but we don't use channel position lists (yet) so it's safe for now
519        if (acl.mChannelLayoutTag == 0) acl = GetDefaultChannelLayout(&asbd);
520        if (acl.mChannelLayoutTag == 0) {
521                pacl = NULL;
522                acl_size = 0;
523        }
524       
525        OSStatus err = QTSoundDescriptionCreate(&asbd, pacl, acl_size, magicCookie, cookieSize, 
526                                                                                        kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndDesc);
527        if (err) {
528                Codecprintf(NULL, "Borked audio track entry, hoping we can parse the track for asbd\n");
529                DisposeHandle((Handle)sndDesc);
530                return noErr;
531        }
532       
533        mkvTrack.desc = (SampleDescriptionHandle) sndDesc;
534        err = MkvFinishSampleDescription(&kaxTrack, mkvTrack.desc, kToSampleDescription);
535        if (err) return err;
536       
537        err = QTSampleTableCreateMutable(NULL, GetMovieTimeScale(theMovie), NULL, &mkvTrack.sampleTable);
538        if (err) return err;
539       
540        err = QTSampleTableAddSampleDescription(mkvTrack.sampleTable, mkvTrack.desc, 0, &mkvTrack.qtSampleDesc);
541        return err;
542}
543
544ComponentResult MatroskaImport::AddSubtitleTrack(KaxTrackEntry &kaxTrack, MatroskaTrack &mkvTrack)
545{
546        Fixed trackWidth, trackHeight;
547        Rect movieBox;
548        MediaHandler mh;
549        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
550        mkvTrack.desc = (SampleDescriptionHandle) imgDesc;
551       
552        // we assume that a video track has already been created, so we can set the subtitle track
553        // to have the same dimensions as it. Note that this doesn't work so well with multiple
554        // video tracks with different dimensions; but QuickTime doesn't expect that; how should we handle them?
555        GetMovieBox(theMovie, &movieBox);
556        trackWidth = IntToFixed(movieBox.right - movieBox.left);
557        trackHeight = IntToFixed(movieBox.bottom - movieBox.top);
558       
559        (*imgDesc)->idSize = sizeof(ImageDescription);
560        (*imgDesc)->cType = MkvGetFourCC(&kaxTrack);
561        (*imgDesc)->frameCount = 1;
562        (*imgDesc)->depth = 32;
563        (*imgDesc)->clutID = -1;
564       
565        if ((*imgDesc)->cType == kSubFormatVobSub) {
566                int width, height;
567               
568                // bitmap width & height is in the codec private in text format
569                KaxCodecPrivate & idxFile = GetChild<KaxCodecPrivate>(kaxTrack);
570                string idxFileStr((char *)idxFile.GetBuffer(), idxFile.GetSize());
571                string::size_type loc = idxFileStr.find("size:", 0);
572                if (loc == string::npos)
573                        return -1;
574               
575                sscanf(&idxFileStr.c_str()[loc], "size: %dx%d", &width, &height);
576                (*imgDesc)->width = width;
577                (*imgDesc)->height = height;
578               
579                if (trackWidth == 0 || trackHeight == 0) {
580                        trackWidth = IntToFixed(width);
581                        trackHeight = IntToFixed(height);
582                }
583               
584                mkvTrack.theTrack = NewMovieTrack(theMovie, trackWidth, trackHeight, kNoVolume);
585                if (mkvTrack.theTrack == NULL)
586                        return GetMoviesError();
587               
588                mkvTrack.theMedia = NewTrackMedia(mkvTrack.theTrack, 'vide', GetMovieTimeScale(theMovie), dataRef, dataRefType);
589                if (mkvTrack.theMedia == NULL)
590                        return GetMoviesError();
591               
592                // finally, say that we're transparent
593                mh = GetMediaHandler(mkvTrack.theMedia);
594                MediaSetGraphicsMode(mh, graphicsModePreBlackAlpha, NULL);
595               
596                // subtitle tracks should be above the video track, which should be layer 0
597                SetTrackLayer(mkvTrack.theTrack, -1);
598               
599                mkvTrack.is_vobsub = true;
600               
601        } else if ((*imgDesc)->cType == kSubFormatUTF8 || (*imgDesc)->cType == kSubFormatSSA || (*imgDesc)->cType == kSubFormatASS) {
602                if ((*imgDesc)->cType == kSubFormatASS) (*imgDesc)->cType = kSubFormatSSA; // no real reason to treat them differently
603                UInt32 emptyDataRefExtension[2]; // XXX the various uses of this bit of code should be unified
604                mkvTrack.subDataRefHandler = NewHandleClear(sizeof(Handle) + 1);
605                emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2);
606                emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
607               
608                PtrAndHand(&emptyDataRefExtension[0], mkvTrack.subDataRefHandler, sizeof(emptyDataRefExtension));
609               
610                mkvTrack.theTrack = CreatePlaintextSubTrack(theMovie, imgDesc, GetMovieTimeScale(theMovie), mkvTrack.subDataRefHandler, HandleDataHandlerSubType, (*imgDesc)->cType, NULL, movieBox);
611                if (mkvTrack.theTrack == NULL)
612                        return GetMoviesError();
613               
614                mkvTrack.theMedia = GetTrackMedia(mkvTrack.theTrack);
615                mkvTrack.is_vobsub = false;
616
617                BeginMediaEdits(mkvTrack.theMedia);
618        } else {
619                Codecprintf(NULL, "MKV: Unsupported subtitle type\n");
620                return noErr;
621        }
622       
623        // this sets up anything else needed in the description for the specific codec.
624        return MkvFinishSampleDescription(&kaxTrack, (SampleDescriptionHandle) imgDesc, kToSampleDescription);
625}
626
627ComponentResult MatroskaImport::ReadChapters(KaxChapters &chapterEntries)
628{
629        KaxEditionEntry & edition = GetChild<KaxEditionEntry>(chapterEntries);
630        UInt32 emptyDataRefExtension[2];
631       
632        if (seenChapters)
633                return noErr;
634       
635        chapterTrack = NewMovieTrack(theMovie, 0, 0, kNoVolume);
636        if (chapterTrack == NULL) {
637                Codecprintf(NULL, "MKV: Error creating chapter track %d\n", GetMoviesError());
638                return GetMoviesError();
639        }
640       
641        // we use a handle data reference here because I don't see any way to add textual
642        // sample references (TextMediaAddTextSample() will behave the same as AddSample()
643        // in that it modifies the original file if that's the data reference of the media)
644        Handle dataRef = NewHandleClear(sizeof(Handle) + 1);
645
646        emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2);
647        emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData);
648       
649        PtrAndHand(&emptyDataRefExtension[0], dataRef, sizeof(emptyDataRefExtension));
650       
651        Media chapterMedia = NewTrackMedia(chapterTrack, TextMediaType, GetMovieTimeScale(theMovie), 
652                                                                           dataRef, HandleDataHandlerSubType);
653        if (chapterMedia == NULL) {
654                OSErr err = GetMoviesError();
655                Codecprintf(NULL, "MKV: Error creating chapter media %d\n", err);
656                DisposeMovieTrack(chapterTrack);
657                return err;
658        }
659       
660        // Name the chapter track "Chapters" for easy distinguishing
661        QTMetaDataRef trackMetaData;
662        OSErr err = QTCopyTrackMetaData(chapterTrack, &trackMetaData);
663        if (err == noErr) {
664                OSType key = kUserDataName;
665                string chapterName("Chapters");
666                QTMetaDataAddItem(trackMetaData, kQTMetaDataStorageFormatUserData,
667                                                  kQTMetaDataKeyFormatUserData, (UInt8 *)&key, sizeof(key),
668                                                  (UInt8 *)chapterName.c_str(), chapterName.size(),
669                                                  kQTMetaDataTypeUTF8, NULL);
670                QTMetaDataRelease(trackMetaData);
671        }
672       
673        BeginMediaEdits(chapterMedia);
674       
675        // tell the text media handler the upcoming text samples are
676        // encoded in Unicode with a byte order mark (BOM)
677        MediaHandler mediaHandler = GetMediaHandler(chapterMedia);
678        SInt32 dataPtr = kTextEncodingUnicodeDefault;
679        TextMediaSetTextSampleData(mediaHandler, &dataPtr, kTXNTextEncodingAttribute);
680       
681        KaxChapterAtom *chapterAtom = FindChild<KaxChapterAtom>(edition);
682        while (chapterAtom && chapterAtom->GetSize() > 0) {
683                AddChapterAtom(chapterAtom, chapterTrack);
684                chapterAtom = &GetNextChild<KaxChapterAtom>(edition, *chapterAtom);
685        }
686       
687        EndMediaEdits(chapterMedia);
688        SetTrackEnabled(chapterTrack, false);
689        seenChapters = true;
690        return noErr;
691}
692
693void MatroskaImport::AddChapterAtom(KaxChapterAtom *atom, Track chapterTrack)
694{
695        KaxChapterAtom *subChapter = FindChild<KaxChapterAtom>(*atom);
696        bool addThisChapter = true;
697       
698        // since QuickTime only supports linear chapter tracks (no nesting), only add chapter leaves
699        if (subChapter && subChapter->GetSize() > 0) {
700                while (subChapter && subChapter->GetSize() > 0) {
701                        KaxChapterFlagHidden &hideChapter = GetChild<KaxChapterFlagHidden>(*subChapter);
702                       
703                        if (!uint8_t(hideChapter)) {
704                                AddChapterAtom(subChapter, chapterTrack);
705                                addThisChapter = false;
706                        }
707                        subChapter = &GetNextChild(*atom, *subChapter);
708                }
709        } 
710        if (addThisChapter) {
711                // add the chapter to the track if it has no children
712                KaxChapterTimeStart & startTime = GetChild<KaxChapterTimeStart>(*atom);
713                KaxChapterDisplay & chapDisplay = GetChild<KaxChapterDisplay>(*atom);
714                KaxChapterString & chapString = GetChild<KaxChapterString>(chapDisplay);
715                MediaHandler mh = GetMediaHandler(GetTrackMedia(chapterTrack));
716                TimeValue start = UInt64(startTime) / timecodeScale;
717
718                if (start > movieDuration) {
719                        Codecprintf(NULL, "MKV: Chapter time is beyond the end of the file\n");
720                        return;
721                }
722               
723                Rect bounds = {0, 0, 0, 0};
724                TimeValue inserted;
725                OSErr err = TextMediaAddTextSample(mh, 
726                                                                                   const_cast<Ptr>(UTFstring(chapString).GetUTF8().c_str()), 
727                                                                                   UTFstring(chapString).GetUTF8().size(), 
728                                                                                   0, 0, 0, NULL, NULL, 
729                                                                                   teCenter, &bounds, dfClipToTextBox, 
730                                                                                   0, 0, 0, NULL, 1, &inserted);
731                if (err)
732                        Codecprintf(NULL, "MKV: Error adding text sample %d\n", err);
733                else {
734                        InsertMediaIntoTrack(chapterTrack, start, inserted, 1, fixed1);
735                }
736        }
737}
738
739ComponentResult MatroskaImport::ReadAttachments(KaxAttachments &attachments)
740{
741        KaxAttached *attachedFile = FindChild<KaxAttached>(attachments);
742       
743        while (attachedFile && attachedFile->GetSize() > 0) {
744                string fileMimeType = GetChild<KaxMimeType>(*attachedFile);
745                /* The only attachments handled here are fonts, which currently can be truetype or opentype.
746                   application/x-* is probably not a permanent MIME type, but it is current practice... */
747                if (fileMimeType == "application/x-truetype-font" || fileMimeType == "application/x-font-otf") {
748                        KaxFileData & fontData = GetChild<KaxFileData>(*attachedFile);
749                       
750                        if (fontData.GetSize()) {
751                                ATSFontContainerRef container;
752                                ATSFontActivateFromMemory(fontData.GetBuffer(), fontData.GetSize(), kATSFontContextLocal, 
753                                                          kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &container);
754                        }
755                }
756               
757                attachedFile = &GetNextChild<KaxAttached>(attachments, *attachedFile);
758        }
759        return noErr;
760}
761
762ComponentResult MatroskaImport::ReadMetaSeek(KaxSeekHead &seekHead)
763{
764        ComponentResult err = noErr;
765        KaxSeek *seekEntry = FindChild<KaxSeek>(seekHead);
766       
767        // don't re-read a seek head that's already been read
768        uint64_t currPos = seekHead.GetElementPosition();
769        vector<MatroskaSeek>::iterator itr = levelOneElements.begin();
770        for (; itr != levelOneElements.end(); itr++) {
771                if (itr->GetID() == KaxSeekHead::ClassInfos.GlobalId && 
772                        itr->segmentPos + segmentOffset == currPos)
773                        return noErr;
774        }
775       
776        while (seekEntry && seekEntry->GetSize() > 0) {
777                MatroskaSeek newSeekEntry;
778                KaxSeekID & seekID = GetChild<KaxSeekID>(*seekEntry);
779                KaxSeekPosition & position = GetChild<KaxSeekPosition>(*seekEntry);
780                EbmlId elementID = EbmlId(seekID.GetBuffer(), seekID.GetSize());
781               
782                newSeekEntry.ebmlID = elementID.Value;
783                newSeekEntry.idLength = elementID.Length;
784                newSeekEntry.segmentPos = position;
785               
786                // recursively read seek heads that are pointed to by the current one
787                // as well as the level one elements we care about
788                if (elementID == KaxInfo::ClassInfos.GlobalId || 
789                        elementID == KaxTracks::ClassInfos.GlobalId || 
790                        elementID == KaxChapters::ClassInfos.GlobalId || 
791                        elementID == KaxAttachments::ClassInfos.GlobalId || 
792                        elementID == KaxSeekHead::ClassInfos.GlobalId) {
793                       
794                        MatroskaSeekContext savedContext = SaveContext();
795                        SetContext(newSeekEntry.GetSeekContext(segmentOffset));
796                        if (NextLevel1Element())
797                                err = ProcessLevel1Element();
798                       
799                        SetContext(savedContext);
800                        if (err) return err;
801                }
802               
803                levelOneElements.push_back(newSeekEntry);
804                seekEntry = &GetNextChild<KaxSeek>(seekHead, *seekEntry);
805        }
806       
807        sort(levelOneElements.begin(), levelOneElements.end());
808       
809        return noErr;
810}
811
812void MatroskaImport::ImportCluster(KaxCluster &cluster, bool addToTrack)
813{
814        KaxSegment & segment = *static_cast<KaxSegment *>(el_l0);
815        KaxClusterTimecode & clusterTime = GetChild<KaxClusterTimecode>(cluster);
816        CXXAutoreleasePool pool;
817                               
818        cluster.SetParent(segment);
819        cluster.InitTimecode(uint64(clusterTime), timecodeScale);
820       
821        for (int i = 0; i < cluster.ListSize(); i++) {
822                const EbmlId & elementID = EbmlId(*cluster[i]);
823                KaxInternalBlock *block = NULL;
824                uint32_t duration = 0;          // set to track's default duration in AddBlock if 0
825                short flags = 0;
826               
827                if (elementID == KaxBlockGroup::ClassInfos.GlobalId) {
828                        KaxBlockGroup & blockGroup = *static_cast<KaxBlockGroup *>(cluster[i]);
829                        KaxBlockDuration & blkDuration = GetChild<KaxBlockDuration>(blockGroup);
830                        block = &GetChild<KaxBlock>(blockGroup);
831                        if (blkDuration.ValueIsSet())
832                                duration = uint32(blkDuration);
833                        flags = blockGroup.ReferenceCount() > 0 ? mediaSampleNotSync : 0;
834                       
835                } else if (elementID == KaxSimpleBlock::ClassInfos.GlobalId) {
836                        KaxSimpleBlock & simpleBlock = *static_cast<KaxSimpleBlock *>(cluster[i]);
837                        block = &simpleBlock;
838                        if (!simpleBlock.IsKeyframe())
839                                flags |= mediaSampleNotSync;
840                        if (simpleBlock.IsDiscardable() && IsFrameDroppingEnabled())
841                                flags |= mediaSampleDroppable;
842                }
843               
844                if (block) {
845                        block->SetParent(cluster);
846                       
847                        for (int i = 0; i < tracks.size(); i++) {
848                                if (tracks[i].number == block->TrackNum()) {
849                                        tracks[i].AddBlock(*block, duration, flags);
850                                        break;
851                                }
852                        }
853                }
854        }
855       
856        if (addToTrack) {
857                for (int i = 0; i < tracks.size(); i++)
858                        tracks[i].AddSamplesToTrack();
859               
860                loadState = kMovieLoadStatePlayable;
861        }
862}
863
864MatroskaSeekContext MatroskaImport::SaveContext()
865{
866        MatroskaSeekContext ret = { el_l1, ioHandler->getFilePointer() };
867        el_l1 = NULL;
868        return ret;
869}
870
871void MatroskaImport::SetContext(MatroskaSeekContext context)
872{
873        if (el_l1)
874                delete el_l1;
875       
876        el_l1 = context.el_l1;
877        ioHandler->setFilePointer(context.position);
878}
879
880void MatroskaImport::PrerollSubtitleTracks()
881{
882        if (!seenTracks) return;
883               
884        for (int i = 0; i < tracks.size(); i++) {
885                MatroskaTrack *track = &tracks[i];
886               
887                if (track->type == track_subtitle) {
888                        Handle subtitleDescriptionExt;
889                        OSErr err = GetImageDescriptionExtension((ImageDescriptionHandle)track->desc, &subtitleDescriptionExt, kSubFormatSSA, 1);
890                       
891                        if (err || !subtitleDescriptionExt) continue;
892                       
893                        SubPrerollFromHeader(*subtitleDescriptionExt, GetHandleSize(subtitleDescriptionExt));
894                }
895        }
896}
897
898MatroskaTrack::MatroskaTrack()
899{
900        number = 0;
901        type = -1;
902        theTrack = NULL;
903        theMedia = NULL;
904        desc = NULL;
905        sampleTable = NULL;
906        qtSampleDesc = 0;
907        timecodeScale = 1000000;
908        maxLoadedTime = 0;
909        seenFirstBlock = false;
910        firstSample = -1;
911        numSamples = 0;
912        durationToAdd = 0;
913        displayOffsetSum = 0;
914        durationSinceZeroSum = 0;
915        subtitleSerializer = new CXXSubSerializer;
916        subDataRefHandler = NULL;
917        is_vobsub = false;
918        isEnabled = true;
919        defaultDuration = 0;
920        usesLacing = true;
921        currentFrame = 0;
922}
923
924MatroskaTrack::MatroskaTrack(const MatroskaTrack &copy)
925{
926        number = copy.number;
927        type = copy.type;
928        theTrack = copy.theTrack;
929        theMedia = copy.theMedia;
930       
931        if (copy.desc) {
932                desc = (SampleDescriptionHandle) NewHandle((*copy.desc)->descSize);
933                memcpy(*desc, *copy.desc, (*copy.desc)->descSize);
934        } else
935                desc = NULL;
936       
937        sampleTable = copy.sampleTable;
938        if (sampleTable)
939                QTSampleTableRetain(sampleTable);
940       
941        qtSampleDesc = copy.qtSampleDesc;
942        timecodeScale = copy.timecodeScale;
943        maxLoadedTime = copy.maxLoadedTime;
944       
945        for (int i = 0; i < copy.lastFrames.size(); i++)
946                lastFrames.push_back(copy.lastFrames[i]);
947       
948        seenFirstBlock = copy.seenFirstBlock;
949        firstSample = copy.firstSample;
950        numSamples = copy.numSamples;
951        durationToAdd = copy.durationToAdd;
952        displayOffsetSum = copy.displayOffsetSum;
953        durationSinceZeroSum = copy.durationSinceZeroSum;
954       
955        subtitleSerializer = copy.subtitleSerializer;
956        subtitleSerializer->retain();
957               
958        subDataRefHandler = copy.subDataRefHandler;
959       
960        is_vobsub = copy.is_vobsub;
961        isEnabled = copy.isEnabled;
962        defaultDuration = copy.defaultDuration;
963        usesLacing = copy.usesLacing;
964        currentFrame = copy.currentFrame;
965}
966
967MatroskaTrack::~MatroskaTrack()
968{
969        if (desc)
970                DisposeHandle((Handle) desc);
971       
972        if (sampleTable)
973                QTSampleTableRelease(sampleTable);
974       
975        if (subtitleSerializer)
976                subtitleSerializer->release();
977}
978
979void MatroskaTrack::ParseFirstBlock(KaxInternalBlock &block)
980{
981        AudioStreamBasicDescription asbd = {0};
982        AudioChannelLayout acl = {0};
983        bool replaceSoundDesc = false;
984       
985        if (desc) {
986                switch ((*desc)->dataFormat) {
987                        case kAudioFormatAC3:
988                                replaceSoundDesc = parse_ac3_bitstream(&asbd, &acl, block.GetBuffer(0).Buffer(), block.GetFrameSize(0), true);
989                                break;
990                }
991        }
992       
993        if (replaceSoundDesc) {
994                // successful in parsing, so the acl and asbd are more correct than what we generated in
995                // AddAudioTrack() so replace our sound description
996                SoundDescriptionHandle sndDesc = NULL;
997               
998                OSStatus err = QTSoundDescriptionCreate(&asbd, &acl, sizeof(AudioChannelLayout), NULL, 0, 
999                                                        kQTSoundDescriptionKind_Movie_LowestPossibleVersion, &sndDesc);
1000                if (err == noErr) {
1001                        DisposeHandle((Handle) desc);
1002                        desc = (SampleDescriptionHandle) sndDesc;
1003                        err = QTSampleTableAddSampleDescription(sampleTable, desc, 0, &qtSampleDesc);
1004                }
1005        }
1006}
1007
1008void MatroskaTrack::AddBlock(KaxInternalBlock &block, uint32 duration, short flags)
1009{
1010        if (!seenFirstBlock) {
1011                ParseFirstBlock(block);
1012                seenFirstBlock = true;
1013        }
1014       
1015        // tracks w/ lacing can't have b-frames, and neither can any known audio codec
1016        if (usesLacing || type != track_video) {
1017                // don't add the blocks until we get one with a new timecode
1018                TimeValue64 duration = 0;
1019                if (lastFrames.size() > 0)
1020                        duration = block.GlobalTimecode() / timecodeScale - lastFrames[0].pts;
1021               
1022                if (duration > 0) {
1023                        for (int i = 0; i < lastFrames.size(); i++) {
1024                                // since there can be multiple frames in one block, split the duration evenly between them
1025                                // giving the remainder to the latter blocks
1026                                int remainder = duration % lastFrames.size() >= lastFrames.size() - i ? 1 : 0;
1027                               
1028                                lastFrames[i].duration = duration / lastFrames.size() + remainder;
1029                               
1030                                AddFrame(lastFrames[i]);
1031                        }
1032                        lastFrames.clear();
1033                }
1034        } else if (ptsReorder.size() - currentFrame > MAX_DECODE_DELAY + 1) {
1035                map<TimeValue64, TimeValue>::iterator duration;
1036                MatroskaFrame &curr = lastFrames[currentFrame];
1037                MatroskaFrame &next = lastFrames[currentFrame+1];
1038                currentFrame++;
1039               
1040                // pts -> dts works this way: we assume that the first frame has correct dts
1041                // (we start at a keyframe, so pts = dts), and then we fill up a buffer with
1042                // frames until we have the frame whose pts is equal to the next dts
1043                // Then, we sort this buffer, extract the pts as dts, and calculate the duration.
1044               
1045                ptsReorder.sort();
1046                next.dts = *(++ptsReorder.begin());
1047                ptsReorder.pop_front();
1048               
1049                // Duration calculation has to be done between consecutive pts. Since we reorder
1050                // the pts into the dts, which have to be in order, we calculate the duration then
1051                // from the dts, then save it for the frame with the same pts.
1052               
1053                durationForPTS[curr.dts] = next.dts - curr.dts;
1054               
1055                duration = durationForPTS.find(lastFrames[0].pts);
1056                if (duration != durationForPTS.end()) {
1057                        lastFrames[0].duration = duration->second;
1058                        AddFrame(lastFrames[0]);
1059                        lastFrames.erase(lastFrames.begin());
1060                        durationForPTS.erase(duration);
1061                        currentFrame--;
1062                }
1063        }
1064       
1065        for (int i = 0; i < block.NumberFrames(); i++) {
1066                MatroskaFrame newFrame;
1067                newFrame.pts = block.GlobalTimecode() / timecodeScale;
1068                newFrame.dts = newFrame.pts;
1069                if (duration > 0)
1070                        newFrame.duration = duration;
1071                else
1072                        newFrame.duration = defaultDuration;
1073                newFrame.offset = block.GetDataPosition(i);
1074                newFrame.size = block.GetFrameSize(i);
1075                newFrame.flags = flags;
1076
1077                if (type == track_subtitle) {
1078                        newFrame.buffer = &block.GetBuffer(i);
1079                        AddFrame(newFrame);
1080                }
1081                else {
1082                        lastFrames.push_back(newFrame);
1083                        if (!usesLacing && type == track_video)
1084                                ptsReorder.push_back(newFrame.pts);
1085                }
1086               
1087                newFrame.buffer = NULL;
1088        }
1089}
1090
1091void MatroskaTrack::AddFrame(MatroskaFrame &frame)
1092{
1093        ComponentResult err = noErr;
1094        TimeValue sampleTime;
1095        TimeValue64 displayOffset = frame.pts - frame.dts;
1096       
1097        if (desc == NULL) return;
1098       
1099        if (type == track_subtitle && !is_vobsub) {
1100                const char *packet=NULL; size_t size=0; unsigned start=0, end=0;
1101               
1102                if (frame.size > 0)
1103                        subtitleSerializer->pushLine((const char*)frame.buffer->Buffer(), frame.buffer->Size(), frame.pts, frame.pts + frame.duration);
1104
1105                packet = subtitleSerializer->popPacket(&size, &start, &end);
1106                if (packet) {
1107                        Handle sampleH;
1108                        PtrToHand(packet, &sampleH, size);
1109                        err = AddMediaSample(theMedia, sampleH, 0, size, end - start, desc, 1, 0, &sampleTime);
1110                        if (err) {
1111                                Codecprintf(NULL, "MKV: error adding subtitle sample %d\n", err);
1112                                return;
1113                        }
1114                        DisposeHandle(sampleH);
1115                        frame.pts = start;
1116                        frame.duration = end - start;
1117                } else return;
1118        } else if (sampleTable) {
1119                SInt64 sampleNum;
1120               
1121                err = QTSampleTableAddSampleReferences(sampleTable, frame.offset, frame.size, frame.duration, 
1122                                                                                           displayOffset, 1, frame.flags, qtSampleDesc, &sampleNum);
1123                if (err) {
1124                        Codecprintf(NULL, "MKV: error adding sample reference to table %d\n", err);
1125                        return;
1126                }
1127               
1128                if (firstSample == -1)
1129                        firstSample = sampleNum;
1130                numSamples++;
1131        } else {
1132                SampleReference64Record sample;
1133                sample.dataOffset = SInt64ToWide(frame.offset);
1134                sample.dataSize = frame.size;
1135                sample.durationPerSample = frame.duration;
1136                sample.numberOfSamples = 1;
1137                sample.sampleFlags = frame.flags;
1138               
1139                err = AddMediaSampleReferences64(theMedia, desc, 1, &sample, &sampleTime);
1140                if (err) {
1141                        Codecprintf(NULL, "MKV: error adding sample reference to media %d\n", err);
1142                        return;
1143                }
1144        }
1145       
1146        // add to track immediately if subtitle, otherwise we let it be added elsewhere when we can do several at once
1147        if (type == track_subtitle) {
1148                err = InsertMediaIntoTrack(theTrack, frame.pts, sampleTime, frame.duration, fixed1);
1149                if (err) {
1150                        Codecprintf(NULL, "MKV: error adding subtitle media into track %d\n", err);
1151                        return;
1152                }
1153        } else {
1154                durationSinceZeroSum += frame.duration;
1155                displayOffsetSum += displayOffset;
1156                if (displayOffsetSum == 0) {
1157                        durationToAdd += durationSinceZeroSum;
1158                        durationSinceZeroSum = 0;
1159                }
1160        }
1161}
1162
1163void MatroskaTrack::AddSamplesToTrack()
1164{
1165        OSStatus err = noErr;
1166       
1167        if (type == track_subtitle)
1168                return;                 // handled in AddFrame()
1169
1170        if (durationToAdd == 0 && numSamples == 0)
1171                // nothing to add
1172                return;
1173       
1174        if (sampleTable) {
1175                if (firstSample == -1)
1176                        return;         // nothing to add
1177               
1178                err = AddSampleTableToMedia(theMedia, sampleTable, firstSample, numSamples, NULL);
1179                firstSample = -1;
1180                numSamples = 0;
1181                if (err) {
1182                        Codecprintf(NULL, "MKV: error adding sample table to the media %d\n", err);
1183                        durationToAdd = 0;
1184                        return;
1185                }
1186        }
1187       
1188        err = InsertMediaIntoTrack(theTrack, -1, maxLoadedTime, durationToAdd, fixed1);
1189        if (err)
1190                Codecprintf(NULL, "MKV: error inserting media into track %d\n", err);
1191        else
1192                maxLoadedTime += durationToAdd;
1193       
1194        durationToAdd = 0;
1195}
1196
1197void MatroskaTrack::FinishTrack()
1198{
1199        CXXAutoreleasePool pool;
1200       
1201        if (type == track_subtitle && !is_vobsub)
1202        {
1203                 subtitleSerializer->setFinished();
1204                 do {
1205                         MatroskaFrame fr = {0};
1206                         AddFrame(fr); // add empty frames to flush the subtitle packet queue
1207                 } while (!subtitleSerializer->empty());
1208                 EndMediaEdits(theMedia);
1209        } else {
1210                AddSamplesToTrack();
1211        }
1212}
Note: See TracBrowser for help on using the repository browser.