root/trunk/FFissionCodec/FFissionDecoder.cpp

Revision 1003, 14.2 kB (checked in by astrange, 2 weeks ago)

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
28 typedef struct CookieAtomHeader {
29     long           size;
30     long           type;
31     unsigned char  data[1];
32 } CookieAtomHeader;
33
34 struct CodecPair {
35         OSType mFormatID;
36         CodecID codecID;
37 };
38
39 static 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
54 static 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
64 FFissionDecoder::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
85 FFissionDecoder::~FFissionDecoder()
86 {
87         if (magicCookie)
88                 delete[] magicCookie;
89        
90         delete[] outputBuffer;
91 }
92
93 void 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
101 void FFissionDecoder::Uninitialize()
102 {
103         outBufUsed = 0;
104         inputBuffer.Zap(inputBuffer.GetDataAvailable());
105        
106         FFissionCodec::Uninitialize();
107 }
108
109 void FFissionDecoder::Reset()
110 {
111         outBufUsed = 0;
112         inputBuffer.Zap(inputBuffer.GetDataAvailable());
113        
114         FFissionCodec::Reset();
115 }
116
117 void 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
133 void 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
161 int 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
218 void 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
250 void 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        
273 void 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
307 void 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
326 void 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
356 UInt32 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
427 UInt32 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
443 void 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
470 extern "C"
471 ComponentResult FFissionDecoderEntry(ComponentParameters* inParameters, FFissionDecoder* inThis)
472 {
473         return ACCodecDispatch(inParameters, inThis);
474 }
475
476 extern "C"
477 ComponentResult FFissionVBRDecoderEntry(ComponentParameters* inParameters, FFissionVBRDecoder* inThis)
478 {
479         return ACCodecDispatch(inParameters, inThis);
480 }
Note: See TracBrowser for help on using the browser.