Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "AppleMP3Reader.h"
7 #include "nsISeekableStream.h"
8 #include "MediaDecoder.h"
10 // Number of bytes we will read and pass to the audio parser in each
11 // |DecodeAudioData| call.
12 #define AUDIO_READ_BYTES 4096
14 // Maximum number of audio frames we will accept from the audio decoder in one
15 // go. Carefully select this to work well with both the mp3 1152 max frames
16 // per block and power-of-2 allocation sizes. Since we must pre-allocate the
17 // buffer we cannot use AudioCompactor without paying for an additional
18 // allocation and copy. Therefore, choosing a value that divides exactly into
19 // 1152 is most memory efficient.
20 #define MAX_AUDIO_FRAMES 128
22 namespace mozilla {
24 #ifdef PR_LOGGING
25 extern PRLogModuleInfo* gMediaDecoderLog;
26 #define LOGE(...) PR_LOG(gMediaDecoderLog, PR_LOG_ERROR, (__VA_ARGS__))
27 #define LOGW(...) PR_LOG(gMediaDecoderLog, PR_LOG_WARNING, (__VA_ARGS__))
28 #define LOGD(...) PR_LOG(gMediaDecoderLog, PR_LOG_DEBUG, (__VA_ARGS__))
29 #else
30 #define LOGE(...)
31 #define LOGW(...)
32 #define LOGD(...)
33 #endif
35 #define PROPERTY_ID_FORMAT "%c%c%c%c"
36 #define PROPERTY_ID_PRINT(x) ((x) >> 24), \
37 ((x) >> 16) & 0xff, \
38 ((x) >> 8) & 0xff, \
39 (x) & 0xff
41 AppleMP3Reader::AppleMP3Reader(AbstractMediaDecoder *aDecoder)
42 : MediaDecoderReader(aDecoder)
43 , mStreamReady(false)
44 , mAudioFramesPerCompressedPacket(0)
45 , mCurrentAudioFrame(0)
46 , mAudioChannels(0)
47 , mAudioSampleRate(0)
48 , mAudioFileStream(nullptr)
49 , mAudioConverter(nullptr)
50 , mMP3FrameParser(mDecoder->GetResource()->GetLength())
51 {
52 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
53 }
55 AppleMP3Reader::~AppleMP3Reader()
56 {
57 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread");
58 }
61 /*
62 * The Apple audio decoding APIs are very callback-happy. When the parser has
63 * some metadata, it will call back to here.
64 */
65 static void _AudioMetadataCallback(void *aThis,
66 AudioFileStreamID aFileStream,
67 AudioFileStreamPropertyID aPropertyID,
68 UInt32 *aFlags)
69 {
70 ((AppleMP3Reader*)aThis)->AudioMetadataCallback(aFileStream, aPropertyID,
71 aFlags);
72 }
74 /*
75 * Similar to above, this is called when the parser has enough data to parse
76 * one or more samples.
77 */
78 static void _AudioSampleCallback(void *aThis,
79 UInt32 aNumBytes, UInt32 aNumPackets,
80 const void *aData,
81 AudioStreamPacketDescription *aPackets)
82 {
83 ((AppleMP3Reader*)aThis)->AudioSampleCallback(aNumBytes, aNumPackets,
84 aData, aPackets);
85 }
88 /*
89 * If we're not at end of stream, read |aNumBytes| from the media resource,
90 * put it in |aData|, and return true.
91 * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
92 * amount of data we have left, and return false.
93 */
94 nsresult
95 AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
96 {
97 MediaResource *resource = mDecoder->GetResource();
99 // Loop until we have all the data asked for, or we've reached EOS
100 uint32_t totalBytes = 0;
101 uint32_t numBytes;
102 do {
103 uint32_t bytesWanted = *aNumBytes - totalBytes;
104 nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
105 totalBytes += numBytes;
107 if (NS_FAILED(rv)) {
108 *aNumBytes = 0;
109 return NS_ERROR_FAILURE;
110 }
111 } while(totalBytes < *aNumBytes && numBytes);
113 *aNumBytes = totalBytes;
115 // We will have read some data in the last iteration iff we filled the buffer.
116 // XXX Maybe return a better value than NS_ERROR_FAILURE?
117 return numBytes ? NS_OK : NS_ERROR_FAILURE;
118 }
120 nsresult
121 AppleMP3Reader::Init(MediaDecoderReader* aCloneDonor)
122 {
123 AudioFileTypeID fileType = kAudioFileMP3Type;
125 OSStatus rv = AudioFileStreamOpen(this,
126 _AudioMetadataCallback,
127 _AudioSampleCallback,
128 fileType,
129 &mAudioFileStream);
131 if (rv) {
132 return NS_ERROR_FAILURE;
133 }
135 return NS_OK;
136 }
139 struct PassthroughUserData {
140 AppleMP3Reader *mReader;
141 UInt32 mNumPackets;
142 UInt32 mDataSize;
143 const void *mData;
144 AudioStreamPacketDescription *mPacketDesc;
145 bool mDone;
146 };
148 // Error value we pass through the decoder to signal that nothing has gone wrong
149 // during decoding, but more data is needed.
150 const UInt32 kNeedMoreData = 'MOAR';
152 /*
153 * This function is called from |AudioConverterFillComplexBuffer|, which is
154 * called from |AudioSampleCallback| below, which in turn is called by
155 * |AudioFileStreamParseBytes|, which is called by |DecodeAudioData|.
156 *
157 * Mercifully, this is all synchronous.
158 *
159 * This callback is run when the AudioConverter (decoder) wants more MP3 packets
160 * to decode.
161 */
162 /* static */ OSStatus
163 AppleMP3Reader::PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
164 UInt32 *aNumDataPackets /* in/out */,
165 AudioBufferList *aData /* in/out */,
166 AudioStreamPacketDescription **aPacketDesc,
167 void *aUserData)
168 {
169 PassthroughUserData *userData = (PassthroughUserData *)aUserData;
170 if (userData->mDone) {
171 // We make sure this callback is run _once_, with all the data we received
172 // from |AudioFileStreamParseBytes|. When we return an error, the decoder
173 // simply passes the return value on to the calling method,
174 // |AudioSampleCallback|; and flushes all of the audio frames it had
175 // buffered. It does not change the decoder's state.
176 LOGD("requested too much data; returning\n");
177 *aNumDataPackets = 0;
178 return kNeedMoreData;
179 }
181 userData->mDone = true;
183 LOGD("AudioConverter wants %u packets of audio data\n", *aNumDataPackets);
185 *aNumDataPackets = userData->mNumPackets;
186 *aPacketDesc = userData->mPacketDesc;
188 aData->mBuffers[0].mNumberChannels = userData->mReader->mAudioChannels;
189 aData->mBuffers[0].mDataByteSize = userData->mDataSize;
190 aData->mBuffers[0].mData = const_cast<void *>(userData->mData);
192 return 0;
193 }
195 /*
196 * This callback is called when |AudioFileStreamParseBytes| has enough data to
197 * extract one or more MP3 packets.
198 */
199 void
200 AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
201 UInt32 aNumPackets,
202 const void *aData,
203 AudioStreamPacketDescription *aPackets)
204 {
205 LOGD("got %u bytes, %u packets\n", aNumBytes, aNumPackets);
207 // 1 frame per packet * num channels * 32-bit float
208 uint32_t decodedSize = MAX_AUDIO_FRAMES * mAudioChannels *
209 sizeof(AudioDataValue);
211 // descriptions for _decompressed_ audio packets. ignored.
212 nsAutoArrayPtr<AudioStreamPacketDescription>
213 packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
215 // This API insists on having MP3 packets spoon-fed to it from a callback.
216 // This structure exists only to pass our state and the result of the parser
217 // on to the callback above.
218 PassthroughUserData userData = { this, aNumPackets, aNumBytes, aData, aPackets, false };
220 do {
221 // Decompressed audio buffer
222 nsAutoArrayPtr<uint8_t> decoded(new uint8_t[decodedSize]);
224 AudioBufferList decBuffer;
225 decBuffer.mNumberBuffers = 1;
226 decBuffer.mBuffers[0].mNumberChannels = mAudioChannels;
227 decBuffer.mBuffers[0].mDataByteSize = decodedSize;
228 decBuffer.mBuffers[0].mData = decoded.get();
230 // in: the max number of packets we can handle from the decoder.
231 // out: the number of packets the decoder is actually returning.
232 UInt32 numFrames = MAX_AUDIO_FRAMES;
234 OSStatus rv = AudioConverterFillComplexBuffer(mAudioConverter,
235 PassthroughInputDataCallback,
236 &userData,
237 &numFrames /* in/out */,
238 &decBuffer,
239 packets.get());
241 if (rv && rv != kNeedMoreData) {
242 LOGE("Error decoding audio stream: %x\n", rv);
243 break;
244 }
246 // If we decoded zero frames then AudiOConverterFillComplexBuffer is out
247 // of data to provide. We drained its internal buffer completely on the
248 // last pass.
249 if (numFrames == 0 && rv == kNeedMoreData) {
250 LOGD("FillComplexBuffer out of data exactly\n");
251 break;
252 }
254 int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
255 int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
257 LOGD("pushed audio at time %lfs; duration %lfs\n",
258 (double)time / USECS_PER_S, (double)duration / USECS_PER_S);
260 AudioData *audio = new AudioData(mDecoder->GetResource()->Tell(),
261 time, duration, numFrames,
262 reinterpret_cast<AudioDataValue *>(decoded.forget()),
263 mAudioChannels);
264 mAudioQueue.Push(audio);
266 mCurrentAudioFrame += numFrames;
268 if (rv == kNeedMoreData) {
269 // No error; we just need more data.
270 LOGD("FillComplexBuffer out of data\n");
271 break;
272 }
273 } while (true);
274 }
276 bool
277 AppleMP3Reader::DecodeAudioData()
278 {
279 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
281 // Read AUDIO_READ_BYTES if we can
282 char bytes[AUDIO_READ_BYTES];
283 uint32_t numBytes = AUDIO_READ_BYTES;
285 nsresult readrv = Read(&numBytes, bytes);
287 // This function calls |AudioSampleCallback| above, synchronously, when it
288 // finds compressed MP3 frame.
289 OSStatus rv = AudioFileStreamParseBytes(mAudioFileStream,
290 numBytes,
291 bytes,
292 0 /* flags */);
294 if (NS_FAILED(readrv)) {
295 mAudioQueue.Finish();
296 return false;
297 }
299 // DataUnavailable just means there wasn't enough data to demux anything.
300 // We should have more to push into the demuxer next time we're called.
301 if (rv && rv != kAudioFileStreamError_DataUnavailable) {
302 LOGE("AudioFileStreamParseBytes returned unknown error %x", rv);
303 return false;
304 }
306 return true;
307 }
309 bool
310 AppleMP3Reader::DecodeVideoFrame(bool &aKeyframeSkip,
311 int64_t aTimeThreshold)
312 {
313 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
314 return false;
315 }
318 bool
319 AppleMP3Reader::HasAudio()
320 {
321 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
322 return mStreamReady;
323 }
325 bool
326 AppleMP3Reader::HasVideo()
327 {
328 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
329 return false;
330 }
333 /*
334 * Query the MP3 parser for a piece of metadata.
335 */
336 static nsresult
337 GetProperty(AudioFileStreamID aAudioFileStream,
338 AudioFileStreamPropertyID aPropertyID, void *aData)
339 {
340 UInt32 size;
341 Boolean writeable;
342 OSStatus rv = AudioFileStreamGetPropertyInfo(aAudioFileStream, aPropertyID,
343 &size, &writeable);
345 if (rv) {
346 LOGW("Couldn't get property " PROPERTY_ID_FORMAT "\n",
347 PROPERTY_ID_PRINT(aPropertyID));
348 return NS_ERROR_FAILURE;
349 }
351 rv = AudioFileStreamGetProperty(aAudioFileStream, aPropertyID,
352 &size, aData);
354 return NS_OK;
355 }
358 nsresult
359 AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
360 MetadataTags** aTags)
361 {
362 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
364 *aTags = nullptr;
366 /*
367 * Feed bytes into the parser until we have all the metadata we need to
368 * set up the decoder. When the parser has enough data, it will
369 * synchronously call back to |AudioMetadataCallback| below.
370 */
371 OSStatus rv;
372 nsresult readrv;
373 uint32_t offset = 0;
374 do {
375 char bytes[AUDIO_READ_BYTES];
376 uint32_t numBytes = AUDIO_READ_BYTES;
377 readrv = Read(&numBytes, bytes);
379 rv = AudioFileStreamParseBytes(mAudioFileStream,
380 numBytes,
381 bytes,
382 0 /* flags */);
384 mMP3FrameParser.Parse(bytes, numBytes, offset);
386 offset += numBytes;
388 // We have to do our decoder setup from the callback. When it's done it will
389 // set mStreamReady.
390 } while (!mStreamReady && !rv && NS_SUCCEEDED(readrv));
392 if (rv) {
393 LOGE("Error decoding audio stream metadata\n");
394 return NS_ERROR_FAILURE;
395 }
397 if (!mAudioConverter) {
398 LOGE("Failed to setup the AudioToolbox audio decoder\n");
399 return NS_ERROR_FAILURE;
400 }
402 if (!mMP3FrameParser.IsMP3()) {
403 LOGE("Frame parser failed to parse MP3 stream\n");
404 return NS_ERROR_FAILURE;
405 }
407 aInfo->mAudio.mRate = mAudioSampleRate;
408 aInfo->mAudio.mChannels = mAudioChannels;
409 aInfo->mAudio.mHasAudio = mStreamReady;
411 {
412 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
413 mDuration = mMP3FrameParser.GetDuration();
414 mDecoder->SetMediaDuration(mDuration);
415 }
417 return NS_OK;
418 }
421 void
422 AppleMP3Reader::AudioMetadataCallback(AudioFileStreamID aFileStream,
423 AudioFileStreamPropertyID aPropertyID,
424 UInt32 *aFlags)
425 {
426 if (aPropertyID == kAudioFileStreamProperty_ReadyToProducePackets) {
427 /*
428 * The parser is ready to send us packets of MP3 audio.
429 *
430 * We need to set the decoder up here, because if
431 * |AudioFileStreamParseBytes| has enough audio data, then it will call
432 * |AudioSampleCallback| before we get back to |ReadMetadata|.
433 */
434 SetupDecoder();
435 mStreamReady = true;
436 }
437 }
440 void
441 AppleMP3Reader::SetupDecoder()
442 {
443 // Get input format description from demuxer
444 AudioStreamBasicDescription inputFormat, outputFormat;
445 GetProperty(mAudioFileStream, kAudioFileStreamProperty_DataFormat, &inputFormat);
447 memset(&outputFormat, 0, sizeof(outputFormat));
449 // Set output format
450 #if defined(MOZ_SAMPLE_TYPE_FLOAT32)
451 outputFormat.mBitsPerChannel = 32;
452 outputFormat.mFormatFlags =
453 kLinearPCMFormatFlagIsFloat |
454 0;
455 #else
456 #error Unknown audio sample type
457 #endif
459 mAudioSampleRate = outputFormat.mSampleRate = inputFormat.mSampleRate;
460 mAudioChannels
461 = outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
462 mAudioFramesPerCompressedPacket = inputFormat.mFramesPerPacket;
464 outputFormat.mFormatID = kAudioFormatLinearPCM;
466 // Set up the decoder so it gives us one sample per frame; this way, it will
467 // pass us all the samples it has in one go. Also makes it much easier to
468 // deinterlace.
469 outputFormat.mFramesPerPacket = 1;
470 outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame
471 = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
473 OSStatus rv = AudioConverterNew(&inputFormat,
474 &outputFormat,
475 &mAudioConverter);
477 if (rv) {
478 LOGE("Error constructing audio format converter: %x\n", rv);
479 mAudioConverter = nullptr;
480 return;
481 }
482 }
485 nsresult
486 AppleMP3Reader::Seek(int64_t aTime,
487 int64_t aStartTime,
488 int64_t aEndTime,
489 int64_t aCurrentTime)
490 {
491 MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread");
492 NS_ASSERTION(aStartTime < aEndTime,
493 "Seeking should happen over a positive range");
495 // Find the exact frame/packet that contains |aTime|.
496 mCurrentAudioFrame = aTime * mAudioSampleRate / USECS_PER_S;
497 SInt64 packet = mCurrentAudioFrame / mAudioFramesPerCompressedPacket;
499 // |AudioFileStreamSeek| will pass back through |byteOffset| the byte offset
500 // into the stream it expects next time it reads.
501 SInt64 byteOffset;
502 UInt32 flags = 0;
504 OSStatus rv = AudioFileStreamSeek(mAudioFileStream,
505 packet,
506 &byteOffset,
507 &flags);
509 if (rv) {
510 LOGE("Couldn't seek demuxer. Error code %x\n", rv);
511 return NS_ERROR_FAILURE;
512 }
514 LOGD("computed byte offset = %lld; estimated = %s\n",
515 byteOffset,
516 (flags & kAudioFileStreamSeekFlag_OffsetIsEstimated) ? "YES" : "NO");
518 mDecoder->GetResource()->Seek(nsISeekableStream::NS_SEEK_SET, byteOffset);
520 ResetDecode();
522 return NS_OK;
523 }
525 void
526 AppleMP3Reader::NotifyDataArrived(const char* aBuffer,
527 uint32_t aLength,
528 int64_t aOffset)
529 {
530 MOZ_ASSERT(NS_IsMainThread());
531 if (!mMP3FrameParser.NeedsData()) {
532 return;
533 }
535 mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
537 uint64_t duration = mMP3FrameParser.GetDuration();
538 if (duration != mDuration) {
539 LOGD("Updating media duration to %lluus\n", duration);
540 mDuration = duration;
541 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
542 mDecoder->UpdateEstimatedMediaDuration(duration);
543 }
544 }
546 } // namespace mozilla