source: trunk/FFissionCodec/FFissionDecoder.cpp @ 1003

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

Fix compilation errors/warnings with gcc 4.2.

Line 
1/*
2 *  FFissionDecoder.cpp
3 *
4 *  Copyright (c) 2006  David Conrad
5 *
6 *  This program is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation;
9 *  version 2.1 of the License.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 *
20 */
21
22#include "FFissionDecoder.h"
23#include "ACCodecDispatch.h"
24#include "FFusionCodec.h"
25#include "Codecprintf.h"
26#include "CodecIDs.h"
27
28typedef struct CookieAtomHeader {
29    long           size;
30    long           type;
31    unsigned char  data[1];
32} CookieAtomHeader;
33
34struct CodecPair {
35        OSType mFormatID;
36        CodecID codecID;
37};
38
39static const CodecPair kAllInputFormats[] = 
40{
41        { kAudioFormatWMA1MS, CODEC_ID_WMAV1 },
42        { kAudioFormatWMA2MS, CODEC_ID_WMAV2 },
43        { kAudioFormatFlashADPCM, CODEC_ID_ADPCM_SWF },
44        { kAudioFormatXiphVorbis, CODEC_ID_VORBIS },
45        { kAudioFormatMPEGLayer2, CODEC_ID_MP2 },
46        { kAudioFormatMPEGLayer1, CODEC_ID_MP2 },
47        { 0x6d730050, CODEC_ID_MP2 },
48        { kAudioFormatTTA, CODEC_ID_TTA },
49        { kAudioFormatDTS, CODEC_ID_DTS },
50        { kAudioFormatNellymoser, CODEC_ID_NELLYMOSER },
51        { 0, CODEC_ID_NONE }
52};
53
54static CodecID GetCodecID(OSType formatID)
55{
56        for (int i = 0; kAllInputFormats[i].codecID != CODEC_ID_NONE; i++) {
57                if (kAllInputFormats[i].mFormatID == formatID)
58                        return kAllInputFormats[i].codecID;
59        }
60        return CODEC_ID_NONE;
61}
62
63
64FFissionDecoder::FFissionDecoder(UInt32 inInputBufferByteSize) : FFissionCodec(0)
65{
66        kIntPCMOutFormatFlag = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
67        magicCookie = NULL;
68        magicCookieSize = 0;
69       
70        for (int i = 0; kAllInputFormats[i].codecID != CODEC_ID_NONE; i++) {
71                CAStreamBasicDescription theInputFormat(kAudioStreamAnyRate, kAllInputFormats[i].mFormatID, 0, 1, 0, 0, 0, 0);
72                AddInputFormat(theInputFormat);
73        }
74       
75        // libavcodec outputs 16-bit native-endian integer pcm, so why do conversions ourselves?
76        CAStreamBasicDescription theOutputFormat(kAudioStreamAnyRate, kAudioFormatLinearPCM, 0, 1, 0, 0, 16, kIntPCMOutFormatFlag);
77        AddOutputFormat(theOutputFormat);
78       
79        inputBuffer.Initialize(inInputBufferByteSize);
80        outBufUsed = 0;
81        outBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
82        outputBuffer = new Byte[outBufSize];
83}
84
85FFissionDecoder::~FFissionDecoder()
86{
87        if (magicCookie)
88                delete[] magicCookie;
89       
90        delete[] outputBuffer;
91}
92
93void FFissionDecoder::Initialize(const AudioStreamBasicDescription* inInputFormat, const AudioStreamBasicDescription* inOutputFormat, const void* inMagicCookie, UInt32 inMagicCookieByteSize)
94{
95        if (inMagicCookieByteSize > 0)
96                SetMagicCookie(inMagicCookie, inMagicCookieByteSize);
97       
98        FFissionCodec::Initialize(inInputFormat, inOutputFormat, inMagicCookie, inMagicCookieByteSize);
99}
100
101void FFissionDecoder::Uninitialize()
102{
103        outBufUsed = 0;
104        inputBuffer.Zap(inputBuffer.GetDataAvailable());
105       
106        FFissionCodec::Uninitialize();
107}
108
109void FFissionDecoder::Reset()
110{
111        outBufUsed = 0;
112        inputBuffer.Zap(inputBuffer.GetDataAvailable());
113       
114        FFissionCodec::Reset();
115}
116
117void FFissionDecoder::SetMagicCookie(const void* inMagicCookieData, UInt32 inMagicCookieDataByteSize)
118{
119        if (magicCookie)
120                delete[] magicCookie;
121       
122        magicCookieSize = inMagicCookieDataByteSize;
123       
124        if (magicCookieSize > 0) {
125                magicCookie = new Byte[magicCookieSize + FF_INPUT_BUFFER_PADDING_SIZE];
126                memcpy(magicCookie, inMagicCookieData, magicCookieSize);
127        } else
128                magicCookie = NULL;
129       
130        FFissionCodec::SetMagicCookie(inMagicCookieData, inMagicCookieDataByteSize);
131}
132
133void FFissionDecoder::SetupExtradata(OSType formatID)
134{
135        if (!magicCookie) return;
136       
137        switch (formatID) {
138                case kAudioFormatWMA1MS:
139                case kAudioFormatWMA2MS:
140                case kAudioFormatTTA:
141                        if (magicCookieSize < 12 + 18 + 8 + 8)
142                                return;
143                       
144                        avContext->extradata = magicCookie + 12 + 18 + 8;
145                        avContext->extradata_size = magicCookieSize - 12 - 18 - 8 - 8;
146                        break;
147                       
148                case kAudioFormatXiphVorbis:
149                        avContext->extradata_size = ConvertXiphVorbisCookie();
150                        avContext->extradata = magicCookie;
151                        break;
152                       
153                default:
154                        return;
155        }
156       
157        // this is safe because we always allocate this amount of additional memory for our copy of the magic cookie
158        memset(avContext->extradata + avContext->extradata_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
159}
160
161int FFissionDecoder::ConvertXiphVorbisCookie()
162{
163        Byte *ptr = magicCookie;
164        Byte *cend = magicCookie + magicCookieSize;
165        Byte *headerData[3] = {NULL};
166        int headerSize[3] = {0};
167       
168        while (ptr < cend) {
169                CookieAtomHeader *aheader = reinterpret_cast<CookieAtomHeader *>(ptr);
170                int size = EndianU32_BtoN(aheader->size);
171                ptr += size;
172                if (ptr > cend || size <= 0)
173                        break;
174               
175                switch(EndianS32_BtoN(aheader->type)) {
176                        case kCookieTypeVorbisHeader:
177                                headerData[0] = aheader->data;
178                                headerSize[0] = size - 8;
179                                break;
180                               
181                        case kCookieTypeVorbisComments:
182                                headerData[1] = aheader->data;
183                                headerSize[1] = size - 8;
184                                break;
185                               
186                        case kCookieTypeVorbisCodebooks:
187                                headerData[2] = aheader->data;
188                                headerSize[2] = size - 8;
189                                break;
190                }
191        }
192       
193    if (headerSize[0] <= 0 || headerSize[1] <= 0 || headerSize[2] <= 0) {
194                Codecprintf(NULL, "Invalid Vorbis extradata\n");
195                return 0;
196        }
197       
198        int len = headerSize[0] + headerSize[1] + headerSize[2];
199        Byte *newCookie = new Byte[len + len/255 + 64];
200        ptr = newCookie;
201       
202        ptr[0] = 2;             // number of packets minus 1
203        int offset = 1;
204        offset += av_xiphlacing(&ptr[offset], headerSize[0]);
205        offset += av_xiphlacing(&ptr[offset], headerSize[1]);
206        for (int i = 0; i < 3; i++) {
207                memcpy(&ptr[offset], headerData[i], headerSize[i]);
208                offset += headerSize[i];
209        }
210       
211        delete[] magicCookie;
212        magicCookie = newCookie;
213        magicCookieSize = offset;
214       
215        return offset;
216}
217
218void FFissionDecoder::GetProperty(AudioCodecPropertyID inPropertyID, UInt32& ioPropertyDataSize, void* outPropertyData) {
219        switch (inPropertyID) {
220                case kAudioCodecPropertyNameCFString:
221                        if (ioPropertyDataSize != sizeof(CFStringRef))
222                                CODEC_THROW(kAudioCodecBadPropertySizeError);
223                        break;
224                       
225                case kAudioCodecPropertyInputBufferSize:
226                case kAudioCodecPropertyUsedInputBufferSize:
227                        if (ioPropertyDataSize != sizeof(UInt32))
228                                CODEC_THROW(kAudioCodecBadPropertySizeError);
229                        break;
230        }
231       
232        switch (inPropertyID) {
233                case kAudioCodecPropertyNameCFString:
234                        *(CFStringRef*)outPropertyData = CFCopyLocalizedStringFromTableInBundle(CFSTR("Perian FFmpeg audio decoder"), CFSTR("CodecNames"), GetCodecBundle(), CFSTR(""));
235                        break; 
236                       
237                case kAudioCodecPropertyInputBufferSize:
238                        *reinterpret_cast<UInt32*>(outPropertyData) = inputBuffer.GetBufferByteSize();
239                        break;
240                       
241                case kAudioCodecPropertyUsedInputBufferSize:
242                        *reinterpret_cast<UInt32*>(outPropertyData) = inputBuffer.GetDataAvailable();
243                        break;
244                       
245                default:
246                        FFissionCodec::GetProperty(inPropertyID, ioPropertyDataSize, outPropertyData);
247        }
248}
249
250void FFissionDecoder::SetCurrentInputFormat(const AudioStreamBasicDescription& inInputFormat)
251{
252        if(mIsInitialized) {
253                CODEC_THROW(kAudioCodecStateError);
254        }
255       
256        if (avCodec) {
257                avcodec_close(avContext);
258                avCodec = NULL;
259        }
260       
261        CodecID codecID = GetCodecID(inInputFormat.mFormatID);
262       
263        // check to make sure the input format is legal
264        if (avcodec_find_decoder(codecID) == NULL) {
265                Codecprintf(NULL, "Unsupported input format id %4.4s\n", &inInputFormat.mFormatID);
266                CODEC_THROW(kAudioCodecUnsupportedFormatError);
267        }
268       
269        // tell our base class about the new format
270        FFissionCodec::SetCurrentInputFormat(inInputFormat);
271}
272       
273void FFissionDecoder::OpenAVCodec()
274{
275        if (!mIsInitialized)
276                CODEC_THROW(kAudioCodecStateError);
277       
278        CodecID codecID = GetCodecID(mInputFormat.mFormatID);
279       
280        avCodec = avcodec_find_decoder(codecID);
281       
282        avcodec_get_context_defaults(avContext);
283       
284        avContext->sample_rate = mInputFormat.mSampleRate;
285        avContext->channels = mInputFormat.mChannelsPerFrame;
286        avContext->block_align = mInputFormat.mBytesPerPacket;
287        avContext->frame_size = mInputFormat.mFramesPerPacket;
288        avContext->bits_per_coded_sample = mInputFormat.mBitsPerChannel;
289       
290        if (avContext->sample_rate == 0) {
291                Codecprintf(NULL, "Invalid sample rate %d\n", avContext->sample_rate);
292                avCodec = NULL;
293                return;
294        }
295       
296        if (magicCookie) {
297                SetupExtradata(mInputFormat.mFormatID);
298        }
299       
300        if (avcodec_open(avContext, avCodec)) {
301                Codecprintf(NULL, "error opening audio avcodec\n");
302                avCodec = NULL;
303                CODEC_THROW(kAudioCodecUnsupportedFormatError);
304        }
305}
306
307void FFissionDecoder::SetCurrentOutputFormat(const AudioStreamBasicDescription& inOutputFormat)
308{
309        if(mIsInitialized) {
310                CODEC_THROW(kAudioCodecStateError);
311        }
312       
313        //      check to make sure the output format is legal
314        if (inOutputFormat.mFormatID != kAudioFormatLinearPCM ||
315                inOutputFormat.mFormatFlags != kIntPCMOutFormatFlag || 
316                inOutputFormat.mBitsPerChannel != 16)
317        {
318                Codecprintf(NULL, "We only support 16 bit native endian signed integer for output\n");
319                CODEC_THROW(kAudioCodecUnsupportedFormatError);
320        }
321       
322        //      tell our base class about the new format
323        FFissionCodec::SetCurrentOutputFormat(inOutputFormat);
324}
325
326void FFissionDecoder::AppendInputData(const void* inInputData, UInt32& ioInputDataByteSize, 
327                                                                          UInt32& ioNumberPackets, const AudioStreamPacketDescription* inPacketDescription)
328{
329        const Byte *inData = (const Byte *)inInputData;
330       
331        if (inPacketDescription && ioNumberPackets) {
332                for (int i = 0; i < ioNumberPackets; i++) {
333                        UInt32 packetSize = inPacketDescription[i].mDataByteSize;
334                        inputBuffer.In(inData + inPacketDescription[i].mStartOffset, packetSize);
335                }
336        } else if (mInputFormat.mBytesPerPacket != 0) {
337                // no packet description, assume cbr
338                UInt32 amountToCopy = FFMIN(mInputFormat.mBytesPerPacket * ioNumberPackets, ioInputDataByteSize);
339                UInt32 numPackets = amountToCopy / mInputFormat.mBytesPerPacket;
340               
341                ioInputDataByteSize = amountToCopy;
342                ioNumberPackets = numPackets;
343               
344                for (int i = 0; i < numPackets; i++) {
345                        UInt32 packetSize = mInputFormat.mBytesPerPacket;
346                        inputBuffer.In(inData, packetSize);
347                        inData += mInputFormat.mBytesPerPacket;
348                }
349        } else {
350                // XiphQT throws this in this situation (we need packet descriptions, but don't get them)
351                // is there a better error to throw?
352                CODEC_THROW(kAudioCodecNotEnoughBufferSpaceError);
353        }
354}
355
356UInt32 FFissionDecoder::ProduceOutputPackets(void* outOutputData,
357                                                                                         UInt32& ioOutputDataByteSize,  // number of bytes written to outOutputData
358                                                                                         UInt32& ioNumberPackets, 
359                                                                                         AudioStreamPacketDescription* outPacketDescription)
360{
361        UInt32 ans;
362       
363        if (!avCodec)
364                OpenAVCodec();
365       
366        if (!mIsInitialized || !avCodec)
367                CODEC_THROW(kAudioCodecStateError);
368       
369        UInt32 written = 0;
370        Byte *outData = (Byte *) outOutputData;
371       
372        // we have leftovers from the last packet, use that first
373        if (outBufUsed > 0) {
374                int amountToCopy = FFMIN(outBufUsed, ioOutputDataByteSize);
375                memcpy(outData, outputBuffer, amountToCopy);
376                outBufUsed -= amountToCopy;
377                written += amountToCopy;
378               
379                if (outBufUsed > 0)
380                        memmove(outputBuffer, outputBuffer + amountToCopy, outBufUsed);
381        }
382       
383        // loop until we satisfy the request or run out of input data
384        while (written < ioOutputDataByteSize && inputBuffer.GetNumPackets() > 0) {
385                int packetSize = inputBuffer.GetCurrentPacketSize();
386                uint8_t *packet = inputBuffer.GetData();
387               
388                // decode one packet to our buffer
389                outBufUsed = outBufSize;
390                int len = avcodec_decode_audio2(avContext, (int16_t *)outputBuffer, &outBufUsed, packet, packetSize);
391               
392                if (len < 0) {
393                        Codecprintf(NULL, "Error decoding audio frame\n");
394                        inputBuffer.Zap(packetSize);
395                        outBufUsed = 0;
396                        ioOutputDataByteSize = written;
397                        ioNumberPackets = ioOutputDataByteSize / (2 * mOutputFormat.NumberChannels());
398                        return kAudioCodecProduceOutputPacketFailure;
399                }
400                inputBuffer.Zap(len);
401               
402                // copy up to the amount requested
403                int amountToCopy = FFMIN(outBufUsed, ioOutputDataByteSize - written);
404                memcpy(outData + written, outputBuffer, amountToCopy);
405                outBufUsed -= amountToCopy;
406                written += amountToCopy;
407               
408                // and save what's left over
409                if (outBufUsed > 0)
410                        memmove(outputBuffer, outputBuffer + amountToCopy, outBufUsed);
411        }
412       
413        if (written < ioOutputDataByteSize)
414                ans = kAudioCodecProduceOutputPacketNeedsMoreInputData;
415        else if (inputBuffer.GetNumPackets() > 0)
416                // we have an entire packet left to decode
417                ans = kAudioCodecProduceOutputPacketSuccessHasMore;
418        else
419                ans = kAudioCodecProduceOutputPacketSuccess;
420       
421        ioOutputDataByteSize = written;
422        ioNumberPackets = ioOutputDataByteSize / (2 * mOutputFormat.NumberChannels());
423       
424        return ans;
425}
426
427UInt32 FFissionDecoder::GetVersion() const
428{
429        return kFFusionCodecVersion;
430}
431
432// comment from XiphQT (variable frames per packet means FrameSize should be reported 0,
433// but apparently needs 1 on Intel?):
434/* The following line has been changed according to Apple engineers' suggestion
435   I received via Steve Nicolai (in response to *my* bugreport, I think...).
436   (Why don't they just implement the VBR-VFPP properly? *sigh*) */
437#ifdef TARGET_CPU_X86
438#define SHOULD_BE_ZERO 1
439#else
440#define SHOULD_BE_ZERO 0
441#endif
442
443void FFissionVBRDecoder::GetProperty(AudioCodecPropertyID inPropertyID, UInt32& ioPropertyDataSize, void* outPropertyData)
444{
445        switch (inPropertyID) {
446                case kAudioCodecPropertyPacketFrameSize:
447                case kAudioCodecPropertyHasVariablePacketByteSizes:
448                case kAudioCodecPropertyRequiresPacketDescription:
449                        if (ioPropertyDataSize != sizeof(UInt32))
450                                CODEC_THROW(kAudioCodecBadPropertySizeError);
451                        break;
452        }
453       
454        switch (inPropertyID) {
455                case kAudioCodecPropertyPacketFrameSize:
456                        *reinterpret_cast<UInt32*>(outPropertyData) = SHOULD_BE_ZERO;
457                        break;
458                       
459                case kAudioCodecPropertyHasVariablePacketByteSizes:
460                case kAudioCodecPropertyRequiresPacketDescription:
461                        *reinterpret_cast<UInt32*>(outPropertyData) = true;
462                        break;
463                       
464                default:
465                        FFissionDecoder::GetProperty(inPropertyID, ioPropertyDataSize, outPropertyData);
466        }
467}
468
469
470extern "C"
471ComponentResult FFissionDecoderEntry(ComponentParameters* inParameters, FFissionDecoder* inThis)
472{
473        return ACCodecDispatch(inParameters, inThis);
474}
475
476extern "C"
477ComponentResult FFissionVBRDecoderEntry(ComponentParameters* inParameters, FFissionVBRDecoder* inThis)
478{
479        return ACCodecDispatch(inParameters, inThis);
480}
Note: See TracBrowser for help on using the repository browser.