content/media/apple/AppleMP3Reader.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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

mercurial