source: trunk/MatroskaCodecIDs.cpp @ 1003

Revision 1003, 31.7 KB checked in by astrange, 5 years ago (diff)

Fix compilation errors/warnings with gcc 4.2.

Line 
1/*
2 *  MatroskaCodecIDs.h
3 *
4 *    MatroskaCodecIDs.h - Codec description extension conversion utilities between MKV and QT
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 <AudioToolbox/AudioToolbox.h>
27#include <matroska/KaxTracks.h>
28#include <matroska/KaxTrackEntryData.h>
29#include <matroska/KaxTrackAudio.h>
30#include "MatroskaCodecIDs.h"
31#include "CommonUtils.h"
32#include "Codecprintf.h"
33#include <string>
34
35using namespace std;
36using namespace libmatroska;
37
38ComponentResult DescExt_H264(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
39{
40        if (!tr_entry || !desc) return paramErr;
41        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
42       
43        if (dir == kToSampleDescription) {
44                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
45                if (codecPrivate == NULL) {
46                        // technically invalid file, assume that the h.264 is in VfW format (no pts, etc).
47                        (*desc)->dataFormat = 'H264';
48                        return noErr;
49                }
50               
51                Handle imgDescExt = NewHandle(codecPrivate->GetSize());
52                memcpy(*imgDescExt, codecPrivate->GetBuffer(), codecPrivate->GetSize());
53               
54                AddImageDescriptionExtension(imgDesc, imgDescExt, 'avcC');
55               
56                DisposeHandle((Handle) imgDescExt);
57        }
58        return noErr;
59}
60
61// xiph-qt expects these this sound extension to have been created from first 3 packets
62// which are stored in CodecPrivate in Matroska
63ComponentResult DescExt_XiphVorbis(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
64{
65        if (!tr_entry || !desc) return paramErr;
66        SoundDescriptionHandle sndDesc = (SoundDescriptionHandle) desc;
67       
68        if (dir == kToSampleDescription) {
69                Handle sndDescExt = NewHandle(0);
70                unsigned char *privateBuf;
71                int i;
72                int numPackets;
73                int *packetSizes;
74                int offset = 1;
75                UInt32 uid = 0;
76               
77                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
78                if (codecPrivate == NULL)
79                        return invalidAtomErr;
80               
81                KaxTrackUID *trackUID = FindChild<KaxTrackUID>(*tr_entry);
82                if (trackUID != NULL)
83                        uid = uint32(*trackUID);
84               
85                privateBuf = (unsigned char *) codecPrivate->GetBuffer();
86                numPackets = privateBuf[0] + 1;
87                packetSizes = (int *) NewPtrClear(sizeof(int) * numPackets);
88               
89                // get the sizes of the packets
90                packetSizes[numPackets - 1] = codecPrivate->GetSize() - 1;
91                int packetNum = 0;
92                for (i = 1; packetNum < numPackets - 1; i++) {
93                        packetSizes[packetNum] += privateBuf[i];
94                        if (privateBuf[i] < 255) {
95                                packetSizes[numPackets - 1] -= packetSizes[packetNum];
96                                packetNum++;
97                        }
98                        offset++;
99                }
100               
101                // first packet
102                unsigned long serialnoatom[3] = { EndianU32_NtoB(sizeof(serialnoatom)), 
103                        EndianU32_NtoB(kCookieTypeOggSerialNo), 
104                        EndianU32_NtoB(uid) };
105                unsigned long atomhead[2] = { EndianU32_NtoB(packetSizes[0] + sizeof(atomhead)), 
106                        EndianU32_NtoB(kCookieTypeVorbisHeader) };
107               
108                PtrAndHand(serialnoatom, sndDescExt, sizeof(serialnoatom)); //check errors?
109                PtrAndHand(atomhead, sndDescExt, sizeof(atomhead)); //check errors?
110                PtrAndHand(&privateBuf[offset], sndDescExt, packetSizes[0]);
111               
112                // second packet
113                unsigned long atomhead2[2] = { EndianU32_NtoB(packetSizes[1] + sizeof(atomhead)), 
114                        EndianU32_NtoB(kCookieTypeVorbisComments) };
115                PtrAndHand(atomhead2, sndDescExt, sizeof(atomhead2));
116                PtrAndHand(&privateBuf[offset + packetSizes[0]], sndDescExt, packetSizes[1]);
117               
118                // third packet
119                unsigned long atomhead3[2] = { EndianU32_NtoB(packetSizes[2] + sizeof(atomhead)), 
120                        EndianU32_NtoB(kCookieTypeVorbisCodebooks) };
121                PtrAndHand(atomhead3, sndDescExt, sizeof(atomhead3));
122                PtrAndHand(&privateBuf[offset + packetSizes[1] + packetSizes[0]], sndDescExt, packetSizes[2]);
123               
124                // add the extension
125                unsigned long endAtom[2] = { EndianU32_NtoB(sizeof(endAtom)), 
126                        EndianU32_NtoB(kAudioTerminatorAtomType) };
127                PtrAndHand(endAtom, sndDescExt, sizeof(endAtom));
128               
129                AddSoundDescriptionExtension(sndDesc, sndDescExt, siDecompressionParams);
130               
131                DisposePtr((Ptr)packetSizes);
132                DisposeHandle(sndDescExt);
133        }
134        return noErr;
135}
136
137// xiph-qt expects these this sound extension to have been created in this way
138// from the packets which are stored in the CodecPrivate element in Matroska
139ComponentResult DescExt_XiphFLAC(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
140{
141        if (!tr_entry || !desc) return paramErr;
142        SoundDescriptionHandle sndDesc = (SoundDescriptionHandle) desc;
143       
144        if (dir == kToSampleDescription) {
145                KaxCodecPrivate & codecPrivate = GetChild<KaxCodecPrivate>(*tr_entry);
146               
147                QTSoundDescriptionSetProperty(sndDesc, 
148                                              kQTPropertyClass_SoundDescription, 
149                                              kQTSoundDescriptionPropertyID_MagicCookie, 
150                                              codecPrivate.GetSize(), codecPrivate.GetBuffer());
151               
152                //XXX: What exactly was the point of all this?
153#if 0
154                Handle sndDescExt = NewHandle(0);
155                unsigned char *privateBuf;
156                int i;
157                int numPackets;
158                int *packetSizes;
159                int offset = 1;
160                UInt32 uid = 0;
161               
162                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
163                if (codecPrivate == NULL)
164                        return invalidAtomErr;
165               
166                KaxTrackUID *trackUID = FindChild<KaxTrackUID>(*tr_entry);
167                if (trackUID != NULL)
168                        uid = uint32(*trackUID);
169               
170                privateBuf = (unsigned char *) codecPrivate->GetBuffer();
171                numPackets = privateBuf[0] + 1;
172                packetSizes = (int *) NewPtrClear(sizeof(int) * numPackets);
173               
174                // get the sizes of the packets
175                packetSizes[numPackets - 1] = codecPrivate->GetSize() - 1;
176                int packetNum = 0;
177                for (i = 1; packetNum < numPackets - 1; i++) {
178                        packetSizes[packetNum] += privateBuf[i];
179                        if (privateBuf[i] < 255) {
180                                packetSizes[numPackets - 1] -= packetSizes[packetNum];
181                                packetNum++;
182                        }
183                        offset++;
184                }
185               
186                // first packet
187                unsigned long serialnoatom[3] = { EndianU32_NtoB(sizeof(serialnoatom)),
188                        EndianU32_NtoB(kCookieTypeOggSerialNo),
189                        EndianU32_NtoB(uid) };
190                unsigned long atomhead[2] = { EndianU32_NtoB(packetSizes[0] + sizeof(atomhead)),
191                        EndianU32_NtoB(kCookieTypeFLACStreaminfo) };
192               
193                PtrAndHand(serialnoatom, sndDescExt, sizeof(serialnoatom)); //check errors?
194                PtrAndHand(atomhead, sndDescExt, sizeof(atomhead)); //check errors?
195                PtrAndHand(&privateBuf[offset], sndDescExt, packetSizes[0]);
196               
197                // metadata packets
198                for (i = 1; i < numPackets; i++) {
199                        int j;
200                        int additionalOffset = 0;
201                        for (j = 0; j < i; j++) {
202                                additionalOffset += packetSizes[j];
203                        }
204                        unsigned long atomhead2[2] = { EndianU32_NtoB(packetSizes[1] + sizeof(atomhead)),
205                                EndianU32_NtoB(kCookieTypeFLACMetadata) };
206                        PtrAndHand(atomhead2, sndDescExt, sizeof(atomhead2));
207                        PtrAndHand(&privateBuf[offset + additionalOffset], sndDescExt, packetSizes[1]);
208                }
209                // add the extension
210                unsigned long endAtom[2] = { EndianU32_NtoB(sizeof(endAtom)),
211                        EndianU32_NtoB(kAudioTerminatorAtomType) };
212                PtrAndHand(endAtom, sndDescExt, sizeof(endAtom));
213               
214                AddSoundDescriptionExtension(sndDesc, sndDescExt, siDecompressionParams);
215               
216                DisposePtr((Ptr)packetSizes);
217                DisposeHandle(sndDescExt);
218#endif
219        }
220        return noErr;
221}
222
223ComponentResult DescExt_XiphTheora(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
224{
225        if (!tr_entry || !desc) return paramErr;
226        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
227       
228        if (dir == kToSampleDescription) {
229                Handle imgDescExt = NewHandle(0);
230                unsigned char *privateBuf;
231                int i;
232                int numPackets;
233                int *packetSizes;
234                int offset = 1;
235                UInt32 uid = 0;
236               
237                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
238                if (codecPrivate == NULL)
239                        return invalidAtomErr;
240               
241                KaxTrackUID *trackUID = FindChild<KaxTrackUID>(*tr_entry);
242                if (trackUID != NULL)
243                        uid = uint32(*trackUID);
244               
245                privateBuf = (unsigned char *) codecPrivate->GetBuffer();
246                numPackets = privateBuf[0] + 1;
247                packetSizes = (int *) NewPtrClear(sizeof(int) * numPackets);
248               
249                // get the sizes of the packets
250                packetSizes[numPackets - 1] = codecPrivate->GetSize() - 1;
251                int packetNum = 0;
252                for (i = 1; packetNum < numPackets - 1; i++) {
253                        packetSizes[packetNum] += privateBuf[i];
254                        if (privateBuf[i] < 255) {
255                                packetSizes[numPackets - 1] -= packetSizes[packetNum];
256                                packetNum++;
257                        }
258                        offset++;
259                }
260               
261                // first packet
262                unsigned long serialnoatom[3] = { EndianU32_NtoB(sizeof(serialnoatom)), 
263                        EndianU32_NtoB(kCookieTypeOggSerialNo), EndianU32_NtoB(uid) };
264                unsigned long atomhead[2] = { EndianU32_NtoB(packetSizes[0] + sizeof(atomhead)), 
265                        EndianU32_NtoB(kCookieTypeTheoraHeader) };
266               
267                PtrAndHand(serialnoatom, imgDescExt, sizeof(serialnoatom)); //check errors?
268                PtrAndHand(atomhead, imgDescExt, sizeof(atomhead)); //check errors?
269                PtrAndHand(&privateBuf[offset], imgDescExt, packetSizes[0]);
270               
271                // second packet
272                unsigned long atomhead2[2] = { EndianU32_NtoB(packetSizes[1] + sizeof(atomhead)), 
273                        EndianU32_NtoB(kCookieTypeTheoraComments) };
274                PtrAndHand(atomhead2, imgDescExt, sizeof(atomhead2));
275                PtrAndHand(&privateBuf[offset + packetSizes[0]], imgDescExt, packetSizes[1]);
276               
277                // third packet
278                unsigned long atomhead3[2] = { EndianU32_NtoB(packetSizes[2] + sizeof(atomhead)), 
279                        EndianU32_NtoB(kCookieTypeTheoraCodebooks) };
280                PtrAndHand(atomhead3, imgDescExt, sizeof(atomhead3));
281                PtrAndHand(&privateBuf[offset + packetSizes[1] + packetSizes[0]], imgDescExt, packetSizes[2]);
282               
283                // add the extension
284                unsigned long endAtom[2] = { EndianU32_NtoB(sizeof(endAtom)), EndianU32_NtoB(kAudioTerminatorAtomType) };
285                PtrAndHand(endAtom, imgDescExt, sizeof(endAtom));
286               
287                AddImageDescriptionExtension(imgDesc, imgDescExt, kTheoraDescExtension);
288               
289                DisposePtr((Ptr)packetSizes);
290                DisposeHandle(imgDescExt);
291        }
292        return noErr;
293}
294
295// VobSub stores the .idx file in the codec private, pass it as an .IDX extension
296ComponentResult DescExt_VobSub(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
297{
298        if (!tr_entry || !desc) return paramErr;
299        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
300       
301        if (dir == kToSampleDescription) {
302                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
303                if (codecPrivate == NULL)
304                        return invalidAtomErr;
305               
306                Handle imgDescExt = NewHandle(codecPrivate->GetSize());
307                memcpy(*imgDescExt, codecPrivate->GetBuffer(), codecPrivate->GetSize());
308               
309                AddImageDescriptionExtension(imgDesc, imgDescExt, kVobSubIdxExtension);
310               
311                DisposeHandle((Handle) imgDescExt);
312        }
313        return noErr;
314}
315
316ComponentResult DescExt_SSA(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
317{
318        if (!tr_entry || !desc) return paramErr;
319        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
320       
321        if (dir == kToSampleDescription) {
322                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
323                if (codecPrivate == NULL)
324                        return invalidAtomErr;
325               
326                Handle imgDescExt = NewHandle(codecPrivate->GetSize());
327                memcpy(*imgDescExt, codecPrivate->GetBuffer(), codecPrivate->GetSize());
328               
329                AddImageDescriptionExtension(imgDesc, imgDescExt, kSubFormatSSA);
330               
331                DisposeHandle((Handle) imgDescExt);
332        }
333        return noErr;
334}
335
336ComponentResult DescExt_Real(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
337{
338        if (!tr_entry || !desc) return paramErr;
339        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
340       
341        if (dir == kToSampleDescription) {
342                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
343                if (codecPrivate == NULL)
344                        return invalidAtomErr;
345               
346                Handle imgDescExt = NewHandle(codecPrivate->GetSize());
347                memcpy(*imgDescExt, codecPrivate->GetBuffer(), codecPrivate->GetSize());
348               
349                AddImageDescriptionExtension(imgDesc, imgDescExt, kRealVideoExtension);
350               
351                DisposeHandle((Handle) imgDescExt);
352        }
353        return noErr;
354}
355
356// c.f. http://wiki.multimedia.cx/index.php?title=Understanding_AAC
357
358struct MatroskaQT_AACProfileName
359{
360        const char *name;
361        char profile;
362};
363
364static const MatroskaQT_AACProfileName kMatroskaAACProfiles[] = {
365        {"A_AAC/MPEG2/MAIN", 1},
366        {"A_AAC/MPEG2/LC", 2},
367        {"A_AAC/MPEG2/LC/SBR", 5},
368        {"A_AAC/MPEG2/SSR", 3},
369        {"A_AAC/MPEG4/MAIN", 1},
370        {"A_AAC/MPEG4/LC", 2},
371        {"A_AAC/MPEG4/LC/SBR", 5},
372        {"A_AAC/MPEG4/SSR", 3},
373        {"A_AAC/MPEG4/LTP", 4}
374};
375
376static const unsigned kAACFrequencyIndexes[] = {
377        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000,
378        11025, 8000, 7350
379};
380
381static void RecreateAACVOS(KaxTrackEntry *tr_entry, uint8_t *vosBuf, size_t *vosLen)
382{
383        unsigned char profile = 2, freq_index = 15;
384        KaxCodecID *tr_codec = FindChild<KaxCodecID>(*tr_entry);
385        KaxTrackAudio & audioTrack = GetChild<KaxTrackAudio>(*tr_entry);
386        KaxAudioSamplingFreq & sampleFreq = GetChild<KaxAudioSamplingFreq>(audioTrack);
387        KaxAudioChannels & numChannels = GetChild<KaxAudioChannels>(audioTrack);
388        unsigned freq = unsigned((double)sampleFreq), channels = unsigned(numChannels);
389
390        string codecString(*tr_codec);
391
392        for (int i = 0; i < sizeof(kMatroskaAACProfiles)/sizeof(MatroskaQT_AACProfileName); i++) {
393                const MatroskaQT_AACProfileName *prof = &kMatroskaAACProfiles[i];
394                if (strcmp(codecString.c_str(), prof->name) == 0) {profile = prof->profile; break;}
395        }
396       
397        for (int i = 0; i < sizeof(kAACFrequencyIndexes)/sizeof(unsigned); i++) {
398                if (kAACFrequencyIndexes[i] == freq) {freq_index = i; break;}
399        }
400       
401        if (freq_index != 15) {
402                *vosBuf++ = (profile << 3) | (freq_index >> 1);
403                *vosBuf++ = (freq_index << 7) | (channels << 3);
404                *vosLen = 2;
405        } else {               
406                *vosBuf++ = (profile << 3) | (freq_index >> 1);
407                *vosBuf++ = (freq_index << 7) | (freq >> (24 - 7));
408                *vosBuf++ = (freq >> (24 - 7 - 8));
409                *vosBuf++ = (freq >> (24 - 7 - 16));
410                *vosBuf++ = ((freq & 1) << 7) | (channels << 3);
411                *vosLen = 5;
412        }
413}
414
415// the esds atom creation is based off of the routines for it in ffmpeg's movenc.c
416static unsigned int descrLength(unsigned int len)
417{
418    int i;
419    for(i=1; len>>(7*i); i++);
420    return len + 1 + i;
421}
422
423static uint8_t* putDescr(uint8_t *buffer, int tag, unsigned int size)
424{
425    int i= descrLength(size) - size - 2;
426    *buffer++ = tag;
427    for(; i>0; i--)
428       *buffer++ = (size>>(7*i)) | 0x80;
429    *buffer++ = size & 0x7F;
430        return buffer;
431}
432
433// ESDS layout:
434//  + version             (4 bytes)
435//  + ES descriptor
436//   + Track ID            (2 bytes)
437//   + Flags               (1 byte)
438//   + DecoderConfig descriptor
439//    + Object Type         (1 byte)
440//    + Stream Type         (1 byte)
441//    + Buffersize DB       (3 bytes)
442//    + Max bitrate         (4 bytes)
443//    + VBR/Avg bitrate     (4 bytes)
444//    + DecoderSpecific info descriptor
445//     + codecPrivate        (codecPrivate->GetSize())
446//   + SL descriptor
447//    + dunno               (1 byte)
448
449uint8_t *CreateEsdsFromSetupData(uint8_t *codecPrivate, size_t vosLen, size_t *esdsLen, int trackID, bool audio)
450{
451        int decoderSpecificInfoLen = vosLen ? descrLength(vosLen) : 0;
452       
453        *esdsLen = descrLength(3 + descrLength(13 + decoderSpecificInfoLen) + descrLength(1));
454        uint8_t *esds = (uint8_t*)malloc(*esdsLen);
455        UInt8 *pos = (UInt8 *) esds;
456               
457        // ES Descriptor
458        pos = putDescr(pos, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + descrLength(1));
459        pos = write_int16(pos, EndianS16_NtoB(trackID));
460        *pos++ = 0;             // no flags
461       
462        // DecoderConfig descriptor
463        pos = putDescr(pos, 0x04, 13 + decoderSpecificInfoLen);
464       
465        // Object type indication, see http://gpac.sourceforge.net/tutorial/mediatypes.htm
466        if (audio)
467                *pos++ = 0x40;          // aac
468        else
469                *pos++ = 0x20;          // mpeg4 part 2
470       
471        // streamtype
472        if (audio)
473                *pos++ = 0x15;
474        else
475                *pos++ = 0x11;
476       
477        // 3 bytes: buffersize DB (not sure how to get easily)
478        *pos++ = 0;
479        pos = write_int16(pos, 0);
480       
481        // max bitrate, not sure how to get easily
482        pos = write_int32(pos, 0);
483       
484        // vbr
485        pos = write_int32(pos, 0);
486       
487        if (vosLen) {
488                pos = putDescr(pos, 0x05, vosLen);
489                pos = write_data(pos, codecPrivate, vosLen);
490        }
491       
492        // SL descriptor
493        pos = putDescr(pos, 0x06, 1);
494        *pos++ = 0x02;
495       
496        return esds;
497}
498
499static Handle CreateEsdsExt(KaxTrackEntry *tr_entry, bool audio)
500{
501        KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
502        KaxTrackNumber *trackNum = FindChild<KaxTrackNumber>(*tr_entry);
503       
504        size_t vosLen = codecPrivate ? codecPrivate->GetSize() : 0;
505        int trackID = trackNum ? uint16(*trackNum) : 1;
506        uint8_t aacBuf[5] = {0};
507        uint8_t *vosBuf = codecPrivate ? codecPrivate->GetBuffer() : NULL;
508        size_t esdsLen;
509       
510        if (audio && !vosBuf) {
511                RecreateAACVOS(tr_entry, aacBuf, &vosLen);
512                vosBuf = aacBuf;
513        }
514
515        Handle esdsExt = NewHandleClear(4);
516        uint8_t *esds = CreateEsdsFromSetupData(vosBuf, vosLen, &esdsLen, trackID, audio);
517       
518        PtrAndHand(esds, esdsExt, esdsLen);
519        free((char*)esds);
520       
521        return esdsExt;
522}
523
524ComponentResult DescExt_mp4v(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
525{
526        if (!tr_entry || !desc) return paramErr;
527        ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
528       
529        if (dir == kToSampleDescription) {
530                Handle imgDescExt = CreateEsdsExt(tr_entry, false);
531               
532                if (imgDescExt == NULL) {
533                        // missing extradata -> probably missing pts too, so force VfW mode
534                        (*desc)->dataFormat = 'XVID';
535                        return noErr;
536                }
537               
538                AddImageDescriptionExtension(imgDesc, imgDescExt, 'esds');
539               
540                DisposeHandle((Handle) imgDescExt);
541        }
542        return noErr;
543}
544
545ComponentResult DescExt_aac(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
546{
547        if (!tr_entry || !desc) return paramErr;
548        SoundDescriptionHandle sndDesc = (SoundDescriptionHandle) desc;
549       
550        if (dir == kToSampleDescription) {
551                Handle esdsExt = CreateEsdsExt(tr_entry, true);
552                               
553                QTSoundDescriptionSetProperty(sndDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_MagicCookie, GetHandleSize(esdsExt) - 4, (*esdsExt) + 4);
554                DisposeHandle((Handle) esdsExt);
555        }
556
557        return noErr;
558}
559
560ComponentResult ASBDExt_LPCM(KaxTrackEntry *tr_entry, AudioStreamBasicDescription *asbd)
561{
562        if (!tr_entry || !asbd) return paramErr;
563       
564        KaxCodecID *tr_codec = FindChild<KaxCodecID>(*tr_entry);
565        if (!tr_codec) return paramErr;
566        string codecid(*tr_codec);
567       
568        // is this correct here?
569        asbd->mBytesPerPacket = asbd->mFramesPerPacket = asbd->mChannelsPerFrame * asbd->mBitsPerChannel / 8;
570       
571        if (codecid == MKV_A_PCM_BIG)
572                asbd->mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
573        // not sure about signedness; I think it should all be unsigned,
574        // but saying 16 bits unsigned doesn't work, nor does signed 8 bits
575        else if (codecid == MKV_A_PCM_LIT && asbd->mBitsPerChannel > 8)
576                asbd->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
577        else if (codecid == MKV_A_PCM_FLOAT)
578                asbd->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
579       
580        return noErr;
581}
582
583ComponentResult ASBDExt_AAC(KaxTrackEntry *tr_entry, AudioStreamBasicDescription *asbd, AudioChannelLayout *acl)
584{
585        if (!tr_entry || !asbd) return paramErr;
586        // newer Matroska files have the esds atom stored in the codec private
587        // use it for our AudioStreamBasicDescription and AudioChannelLayout if possible
588        Handle esdsExt = CreateEsdsExt(tr_entry, true);
589        OSStatus err = noErr;
590       
591        if (esdsExt) {
592                // the magic cookie for AAC is the esds atom
593                // but only the AAC-specific part; Apple seems to want the entire thing for this stuff
594                // so this block doesn't really do anything at the moment
595                // actually, it wants to skip the 4 byte version at the front
596                Ptr magicCookie = *esdsExt + 4;
597                ByteCount cookieSize = GetHandleSize(esdsExt) - 4, asbdSize = sizeof(*asbd), aclSize = sizeof(*acl);
598               
599                err = AudioFormatGetProperty(kAudioFormatProperty_ASBDFromESDS,
600                                                                         cookieSize,
601                                                                         magicCookie,
602                                                                         &asbdSize,
603                                                                         asbd);
604                if (err != noErr) 
605                        FourCCprintf("MatroskaQT: Error creating ASBD from AAC esds", err);
606               
607                err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutFromESDS,
608                                                                         cookieSize,
609                                                                         magicCookie,
610                                                                         &aclSize,
611                                                                         acl);
612                if (err != noErr) 
613                        FourCCprintf("MatroskaQT: Error creating ACL from AAC esds", err);
614               
615                DisposeHandle(esdsExt);
616        } else {
617                // if we don't have a esds atom, all we can do is get the profile of the AAC
618                // and hope there isn't a custom channel configuration
619                // (how would we get the esds in that case?)
620                // FIXME check for codec string
621                // and OutputSamplingFrequency (for SBR)
622               
623                asbd->mFormatFlags = kMPEG4Object_AAC_LC;
624                Codecprintf(NULL, "MatroskaQT: AAC track, but no esds atom\n");
625        }
626
627        return err;
628}
629
630ComponentResult MkvFinishSampleDescription(KaxTrackEntry *tr_entry, SampleDescriptionHandle desc, DescExtDirection dir)
631{
632        KaxCodecID & tr_codec = GetChild<KaxCodecID>(*tr_entry);
633       
634        string codecString(tr_codec);
635       
636        if (codecString == MKV_V_MS) {
637                // BITMAPINFOHEADER is stored in the private data, and some codecs (WMV)
638                // need it to decode
639                KaxCodecPrivate & codecPrivate = GetChild<KaxCodecPrivate>(*tr_entry);
640               
641                Handle imgDescExt = NewHandle(codecPrivate.GetSize());
642                memcpy(*imgDescExt, codecPrivate.GetBuffer(), codecPrivate.GetSize());
643               
644                AddImageDescriptionExtension((ImageDescriptionHandle) desc, imgDescExt, 'strf');
645               
646        } else if (codecString == MKV_A_MS) {
647                // WAVFORMATEX is stored in the private data, and some codecs (WMA) need it to decode
648                KaxCodecPrivate & codecPrivate = GetChild<KaxCodecPrivate>(*tr_entry);
649               
650                QTSoundDescriptionSetProperty((SoundDescriptionHandle) desc, 
651                                              kQTPropertyClass_SoundDescription, 
652                                              kQTSoundDescriptionPropertyID_MagicCookie, 
653                                              codecPrivate.GetSize(), codecPrivate.GetBuffer());
654               
655        } else if (codecString == MKV_V_QT) {
656                // This seems to work fine, but there's something it's missing to get the
657                // image description to match perfectly (last 2 bytes are different)
658                // Figure it out later...
659                KaxCodecPrivate & codecPrivate = GetChild<KaxCodecPrivate>(*tr_entry);
660                if (codecPrivate.GetSize() < sizeof(ImageDescription)) {
661                        Codecprintf(NULL, "MatroskaQT: QuickTime track %hu doesn't have needed stsd data\n", 
662                                                uint16(tr_entry->TrackNumber()));
663                        return -1;
664                }
665               
666                ImageDescriptionHandle imgDesc = (ImageDescriptionHandle) desc;
667                SetHandleSize((Handle) imgDesc, codecPrivate.GetSize());
668                memcpy(&(*imgDesc)->cType, codecPrivate.GetBuffer(), codecPrivate.GetSize());
669                // it's stored in big endian, so flip endian to native
670                // I think we have to do this, need to check on Intel without them
671                (*imgDesc)->idSize = codecPrivate.GetSize();
672                (*imgDesc)->cType = EndianU32_BtoN((*imgDesc)->cType);
673                (*imgDesc)->resvd1 = EndianS32_BtoN((*imgDesc)->resvd1);
674                (*imgDesc)->resvd2 = EndianS16_BtoN((*imgDesc)->resvd2);
675                (*imgDesc)->dataRefIndex = EndianS16_BtoN((*imgDesc)->dataRefIndex);
676                (*imgDesc)->version = EndianS16_BtoN((*imgDesc)->version);
677                (*imgDesc)->revisionLevel = EndianS16_BtoN((*imgDesc)->revisionLevel);
678                (*imgDesc)->vendor = EndianS32_BtoN((*imgDesc)->vendor);
679                (*imgDesc)->temporalQuality = EndianU32_BtoN((*imgDesc)->temporalQuality);
680                (*imgDesc)->spatialQuality = EndianU32_BtoN((*imgDesc)->spatialQuality);
681                (*imgDesc)->width = EndianS16_BtoN((*imgDesc)->width);
682                (*imgDesc)->height = EndianS16_BtoN((*imgDesc)->height);
683                (*imgDesc)->vRes = EndianS32_BtoN((*imgDesc)->vRes);
684                (*imgDesc)->hRes = EndianS32_BtoN((*imgDesc)->hRes);
685                (*imgDesc)->dataSize = EndianS32_BtoN((*imgDesc)->dataSize);
686                (*imgDesc)->frameCount = EndianS16_BtoN((*imgDesc)->frameCount);
687                (*imgDesc)->depth = EndianS16_BtoN((*imgDesc)->depth);
688                (*imgDesc)->clutID = EndianS16_BtoN((*imgDesc)->clutID);
689
690        } else {
691                switch ((*desc)->dataFormat) {
692                        case kH264CodecType:
693                                return DescExt_H264(tr_entry, desc, dir);
694                               
695                        case kAudioFormatXiphVorbis:
696                                return DescExt_XiphVorbis(tr_entry, desc, dir);
697                               
698//                      case kAudioFormatXiphFLAC:
699//                              return DescExt_XiphFLAC(tr_entry, desc, dir);
700                               
701                        case kVideoFormatXiphTheora:
702                                return DescExt_XiphTheora(tr_entry, desc, dir);
703                               
704                        case kSubFormatVobSub:
705                                return DescExt_VobSub(tr_entry, desc, dir);
706                               
707                        case kVideoFormatReal5:
708                        case kVideoFormatRealG2:
709                        case kVideoFormatReal8:
710                        case kVideoFormatReal9:
711                                return DescExt_Real(tr_entry, desc, dir);
712                               
713                        case kMPEG4VisualCodecType:
714                                return DescExt_mp4v(tr_entry, desc, dir);
715                               
716                        case kAudioFormatMPEG4AAC:
717                        case kMPEG4AudioFormat:
718                                return DescExt_aac(tr_entry, desc, dir);
719                               
720                        case kSubFormatSSA:
721                                return DescExt_SSA(tr_entry, desc, dir);
722                }
723        }
724        return noErr;
725}
726
727// some default channel layouts for 3 to 8 channels
728// vorbis, flac and aac should be correct unless extradata specifices something else for aac
729static const AudioChannelLayout vorbisChannelLayouts[6] = {
730        { kAudioChannelLayoutTag_UseChannelBitmap, kAudioChannelBit_Left | kAudioChannelBit_Right | kAudioChannelBit_CenterSurround },
731        { kAudioChannelLayoutTag_Quadraphonic },                // L R Ls Rs
732        { kAudioChannelLayoutTag_MPEG_5_0_C },                  // L C R Ls Rs
733        { kAudioChannelLayoutTag_MPEG_5_1_C },                  // L C R Ls Rs LFE
734};
735
736// these should be the default for the number of channels; esds can specify other mappings
737static const AudioChannelLayout aacChannelLayouts[6] = {
738        { kAudioChannelLayoutTag_MPEG_3_0_B },                  // C L R according to wiki.multimedia.cx
739        { kAudioChannelLayoutTag_AAC_4_0 },                             // C L R Cs
740        { kAudioChannelLayoutTag_AAC_5_0 },                             // C L R Ls Rs
741        { kAudioChannelLayoutTag_AAC_5_1 },                             // C L R Ls Rs Lfe
742        { kAudioChannelLayoutTag_AAC_6_1 },                             // C L R Ls Rs Cs Lfe
743        { kAudioChannelLayoutTag_AAC_7_1 },                             // C Lc Rc L R Ls Rs Lfe
744};
745
746static const AudioChannelLayout ac3ChannelLayouts[6] = {
747        { kAudioChannelLayoutTag_ITU_3_0 },                             // L R C
748        { kAudioChannelLayoutTag_ITU_3_1 },                             // L R C Cs
749        { kAudioChannelLayoutTag_ITU_3_2 },                             // L R C Ls Rs
750        { kAudioChannelLayoutTag_ITU_3_2_1 },                   // L R C LFE Ls Rs
751};
752
753static const AudioChannelLayout flacChannelLayouts[6] = {
754        { kAudioChannelLayoutTag_ITU_3_0 },                             // L R C
755        { kAudioChannelLayoutTag_Quadraphonic },                // L R Ls Rs
756        { kAudioChannelLayoutTag_ITU_3_2 },                             // L R C Ls Rs
757        { kAudioChannelLayoutTag_ITU_3_2_1 },                   // L R C LFE Ls Rs
758        { kAudioChannelLayoutTag_MPEG_6_1_A },                  // L R C LFE Ls Rs Cs
759        { kAudioChannelLayoutTag_MPEG_7_1_C },                  // L R C LFE Ls Rs Rls Rrs
760};
761
762// 5.1 is tested, the others are just guesses
763static const AudioChannelLayout dtsChannelLayouts[6] = {
764        { kAudioChannelLayoutTag_MPEG_3_0_B },                  // C L R
765        { kAudioChannelLayoutTag_MPEG_4_0_B },                  // C L R Cs
766        { kAudioChannelLayoutTag_MPEG_5_0_D },                  // C L R Ls Rs
767        { kAudioChannelLayoutTag_MPEG_5_1_D },                  // C L R Ls Rs Lfe
768};
769
770AudioChannelLayout GetDefaultChannelLayout(AudioStreamBasicDescription *asbd)
771{
772        AudioChannelLayout acl = {0};
773        int channelIndex = asbd->mChannelsPerFrame - 3;
774       
775        if (channelIndex >= 0 && channelIndex < 6) {
776                switch (asbd->mFormatID) {
777                        case kAudioFormatXiphVorbis:
778                                acl = vorbisChannelLayouts[channelIndex];
779                                break;
780                               
781                        case kAudioFormatXiphFLAC:
782                                acl = flacChannelLayouts[channelIndex];
783                                break;
784                               
785                        case kAudioFormatMPEG4AAC:
786                                // TODO: use extradata to make ACL
787                                acl = aacChannelLayouts[channelIndex];
788                                break;
789                               
790                        case kAudioFormatAC3:
791                        case kAudioFormatAC3MS:
792                                acl = ac3ChannelLayouts[channelIndex];
793                                break;
794                               
795                        case kAudioFormatDTS:
796                                acl = dtsChannelLayouts[channelIndex];
797                                break;
798                }
799        }
800       
801        return acl;
802}
803
804
805ComponentResult MkvFinishAudioDescriptions(KaxTrackEntry *tr_entry, AudioStreamBasicDescription *asbd, AudioChannelLayout *acl)
806{
807        switch (asbd->mFormatID) {
808                case kAudioFormatMPEG4AAC:
809                        return ASBDExt_AAC(tr_entry, asbd, acl);
810                       
811                case kAudioFormatLinearPCM:
812                        return ASBDExt_LPCM(tr_entry, asbd);
813        }
814        return noErr;
815}
816
817typedef struct {
818        OSType cType;
819        int twocc;
820} WavCodec;
821
822static const WavCodec kWavCodecIDs[] = {
823        { kAudioFormatMPEGLayer2, 0x50 },
824        { kAudioFormatMPEGLayer3, 0x55 },
825        { kAudioFormatAC3, 0x2000 },
826        { kAudioFormatDTS, 0x2001 },
827        { kAudioFormatMPEG4AAC, 0xff },
828        { kAudioFormatXiphFLAC, 0xf1ac },
829        { 0, 0 }
830};
831
832typedef struct {
833        OSType cType;
834        const char *mkvID;
835} MatroskaQT_Codec;
836
837// the first matching pair is used for conversion
838static const MatroskaQT_Codec kMatroskaCodecIDs[] = {
839        { kRawCodecType, "V_UNCOMPRESSED" },
840        { kMPEG4VisualCodecType, "V_MPEG4/ISO/ASP" },
841        { kMPEG4VisualCodecType, "V_MPEG4/ISO/SP" },
842        { kMPEG4VisualCodecType, "V_MPEG4/ISO/AP" },
843        { kH264CodecType, "V_MPEG4/ISO/AVC" },
844        { kVideoFormatMSMPEG4v3, "V_MPEG4/MS/V3" },
845        { kMPEG1VisualCodecType, "V_MPEG1" },
846        { kMPEG2VisualCodecType, "V_MPEG2" },
847        { kVideoFormatReal5, "V_REAL/RV10" },
848        { kVideoFormatRealG2, "V_REAL/RV20" },
849        { kVideoFormatReal8, "V_REAL/RV30" },
850        { kVideoFormatReal9, "V_REAL/RV40" },
851        { kVideoFormatXiphTheora, "V_THEORA" },
852        { kVideoFormatSnow, "V_SNOW" },
853       
854        { kAudioFormatMPEG4AAC, "A_AAC" },
855        { kAudioFormatMPEG4AAC, "A_AAC/MPEG4/LC" },
856        { kAudioFormatMPEG4AAC, "A_AAC/MPEG4/MAIN" },
857        { kAudioFormatMPEG4AAC, "A_AAC/MPEG4/LC/SBR" },
858        { kAudioFormatMPEG4AAC, "A_AAC/MPEG4/SSR" },
859        { kAudioFormatMPEG4AAC, "A_AAC/MPEG4/LTP" },
860        { kAudioFormatMPEG4AAC, "A_AAC/MPEG2/LC" },
861        { kAudioFormatMPEG4AAC, "A_AAC/MPEG2/MAIN" },
862        { kAudioFormatMPEG4AAC, "A_AAC/MPEG2/LC/SBR" },
863        { kAudioFormatMPEG4AAC, "A_AAC/MPEG2/SSR" },
864        { kAudioFormatMPEGLayer1, "A_MPEG/L1" },
865        { kAudioFormatMPEGLayer2, "A_MPEG/L2" },
866        { kAudioFormatMPEGLayer3, "A_MPEG/L3" },
867        { kAudioFormatAC3, "A_AC3" },
868        { kAudioFormatAC3MS, "A_AC3" },
869        // anything special for these two?
870        { kAudioFormatAC3, "A_AC3/BSID9" },
871        { kAudioFormatAC3, "A_AC3/BSID10" },
872        { kAudioFormatXiphVorbis, "A_VORBIS" },
873        { kAudioFormatXiphFLAC, "A_FLAC" },
874        { kAudioFormatLinearPCM, "A_PCM/INT/LIT" },
875        { kAudioFormatLinearPCM, "A_PCM/INT/BIG" },
876        { kAudioFormatLinearPCM, "A_PCM/FLOAT/IEEE" },
877        { kAudioFormatDTS, "A_DTS" },
878        { kAudioFormatTTA, "A_TTA1" },
879        { kAudioFormatWavepack, "A_WAVPACK4" },
880        { kAudioFormatReal1, "A_REAL/14_4" },
881        { kAudioFormatReal2, "A_REAL/28_8" },
882        { kAudioFormatRealCook, "A_REAL/COOK" },
883        { kAudioFormatRealSipro, "A_REAL/SIPR" },
884        { kAudioFormatRealLossless, "A_REAL/RALF" },
885        { kAudioFormatRealAtrac3, "A_REAL/ATRC" },
886       
887#if 0
888        { kBMPCodecType, "S_IMAGE/BMP" },
889
890        { kSubFormatUSF, "S_TEXT/USF" },
891#endif
892        { kSubFormatSSA, "S_TEXT/SSA" },
893    { kSubFormatSSA, "S_SSA" },
894        { kSubFormatASS, "S_TEXT/ASS" },
895        { kSubFormatASS, "S_ASS" },
896        { kSubFormatUTF8, "S_TEXT/UTF8" },
897        { kSubFormatUTF8, "S_TEXT/ASCII" },
898        { kSubFormatVobSub, "S_VOBSUB" },
899};
900
901
902FourCharCode MkvGetFourCC(KaxTrackEntry *tr_entry)
903{
904        KaxCodecID *tr_codec = FindChild<KaxCodecID>(*tr_entry);
905        if (tr_codec == NULL)
906                return 0;
907       
908        string codecString(*tr_codec);
909       
910        if (codecString == MKV_V_MS) {
911                // avi compatibility mode, 4cc is in private info
912                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
913                if (codecPrivate == NULL)
914                        return 0;
915               
916                // offset to biCompression in BITMAPINFO
917                unsigned char *p = (unsigned char *) codecPrivate->GetBuffer() + 16;
918                return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
919               
920        } else if (codecString == MKV_A_MS) {
921                // acm compatibility mode, twocc is in private info
922                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
923                if (codecPrivate == NULL)
924                        return 0;
925               
926                unsigned char *p = (unsigned char *) codecPrivate->GetBuffer();
927                int twocc = p[0] | (p[1] << 8);
928               
929                for (int i = 0; kWavCodecIDs[i].cType; i++) {
930                        if (kWavCodecIDs[i].twocc == twocc)
931                                return kWavCodecIDs[i].cType;
932                }
933                return 'ms\0\0' | twocc;
934               
935        } else if (codecString == MKV_V_QT) {
936                // QT compatibility mode, private info is the ImageDescription structure, big endian
937                KaxCodecPrivate *codecPrivate = FindChild<KaxCodecPrivate>(*tr_entry);
938                if (codecPrivate == NULL)
939                        return 0;
940               
941                // starts at the 4CC
942                unsigned char *p = (unsigned char *) codecPrivate->GetBuffer();
943                return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
944               
945        } else {
946                for (int i = 0; i < sizeof(kMatroskaCodecIDs) / sizeof(MatroskaQT_Codec); i++) {
947                        if (codecString == kMatroskaCodecIDs[i].mkvID)
948                                return kMatroskaCodecIDs[i].cType;
949                }
950        }
951        return 0;
952}
Note: See TracBrowser for help on using the repository browser.