Bug 959440 - Various cleanups in MP4Reader. r=kinetik
authorChris Pearce <cpearce@mozilla.com>
Wed, 15 Jan 2014 16:13:54 +1300
changeset 163447 eb70f67d384a2de272246337052f7225ee84ed4e
parent 163446 d633e3ff201329c99a71bfc1311edc28a061cb99
child 163448 50be01b3989c5f430f6e78fd986e0c96dae4416a
push id25996
push useremorley@mozilla.com
push dateWed, 15 Jan 2014 15:54:39 +0000
treeherdermozilla-central@dd2cf81c56b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs959440
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 959440 - Various cleanups in MP4Reader. r=kinetik Change PlatformDecoderModule::Create*Decoder() to take an mp4_demuxer::{Audio,Video}DecoderConfig parameter, instead of enumerating all parameters. This means the platform decoders can have more data if need be, like the AACAudioConfig. Change MediaDataDecoder::Input() to take an nsAutoPtr<MP4Sample>&. The sample will be deleted by the caller (MP4Reader) if Input() returns DECODE_STATUS_OK, but if the MediaDataDecoder wants to assume responsibility of the lifecycle of the sample (say to enqueue it), it can forget() on the nsAutoPtr& passed in and assume responsibility. Call PlatformDecoderModule::Create() on the decode thread. This is a step towards making these classes decode-thread only. Add PlatformDecoderModule::Init(), which caches the pref's we need, since PlatformDecoderModule::Create() is no longer called on the main thread, we can no longer access them in there. Add Init() method to MediaDataDecoder interface. This is so that we can call MediaDataDecoder::Shutdown() to unblock the initialization of a decoder, if that init needs to block. Pass LayersBackend type to WMFVideoDecoder, so it knows whether to init DXVA.
content/media/fmp4/BlankDecoderModule.cpp
content/media/fmp4/MP4Reader.cpp
content/media/fmp4/MP4Reader.h
content/media/fmp4/PlatformDecoderModule.cpp
content/media/fmp4/PlatformDecoderModule.h
--- a/content/media/fmp4/BlankDecoderModule.cpp
+++ b/content/media/fmp4/BlankDecoderModule.cpp
@@ -6,54 +6,59 @@
 
 #include "MediaDecoderReader.h"
 #include "PlatformDecoderModule.h"
 #include "nsRect.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/CheckedInt.h"
 #include "VideoUtils.h"
 #include "ImageContainer.h"
+#include "mp4_demuxer/mp4_demuxer.h"
+#include "mp4_demuxer/audio_decoder_config.h"
 
 namespace mozilla {
 
 // Decoder that uses a passed in object's Create function to create blank
 // MediaData objects.
 template<class BlankMediaDataCreator>
 class BlankMediaDataDecoder : public MediaDataDecoder {
 public:
 
   BlankMediaDataDecoder(BlankMediaDataCreator* aCreator)
     : mCreator(aCreator),
-      mNextDTS(-1),
+      mNextTimeStamp(-1),
       mNextOffset(-1)
   {
   }
 
+  virtual nsresult Init() MOZ_OVERRIDE {
+    return NS_OK;
+  }
+
   virtual nsresult Shutdown() MOZ_OVERRIDE {
     return NS_OK;
   }
 
-  virtual DecoderStatus Input(const uint8_t* aData,
-                              uint32_t aLength,
-                              Microseconds aDTS,
-                              Microseconds aPTS,
-                              int64_t aOffsetInStream) MOZ_OVERRIDE
+  virtual DecoderStatus Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample) MOZ_OVERRIDE
   {
     // Accepts input, and outputs on the second input, using the difference
     // in DTS as the duration.
     if (mOutput) {
       return DECODE_STATUS_NOT_ACCEPTING;
     }
-    if (mNextDTS != -1 && mNextOffset != -1) {
-      Microseconds duration = aDTS - mNextDTS;
-      mOutput = mCreator->Create(mNextDTS, duration, mNextOffset);
+
+    Microseconds timestamp = aSample->composition_timestamp;
+    if (mNextTimeStamp != -1 && mNextOffset != -1) {
+      Microseconds duration = timestamp - mNextTimeStamp;
+      mOutput = mCreator->Create(mNextTimeStamp, duration, mNextOffset);
     }
 
-    mNextDTS = aDTS;
-    mNextOffset = aOffsetInStream;
+    mNextTimeStamp = timestamp;
+    mNextOffset = aSample->byte_offset;
+
     return DECODE_STATUS_OK;
   }
 
   virtual DecoderStatus Output(nsAutoPtr<MediaData>& aOutData) MOZ_OVERRIDE
   {
     if (!mOutput) {
       return DECODE_STATUS_NEED_MORE_INPUT;
     }
@@ -61,17 +66,17 @@ public:
     return DECODE_STATUS_OK;
   }
 
   virtual DecoderStatus Flush()  MOZ_OVERRIDE {
     return DECODE_STATUS_OK;
   }
 private:
   nsAutoPtr<BlankMediaDataCreator> mCreator;
-  Microseconds mNextDTS;
+  Microseconds mNextTimeStamp;
   int64_t mNextOffset;
   nsAutoPtr<MediaData> mOutput;
   bool mHasInput;
 };
 
 static const uint32_t sFrameWidth = 320;
 static const uint32_t sFrameHeight = 240;
 
@@ -193,31 +198,29 @@ class BlankDecoderModule : public Platfo
 public:
 
   // Called when the decoders have shutdown. Main thread only.
   virtual nsresult Shutdown() MOZ_OVERRIDE {
     return NS_OK;
   }
 
   // Decode thread.
-  virtual MediaDataDecoder* CreateH264Decoder(layers::LayersBackend aLayersBackend,
+  virtual MediaDataDecoder* CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
+                                              layers::LayersBackend aLayersBackend,
                                               layers::ImageContainer* aImageContainer) MOZ_OVERRIDE {
     BlankVideoDataCreator* decoder = new BlankVideoDataCreator(aImageContainer);
     return new BlankMediaDataDecoder<BlankVideoDataCreator>(decoder);
   }
 
   // Decode thread.
-  virtual MediaDataDecoder* CreateAACDecoder(uint32_t aChannelCount,
-                                             uint32_t aSampleRate,
-                                             uint16_t aBitsPerSample,
-                                             const uint8_t* aUserData,
-                                             uint32_t aUserDataLength) MOZ_OVERRIDE {
-    BlankAudioDataCreator* decoder = new BlankAudioDataCreator(aChannelCount,
-                                                               aSampleRate,
-                                                               aBitsPerSample);
+  virtual MediaDataDecoder* CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig) MOZ_OVERRIDE {
+    BlankAudioDataCreator* decoder =
+      new BlankAudioDataCreator(ChannelLayoutToChannelCount(aConfig.channel_layout()),
+                                aConfig.samples_per_second(),
+                                aConfig.bits_per_channel());
     return new BlankMediaDataDecoder<BlankAudioDataCreator>(decoder);
   }
 };
 
 PlatformDecoderModule* CreateBlankDecoderModule()
 {
   return new BlankDecoderModule();
 }
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -118,22 +118,20 @@ MP4Reader::InitLayersBackendType()
 
   mLayersBackendType = layerManager->GetBackendType();
 }
 
 nsresult
 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+  PlatformDecoderModule::Init();
   mMP4Stream = new MP4Stream(mDecoder->GetResource());
   mDemuxer = new MP4Demuxer(mMP4Stream);
 
-  mPlatform = PlatformDecoderModule::Create();
-  NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
-
   InitLayersBackendType();
 
   return NS_OK;
 }
 
 nsresult
 MP4Reader::ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags)
@@ -152,35 +150,38 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
   const VideoDecoderConfig& video = mDemuxer->VideoConfig();
   mInfo.mVideo.mHasVideo = mHasVideo = mDemuxer->HasVideo() &&
                                        video.IsValidConfig();
   // If we have video, we *only* allow H.264 to be decoded.
   if (mHasVideo && video.codec() != kCodecH264) {
     return NS_ERROR_FAILURE;
   }
 
+  mPlatform = PlatformDecoderModule::Create();
+  NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
+
   if (mHasAudio) {
     mInfo.mAudio.mRate = audio.samples_per_second();
     mInfo.mAudio.mChannels = ChannelLayoutToChannelCount(audio.channel_layout());
-    mAudioDecoder = mPlatform->CreateAACDecoder(mInfo.mAudio.mChannels,
-                                                mInfo.mAudio.mRate,
-                                                audio.bits_per_channel(),
-                                                audio.extra_data(),
-                                                audio.extra_data_size());
+    mAudioDecoder = mPlatform->CreateAACDecoder(audio);
     NS_ENSURE_TRUE(mAudioDecoder != nullptr, NS_ERROR_FAILURE);
+    nsresult rv = mAudioDecoder->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mInfo.mVideo.mHasVideo = mHasVideo = mDemuxer->HasVideo();
   if (mHasVideo) {
-    const VideoDecoderConfig& config = mDemuxer->VideoConfig();
-    IntSize sz = config.natural_size();
+    IntSize sz = video.natural_size();
     mInfo.mVideo.mDisplay = nsIntSize(sz.width(), sz.height());
-    mVideoDecoder = mPlatform->CreateH264Decoder(mLayersBackendType,
+    mVideoDecoder = mPlatform->CreateH264Decoder(video,
+                                                 mLayersBackendType,
                                                  mDecoder->GetImageContainer());
     NS_ENSURE_TRUE(mVideoDecoder != nullptr, NS_ERROR_FAILURE);
+    nsresult rv = mVideoDecoder->Init();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Get the duration, and report it to the decoder if we have it.
   Microseconds duration = mDemuxer->Duration();
   if (duration != -1) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(duration);
   }
@@ -277,26 +278,25 @@ MP4Reader::Decode(TrackType aTrack, nsAu
       nsAutoPtr<MP4Sample> compressed;
       do {
         compressed = PopSample(aTrack);
         if (!compressed) {
           // EOS, or error. Let the state machine know there are no more
           // frames coming.
           return false;
         }
-        const std::vector<uint8_t>* data = compressed->data;
-        status = decoder->Input(&data->front(),
-                                data->size(),
-                                compressed->decode_timestamp,
-                                compressed->composition_timestamp,
-                                compressed->byte_offset);
+        status = decoder->Input(compressed);
       } while (status == DECODE_STATUS_OK);
       if (status == DECODE_STATUS_NOT_ACCEPTING) {
         // Decoder should now be able to produce an output.
-        SampleQueue(aTrack).push_front(compressed.forget());
+        if (compressed != nullptr) {
+          // Decoder didn't consume data, attempt to decode the same
+          // sample next time.
+          SampleQueue(aTrack).push_front(compressed.forget());
+        }
         continue;
       }
       LOG("MP4Reader decode failure. track=%d status=%d\n", aTrack, status);
       return false;
     } else {
       LOG("MP4Reader unexpected error. track=%d status=%d\n", aTrack, status);
       return false;
     }
@@ -396,22 +396,24 @@ MP4Reader::Seek(int64_t aTime,
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 void
 MP4Reader::OnDecodeThreadStart()
 {
   MOZ_ASSERT(!NS_IsMainThread(), "Must not be on main thread.");
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  MOZ_ASSERT(mPlatform);
-  mPlatform->OnDecodeThreadStart();
+  if (mPlatform) {
+    mPlatform->OnDecodeThreadStart();
+  }
 }
 
 void
 MP4Reader::OnDecodeThreadFinish()
 {
   MOZ_ASSERT(!NS_IsMainThread(), "Must not be on main thread.");
   MOZ_ASSERT(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  MOZ_ASSERT(mPlatform);
-  mPlatform->OnDecodeThreadFinish();
+  if (mPlatform) {
+    mPlatform->OnDecodeThreadFinish();
+  }
 }
 
 } // namespace mozilla
--- a/content/media/fmp4/MP4Reader.h
+++ b/content/media/fmp4/MP4Reader.h
@@ -9,16 +9,17 @@
 
 #include "MediaDecoderReader.h"
 #include "nsAutoPtr.h"
 #include "PlatformDecoderModule.h"
 #include "mp4_demuxer/mp4_demuxer.h"
 #include "mp4_demuxer/box_definitions.h"
 
 #include <deque>
+#include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
 
 typedef std::deque<mp4_demuxer::MP4Sample*> MP4SampleQueue;
@@ -77,14 +78,13 @@ private:
 
   MP4SampleQueue mCompressedAudioQueue;
   MP4SampleQueue mCompressedVideoQueue;
 
   layers::LayersBackend mLayersBackendType;
 
   bool mHasAudio;
   bool mHasVideo;
-
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/media/fmp4/PlatformDecoderModule.cpp
+++ b/content/media/fmp4/PlatformDecoderModule.cpp
@@ -9,25 +9,40 @@
 #include "WMFDecoderModule.h"
 #endif
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 
 extern PlatformDecoderModule* CreateBlankDecoderModule();
 
+bool PlatformDecoderModule::sUseBlankDecoder = false;
+
+/* static */
+void
+PlatformDecoderModule::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  static bool alreadyInitialized = false;
+  if (alreadyInitialized) {
+    return;
+  }
+  alreadyInitialized = true;
+  sUseBlankDecoder = Preferences::GetBool("media.fragmented-mp4.use-blank-decoder");
+}
+
 /* static */
 PlatformDecoderModule*
 PlatformDecoderModule::Create()
 {
-  if (Preferences::GetBool("media.fragmented-mp4.use-blank-decoder")) {
+  if (sUseBlankDecoder) {
     return CreateBlankDecoderModule();
   }
 #ifdef XP_WIN
   nsAutoPtr<WMFDecoderModule> m(new WMFDecoderModule());
-  if (NS_SUCCEEDED(m->Init())) {
+  if (NS_SUCCEEDED(m->Startup())) {
     return m.forget();
   }
 #endif
   return nullptr;
 }
 
 } // namespace mozilla
--- a/content/media/fmp4/PlatformDecoderModule.h
+++ b/content/media/fmp4/PlatformDecoderModule.h
@@ -4,16 +4,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(PlatformDecoderModule_h_)
 #define PlatformDecoderModule_h_
 
 #include "MediaDecoderReader.h"
 #include "mozilla/layers/LayersTypes.h"
+#include "nsTArray.h"
+
+namespace mp4_demuxer {
+class VideoDecoderConfig;
+class AudioDecoderConfig;
+struct MP4Sample;
+}
 
 namespace mozilla {
 
 namespace layers {
 class ImageContainer;
 }
 
 class MediaDataDecoder;
@@ -30,16 +37,19 @@ typedef int64_t Microseconds;
 // means that we won't have fragmented MP4 supported in Media Source
 // Extensions on platforms without PlatformDecoderModules.
 //
 // A cross-platform decoder module that discards input and produces "blank"
 // output samples exists for testing, and is created if the pref
 // "media.fragmented-mp4.use-blank-decoder" is true.
 class PlatformDecoderModule {
 public:
+  // Call on the main thread to initialize the static state
+  // needed by Create().
+  static void Init();
 
   // Factory method that creates the appropriate PlatformDecoderModule for
   // the platform we're running on. Caller is responsible for deleting this
   // instance. It's expected that there will be multiple
   // PlatformDecoderModules alive at the same time. There is one
   // PlatformDecoderModule's created per MP4Reader.
   // This is called on the main thread.
   static PlatformDecoderModule* Create();
@@ -51,40 +61,39 @@ public:
   // Called on the main thread only.
   virtual nsresult Shutdown() = 0;
 
   // Creates and initializes an H.264 decoder. The layers backend is
   // passed in so that decoders can determine whether hardware accelerated
   // decoding can be used. Returns nullptr if the decoder can't be
   // initialized.
   // Called on decode thread.
-  virtual MediaDataDecoder* CreateH264Decoder(layers::LayersBackend aLayersBackend,
+  virtual MediaDataDecoder* CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
+                                              layers::LayersBackend aLayersBackend,
                                               layers::ImageContainer* aImageContainer) = 0;
 
   // Creates and initializes an AAC decoder with the specified properties.
   // The raw AAC AudioSpecificConfig as contained in the esds box. Some
   // decoders need that to initialize. The caller owns the AAC config,
   // so it must be copied if it is to be retained by the decoder.
   // Returns nullptr if the decoder can't be initialized.
   // Called on decode thread.
-  virtual MediaDataDecoder* CreateAACDecoder(uint32_t aChannelCount,
-                                             uint32_t aSampleRate,
-                                             uint16_t aBitsPerSample,
-                                             const uint8_t* aAACConfig,
-                                             uint32_t aAACConfigLength) = 0;
+  virtual MediaDataDecoder* CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig) = 0;
 
   // Called when a decode thread is started. Called on decode thread.
   virtual void OnDecodeThreadStart() {}
 
   // Called just before a decode thread is finishing. Called on decode thread.
   virtual void OnDecodeThreadFinish() {}
 
   virtual ~PlatformDecoderModule() {}
 protected:
   PlatformDecoderModule() {}
+  // Caches pref media.fragmented-mp4.use-blank-decoder
+  static bool sUseBlankDecoder;
 };
 
 // Return value of the MediaDataDecoder functions.
 enum DecoderStatus {
   DECODE_STATUS_NOT_ACCEPTING, // Can't accept input at this time. Decoder can produce output.
   DECODE_STATUS_NEED_MORE_INPUT, // Can't produce output. Decoder can accept input.
   DECODE_STATUS_OK,
   DECODE_STATUS_ERROR
@@ -97,37 +106,54 @@ enum DecoderStatus {
 // The decoder is assumed to be in one of three mutually exclusive and
 // implicit states: able to accept input, able to produce output, and
 // shutdown. The decoder is assumed to be able to accept input by the time
 // that it's returned by PlatformDecoderModule::Create*Decoder().
 class MediaDataDecoder {
 public:
   virtual ~MediaDataDecoder() {};
 
+  // Initialize the decoder. The decoder should be ready to decode after
+  // this returns. The decoder should do any initialization here, rather
+  // than in its constructor, so that if the MP4Reader needs to Shutdown()
+  // during initialization it can call Shutdown() to cancel this.
+  // Any initialization that requires blocking the calling thread *must*
+  // be done here so that it can be canceled by calling Shutdown()!
+  virtual nsresult Init() = 0;
+
   // Inserts aData into the decoding pipeline. Decoding may begin
-  // asynchronously. The caller owns aData, so it may need to be copied.
-  // The MP4Reader calls Input() with new input in a loop until Input()
-  // stops returning DECODE_STATUS_OK.
+  // asynchronously.
+  //
+  // If the decoder needs to assume ownership of the sample it may do so by
+  // calling forget() on aSample.
+  //
+  // If Input() returns DECODE_STATUS_NOT_ACCEPTING without forget()ing
+  // aSample, then the next call will have the same aSample. Otherwise
+  // the caller will delete aSample after Input() returns.
+  //
+  // The MP4Reader calls Input() in a loop until Input() stops returning
+  // DECODE_STATUS_OK. Input() should return DECODE_STATUS_NOT_ACCEPTING
+  // once the underlying decoder should have enough data to output decoded
+  // data.
+  //
   // Called on the media decode thread.
   // Returns:
   //  - DECODE_STATUS_OK if input was successfully inserted into
   //    the decode pipeline.
   //  - DECODE_STATUS_NOT_ACCEPTING if the decoder cannot accept any input
   //    at this time. The MP4Reader will assume that the decoder can now
   //    produce one or more output samples, and call the Output() function.
   //    The MP4Reader will call Input() again with the same data later,
-  //    after the decoder's Output() function has stopped producing output.
+  //    after the decoder's Output() function has stopped producing output,
+  //    except if Input() called forget() on aSample, whereupon a new sample
+  //    will come in next call.
   //  - DECODE_STATUS_ERROR if the decoder has been shutdown, or some
   //    unspecified error.
   // This function should not return DECODE_STATUS_NEED_MORE_INPUT.
-  virtual DecoderStatus Input(const uint8_t* aData,
-                              uint32_t aLength,
-                              Microseconds aDTS,
-                              Microseconds aPTS,
-                              int64_t aOffsetInStream) = 0;
+  virtual DecoderStatus Input(nsAutoPtr<mp4_demuxer::MP4Sample>& aSample) = 0;
 
   // Blocks until a decoded sample is produced by the deoder. The MP4Reader
   // calls this until it stops returning DECODE_STATUS_OK.
   // Called on the media decode thread.
   // Returns:
   //  - DECODE_STATUS_OK if an output sample was successfully placed in
   //    aOutData. More samples for output may still be available, the
   //    MP4Reader will call again to check.