root/branches/perian-1.0/FFissionCodec/FFissionDecoder.cpp

Revision 424, 13.9 kB (checked in by dconrad, 2 years ago)

MP2 decoder. Closes #120

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