void MatroskaImport::AddChapterAtom(KaxChapterAtom *atom, Track chapterTrack) { KaxChapterAtom *subChapter = FindChild<KaxChapterAtom>(*atom); bool addThisChapter = true; // since QuickTime only supports linear chapter tracks (no nesting), only add chapter leaves if (subChapter && subChapter->GetSize() > 0) { while (subChapter && subChapter->GetSize() > 0) { KaxChapterFlagHidden &hideChapter = GetChild<KaxChapterFlagHidden>(*subChapter); if (!uint8_t(hideChapter)) { AddChapterAtom(subChapter, chapterTrack); addThisChapter = false; } subChapter = &GetNextChild(*atom, *subChapter); } } if (addThisChapter) { // add the chapter to the track if it has no children KaxChapterTimeStart & startTime = GetChild<KaxChapterTimeStart>(*atom); KaxChapterDisplay & chapDisplay = GetChild<KaxChapterDisplay>(*atom); KaxChapterString & chapString = GetChild<KaxChapterString>(chapDisplay); MediaHandler mh = GetMediaHandler(GetTrackMedia(chapterTrack)); TimeValue start = UInt64(startTime) / timecodeScale; if (start > movieDuration) { Codecprintf(NULL, "MKV: Chapter time is beyond the end of the file\n"); return; } Rect bounds = {0, 0, 0, 0}; TimeValue inserted; OSErr err = TextMediaAddTextSample(mh, const_cast<Ptr>(UTFstring(chapString).GetUTF8().c_str()), UTFstring(chapString).GetUTF8().size(), 0, 0, 0, NULL, NULL, teCenter, &bounds, dfClipToTextBox, 0, 0, 0, NULL, 1, &inserted); if (err) Codecprintf(NULL, "MKV: Error adding text sample %d\n", err); else { InsertMediaIntoTrack(chapterTrack, start, inserted, 1, fixed1); } } }
ComponentResult MatroskaImport::ReadChapters(KaxChapters &chapterEntries) { KaxEditionEntry & edition = GetChild<KaxEditionEntry>(chapterEntries); UInt32 emptyDataRefExtension[2]; if (seenChapters) return noErr; chapterTrack = NewMovieTrack(theMovie, 0, 0, kNoVolume); if (chapterTrack == NULL) { Codecprintf(NULL, "MKV: Error creating chapter track %d\n", GetMoviesError()); return GetMoviesError(); } // we use a handle data reference here because I don't see any way to add textual // sample references (TextMediaAddTextSample() will behave the same as AddSample() // in that it modifies the original file if that's the data reference of the media) Handle dataRef = NewHandleClear(sizeof(Handle) + 1); emptyDataRefExtension[0] = EndianU32_NtoB(sizeof(UInt32)*2); emptyDataRefExtension[1] = EndianU32_NtoB(kDataRefExtensionInitializationData); PtrAndHand(&emptyDataRefExtension[0], dataRef, sizeof(emptyDataRefExtension)); Media chapterMedia = NewTrackMedia(chapterTrack, TextMediaType, GetMovieTimeScale(theMovie), dataRef, HandleDataHandlerSubType); if (chapterMedia == NULL) { OSErr err = GetMoviesError(); Codecprintf(NULL, "MKV: Error creating chapter media %d\n", err); DisposeMovieTrack(chapterTrack); return err; } // Name the chapter track "Chapters" for easy distinguishing QTMetaDataRef trackMetaData; OSErr err = QTCopyTrackMetaData(chapterTrack, &trackMetaData); if (err == noErr) { OSType key = kUserDataName; string chapterName("Chapters"); QTMetaDataAddItem(trackMetaData, kQTMetaDataStorageFormatUserData, kQTMetaDataKeyFormatUserData, (UInt8 *)&key, sizeof(key), (UInt8 *)chapterName.c_str(), chapterName.size(), kQTMetaDataTypeUTF8, NULL); QTMetaDataRelease(trackMetaData); } BeginMediaEdits(chapterMedia); // tell the text media handler the upcoming text samples are // encoded in Unicode with a byte order mark (BOM) MediaHandler mediaHandler = GetMediaHandler(chapterMedia); SInt32 dataPtr = kTextEncodingUnicodeDefault; TextMediaSetTextSampleData(mediaHandler, &dataPtr, kTXNTextEncodingAttribute); KaxChapterAtom *chapterAtom = FindChild<KaxChapterAtom>(edition); while (chapterAtom && chapterAtom->GetSize() > 0) { AddChapterAtom(chapterAtom, chapterTrack); chapterAtom = &GetNextChild<KaxChapterAtom>(edition, *chapterAtom); } EndMediaEdits(chapterMedia); SetTrackEnabled(chapterTrack, false); seenChapters = true; return noErr; }