source: trunk/MatroskaImportPrivate.cpp @ 1399

Revision 1399, 46.7 KB checked in by astrange, 4 years ago (diff)

Fix import of single-frame Matroska files

It seems possible that multiple frames could be lost here, but I don't know
what kind of file would trigger it. Added an error log.

Fixes #558

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