Bug 1089159 - Correctly use MediaCodec's audio output format r=cpearce a=lsblakk
authorJames Willcox <snorp@snorp.net>
Thu, 06 Nov 2014 09:34:22 -0600
changeset 225977 53692e16c248
parent 225976 a915fb067948
child 225978 4e453b566e83
push id4089
push userjwillcox@mozilla.com
push date2014-11-06 15:37 +0000
treeherdermozilla-beta@53692e16c248 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, lsblakk
bugs1089159
milestone34.0
Bug 1089159 - Correctly use MediaCodec's audio output format r=cpearce a=lsblakk --HG-- extra : rebase_source : 2b3b3a43e54cdcdac87e303a2705e8be7c60d3dd
content/media/fmp4/android/AndroidDecoderModule.cpp
content/media/fmp4/android/AndroidDecoderModule.h
--- a/content/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/content/media/fmp4/android/AndroidDecoderModule.cpp
@@ -64,17 +64,17 @@ public:
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
     mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
     return MediaCodecDataDecoder::Input(aSample);
   }
 
-  virtual nsresult PostOutput(BufferInfo* aInfo, Microseconds aDuration) MOZ_OVERRIDE {
+  virtual nsresult PostOutput(BufferInfo* aInfo, MediaFormat* aFormat, Microseconds aDuration) MOZ_OVERRIDE {
     VideoInfo videoInfo;
     videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
 
     bool isSync = false;
     if (MediaCodec::getBUFFER_FLAG_SYNC_FRAME() & aInfo->getFlags()) {
       isSync = true;
     }
 
@@ -101,44 +101,39 @@ public:
 protected:
   layers::ImageContainer* mImageContainer;
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   nsRefPtr<AndroidSurfaceTexture> mSurfaceTexture;
 };
 
 class AudioDataDecoder : public MediaCodecDataDecoder {
 public:
-  AudioDataDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
-                   MediaFormat* aFormat, MediaDataDecoderCallback* aCallback)
-  : MediaCodecDataDecoder(MediaData::Type::AUDIO_SAMPLES, aConfig.mime_type, aFormat, aCallback)
-  , mConfig(aConfig)
+  AudioDataDecoder(const char* aMimeType, MediaFormat* aFormat, MediaDataDecoderCallback* aCallback)
+  : MediaCodecDataDecoder(MediaData::Type::AUDIO_SAMPLES, aMimeType, aFormat, aCallback)
   {
-    MOZ_ASSERT(mConfig.bits_per_sample == 16, "We only support 16-bit audio");
   }
 
-  nsresult Output(BufferInfo* aInfo, void* aBuffer, Microseconds aDuration) {
+  nsresult Output(BufferInfo* aInfo, void* aBuffer, MediaFormat* aFormat, Microseconds aDuration) {
     // The output on Android is always 16-bit signed
 
-    uint32_t numChannels = mConfig.channel_count;
+    uint32_t numChannels = aFormat->GetInteger(NS_LITERAL_STRING("channel-count"));
+    uint32_t sampleRate = aFormat->GetInteger(NS_LITERAL_STRING("sample-rate"));
     uint32_t numFrames = (aInfo->getSize() / numChannels) / 2;
 
     AudioDataValue* audio = new AudioDataValue[aInfo->getSize()];
     PodCopy(audio, static_cast<AudioDataValue*>(aBuffer), aInfo->getSize());
 
     mCallback->Output(new AudioData(aInfo->getOffset(), aInfo->getPresentationTimeUs(),
                                     aDuration,
                                     numFrames,
                                     audio,
                                     numChannels,
-                                    mConfig.samples_per_second));
+                                    sampleRate));
     return NS_OK;
   }
-
-protected:
-  const mp4_demuxer::AudioDecoderConfig& mConfig;
 };
 
 
 bool AndroidDecoderModule::SupportsAudioMimeType(const char* aMimeType) {
   JNIEnv* env = GetJNIForThread();
   MediaCodec* decoder = CreateDecoder(env, aMimeType);
   bool supports = (decoder != nullptr);
   delete decoder;
@@ -176,16 +171,17 @@ AndroidDecoderModule::CreateH264Decoder(
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
                                          MediaTaskQueue* aAudioTaskQueue,
                                          MediaDataDecoderCallback* aCallback)
 {
+  MOZ_ASSERT(aConfig.bits_per_sample == 16, "We only handle 16-bit audio!");
 
   nsAutoString mimeType;
   mimeType.AssignASCII(aConfig.mime_type);
 
   jobject jFormat = MediaFormat::CreateAudioFormat(mimeType,
                                                    aConfig.samples_per_second,
                                                    aConfig.channel_count);
 
@@ -211,17 +207,17 @@ AndroidDecoderModule::CreateAudioDecoder
     env->DeleteLocalRef(buffer);
   }
 
   if (mimeType.EqualsLiteral("audio/mp4a-latm")) {
     format->SetInteger(NS_LITERAL_STRING("is-adts"), 1);
   }
 
   nsRefPtr<MediaDataDecoder> decoder =
-    new AudioDataDecoder(aConfig, format, aCallback);
+    new AudioDataDecoder(aConfig.mime_type, format, aCallback);
 
   return decoder.forget();
 
 }
 
 
 nsresult AndroidDecoderModule::Shutdown()
 {
@@ -297,16 +293,18 @@ nsresult MediaCodecDataDecoder::InitDeco
 
 void MediaCodecDataDecoder::DecoderLoop()
 {
   bool outputDone = false;
 
   JNIEnv* env = GetJNIForThread();
   mp4_demuxer::MP4Sample* sample = nullptr;
 
+  nsAutoPtr<MediaFormat> outputFormat;
+
   for (;;) {
     {
       MonitorAutoLock lock(mMonitor);
       while (!mStopping && !mDraining && mQueue.empty()) {
         if (mQueue.empty()) {
           // We could be waiting here forever if we don't signal that we need more input
           mCallback->InputExhausted();
         }
@@ -365,17 +363,17 @@ void MediaCodecDataDecoder::DecoderLoop(
 
       int outputStatus = mDecoder->DequeueOutputBuffer(bufferInfo.wrappedObject(), DECODER_TIMEOUT);
       if (outputStatus == MediaCodec::getINFO_TRY_AGAIN_LATER()) {
         // We might want to call mCallback->InputExhausted() here, but there seems to be
         // some possible bad interactions here with the threading
       } else if (outputStatus == MediaCodec::getINFO_OUTPUT_BUFFERS_CHANGED()) {
         ResetOutputBuffers();
       } else if (outputStatus == MediaCodec::getINFO_OUTPUT_FORMAT_CHANGED()) {
-        // Don't care, we use SurfaceTexture for video
+        outputFormat = new MediaFormat(mDecoder->GetOutputFormat(), GetJNIForThread());
       } else if (outputStatus < 0) {
         printf_stderr("unknown error from decoder! %d\n", outputStatus);
         mCallback->Error();
       } else {
         // We have a valid buffer index >= 0 here
         if (bufferInfo.getFlags() & MediaCodec::getBUFFER_FLAG_END_OF_STREAM()) {
           outputDone = true;
         }
@@ -387,23 +385,23 @@ void MediaCodecDataDecoder::DecoderLoop(
           duration = mDurations.front();
           mDurations.pop();
         }
 
         jobject buffer = env->GetObjectArrayElement(mOutputBuffers, outputStatus);
         if (buffer) {
           // The buffer will be null on Android L if we are decoding to a Surface
           void* directBuffer = env->GetDirectBufferAddress(buffer);
-          Output(&bufferInfo, directBuffer, duration);
+          Output(&bufferInfo, directBuffer, outputFormat, duration);
         }
 
         // The Surface will be updated at this point (for video)
         mDecoder->ReleaseOutputBuffer(outputStatus, true);
 
-        PostOutput(&bufferInfo, duration);
+        PostOutput(&bufferInfo, outputFormat, duration);
 
         if (buffer) {
           env->DeleteLocalRef(buffer);
         }
       }
     }
   }
 
--- a/content/media/fmp4/android/AndroidDecoderModule.h
+++ b/content/media/fmp4/android/AndroidDecoderModule.h
@@ -89,18 +89,18 @@ protected:
   bool mDraining;
   bool mStopping;
 
   SampleQueue mQueue;
   std::queue<Microseconds> mDurations;
 
   virtual nsresult InitDecoder(jobject aSurface = nullptr);
 
-  virtual nsresult Output(mozilla::widget::android::BufferInfo* aInfo, void* aBuffer, Microseconds aDuration) { return NS_OK; }
-  virtual nsresult PostOutput(mozilla::widget::android::BufferInfo* aInfo, Microseconds aDuration) { return NS_OK; }
+  virtual nsresult Output(mozilla::widget::android::BufferInfo* aInfo, void* aBuffer, mozilla::widget::android::MediaFormat* aFormat, Microseconds aDuration) { return NS_OK; }
+  virtual nsresult PostOutput(mozilla::widget::android::BufferInfo* aInfo, mozilla::widget::android::MediaFormat* aFormat, Microseconds aDuration) { return NS_OK; }
 
   void ResetInputBuffers();
   void ResetOutputBuffers();
 
   void DecoderLoop();
   virtual void ClearQueue();
 };