From cb82b1e0574a65e777d54cbd867bafebff468c97 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Wed, 15 Jan 2014 13:31:12 +1300 Subject: [PATCH] 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&. 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 | 45 ++++++++-------- content/media/fmp4/MP4Reader.cpp | 46 ++++++++-------- content/media/fmp4/MP4Reader.h | 2 +- content/media/fmp4/PlatformDecoderModule.cpp | 19 ++++++- content/media/fmp4/PlatformDecoderModule.h | 56 ++++++++++++++------ 5 files changed, 107 insertions(+), 61 deletions(-) diff --git a/content/media/fmp4/BlankDecoderModule.cpp b/content/media/fmp4/BlankDecoderModule.cpp index b578ebcc6fe..30b65ef9178 100644 --- a/content/media/fmp4/BlankDecoderModule.cpp +++ b/content/media/fmp4/BlankDecoderModule.cpp @@ -11,6 +11,8 @@ #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 { @@ -22,33 +24,36 @@ 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& 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; } @@ -66,7 +71,7 @@ public: } private: nsAutoPtr mCreator; - Microseconds mNextDTS; + Microseconds mNextTimeStamp; int64_t mNextOffset; nsAutoPtr mOutput; bool mHasInput; @@ -198,21 +203,19 @@ public: } // 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(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(decoder); } }; diff --git a/content/media/fmp4/MP4Reader.cpp b/content/media/fmp4/MP4Reader.cpp index 82358465b23..9ac386ce2ab 100644 --- a/content/media/fmp4/MP4Reader.cpp +++ b/content/media/fmp4/MP4Reader.cpp @@ -123,12 +123,10 @@ 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; @@ -157,25 +155,28 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo, 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. @@ -282,16 +283,15 @@ MP4Reader::Decode(TrackType aTrack, nsAutoPtr& aOutData) // frames coming. return false; } - const std::vector* 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); @@ -401,8 +401,9 @@ 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 @@ -410,8 +411,9 @@ 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 diff --git a/content/media/fmp4/MP4Reader.h b/content/media/fmp4/MP4Reader.h index 2a17340998a..fe40be90497 100644 --- a/content/media/fmp4/MP4Reader.h +++ b/content/media/fmp4/MP4Reader.h @@ -14,6 +14,7 @@ #include "mp4_demuxer/box_definitions.h" #include +#include "mozilla/Monitor.h" namespace mozilla { @@ -82,7 +83,6 @@ private: bool mHasAudio; bool mHasVideo; - }; } // namespace mozilla diff --git a/content/media/fmp4/PlatformDecoderModule.cpp b/content/media/fmp4/PlatformDecoderModule.cpp index ed51a6842e7..f00a4740ead 100644 --- a/content/media/fmp4/PlatformDecoderModule.cpp +++ b/content/media/fmp4/PlatformDecoderModule.cpp @@ -14,16 +14,31 @@ 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 m(new WMFDecoderModule()); - if (NS_SUCCEEDED(m->Init())) { + if (NS_SUCCEEDED(m->Startup())) { return m.forget(); } #endif diff --git a/content/media/fmp4/PlatformDecoderModule.h b/content/media/fmp4/PlatformDecoderModule.h index 94674acbefd..17077ff4175 100644 --- a/content/media/fmp4/PlatformDecoderModule.h +++ b/content/media/fmp4/PlatformDecoderModule.h @@ -9,6 +9,13 @@ #include "MediaDecoderReader.h" #include "mozilla/layers/LayersTypes.h" +#include "nsTArray.h" + +namespace mp4_demuxer { +class VideoDecoderConfig; +class AudioDecoderConfig; +class MP4Sample; +} namespace mozilla { @@ -35,6 +42,9 @@ typedef int64_t Microseconds; // "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 @@ -56,7 +66,8 @@ public: // 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. @@ -65,11 +76,7 @@ public: // 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() {} @@ -80,6 +87,8 @@ public: virtual ~PlatformDecoderModule() {} protected: PlatformDecoderModule() {} + // Caches pref media.fragmented-mp4.use-blank-decoder + static bool sUseBlankDecoder; }; // Return value of the MediaDataDecoder functions. @@ -102,10 +111,29 @@ 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 @@ -114,15 +142,13 @@ public: // 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& aSample) = 0; // Blocks until a decoded sample is produced by the deoder. The MP4Reader // calls this until it stops returning DECODE_STATUS_OK.