michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "MediaDecoderReader.h" michael@0: #include "PlatformDecoderModule.h" michael@0: #include "nsRect.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "mozilla/CheckedInt.h" michael@0: #include "VideoUtils.h" michael@0: #include "ImageContainer.h" michael@0: #include "mp4_demuxer/mp4_demuxer.h" michael@0: #include "MediaTaskQueue.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Decoder that uses a passed in object's Create function to create blank michael@0: // MediaData objects. michael@0: template michael@0: class BlankMediaDataDecoder : public MediaDataDecoder { michael@0: public: michael@0: michael@0: BlankMediaDataDecoder(BlankMediaDataCreator* aCreator, michael@0: MediaTaskQueue* aTaskQueue, michael@0: MediaDataDecoderCallback* aCallback) michael@0: : mCreator(aCreator) michael@0: , mTaskQueue(aTaskQueue) michael@0: , mCallback(aCallback) michael@0: { michael@0: } michael@0: michael@0: virtual nsresult Init() MOZ_OVERRIDE { michael@0: return NS_OK; michael@0: } michael@0: michael@0: virtual nsresult Shutdown() MOZ_OVERRIDE { michael@0: return NS_OK; michael@0: } michael@0: michael@0: class OutputEvent : public nsRunnable { michael@0: public: michael@0: OutputEvent(mp4_demuxer::MP4Sample* aSample, michael@0: MediaDataDecoderCallback* aCallback, michael@0: BlankMediaDataCreator* aCreator) michael@0: : mSample(aSample) michael@0: , mCreator(aCreator) michael@0: , mCallback(aCallback) michael@0: { michael@0: } michael@0: NS_IMETHOD Run() MOZ_OVERRIDE michael@0: { michael@0: mCallback->Output(mCreator->Create(mSample->composition_timestamp, michael@0: mSample->duration, michael@0: mSample->byte_offset)); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsAutoPtr mSample; michael@0: BlankMediaDataCreator* mCreator; michael@0: MediaDataDecoderCallback* mCallback; michael@0: }; michael@0: michael@0: virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE michael@0: { michael@0: // The MediaDataDecoder must delete the sample when we're finished michael@0: // with it, so the OutputEvent stores it in an nsAutoPtr and deletes michael@0: // it once it's run. michael@0: RefPtr r(new OutputEvent(aSample, mCallback, mCreator)); michael@0: mTaskQueue->Dispatch(r); michael@0: return NS_OK; michael@0: } michael@0: michael@0: virtual nsresult Flush() MOZ_OVERRIDE { michael@0: return NS_OK; michael@0: } michael@0: michael@0: virtual nsresult Drain() MOZ_OVERRIDE { michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsAutoPtr mCreator; michael@0: nsAutoPtr mOutput; michael@0: RefPtr mTaskQueue; michael@0: MediaDataDecoderCallback* mCallback; michael@0: }; michael@0: michael@0: class BlankVideoDataCreator { michael@0: public: michael@0: BlankVideoDataCreator(uint32_t aFrameWidth, michael@0: uint32_t aFrameHeight, michael@0: layers::ImageContainer* aImageContainer) michael@0: : mFrameWidth(aFrameWidth) michael@0: , mFrameHeight(aFrameHeight) michael@0: , mImageContainer(aImageContainer) michael@0: { michael@0: mInfo.mDisplay = nsIntSize(mFrameWidth, mFrameHeight); michael@0: mPicture = gfx::IntRect(0, 0, mFrameWidth, mFrameHeight); michael@0: } michael@0: michael@0: MediaData* Create(Microseconds aDTS, michael@0: Microseconds aDuration, michael@0: int64_t aOffsetInStream) michael@0: { michael@0: // Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane, michael@0: // with a U and V plane that are half the size of the Y plane, i.e 8 bit, michael@0: // 2x2 subsampled. Have the data pointers of each frame point to the michael@0: // first plane, they'll always be zero'd memory anyway. michael@0: uint8_t* frame = new uint8_t[mFrameWidth * mFrameHeight]; michael@0: memset(frame, 0, mFrameWidth * mFrameHeight); michael@0: VideoData::YCbCrBuffer buffer; michael@0: michael@0: // Y plane. michael@0: buffer.mPlanes[0].mData = frame; michael@0: buffer.mPlanes[0].mStride = mFrameWidth; michael@0: buffer.mPlanes[0].mHeight = mFrameHeight; michael@0: buffer.mPlanes[0].mWidth = mFrameWidth; michael@0: buffer.mPlanes[0].mOffset = 0; michael@0: buffer.mPlanes[0].mSkip = 0; michael@0: michael@0: // Cb plane. michael@0: buffer.mPlanes[1].mData = frame; michael@0: buffer.mPlanes[1].mStride = mFrameWidth / 2; michael@0: buffer.mPlanes[1].mHeight = mFrameHeight / 2; michael@0: buffer.mPlanes[1].mWidth = mFrameWidth / 2; michael@0: buffer.mPlanes[1].mOffset = 0; michael@0: buffer.mPlanes[1].mSkip = 0; michael@0: michael@0: // Cr plane. michael@0: buffer.mPlanes[2].mData = frame; michael@0: buffer.mPlanes[2].mStride = mFrameWidth / 2; michael@0: buffer.mPlanes[2].mHeight = mFrameHeight / 2; michael@0: buffer.mPlanes[2].mWidth = mFrameWidth / 2; michael@0: buffer.mPlanes[2].mOffset = 0; michael@0: buffer.mPlanes[2].mSkip = 0; michael@0: michael@0: return VideoData::Create(mInfo, michael@0: mImageContainer, michael@0: nullptr, michael@0: aOffsetInStream, michael@0: aDTS, michael@0: aDuration, michael@0: buffer, michael@0: true, michael@0: aDTS, michael@0: mPicture); michael@0: } michael@0: private: michael@0: VideoInfo mInfo; michael@0: gfx::IntRect mPicture; michael@0: uint32_t mFrameWidth; michael@0: uint32_t mFrameHeight; michael@0: RefPtr mImageContainer; michael@0: }; michael@0: michael@0: michael@0: class BlankAudioDataCreator { michael@0: public: michael@0: BlankAudioDataCreator(uint32_t aChannelCount, michael@0: uint32_t aSampleRate, michael@0: uint16_t aBitsPerSample) michael@0: : mFrameSum(0) michael@0: , mChannelCount(aChannelCount) michael@0: , mSampleRate(aSampleRate) michael@0: { michael@0: } michael@0: michael@0: MediaData* Create(Microseconds aDTS, michael@0: Microseconds aDuration, michael@0: int64_t aOffsetInStream) michael@0: { michael@0: // Convert duration to frames. We add 1 to duration to account for michael@0: // rounding errors, so we get a consistent tone. michael@0: CheckedInt64 frames = UsecsToFrames(aDuration+1, mSampleRate); michael@0: if (!frames.isValid() || michael@0: !mChannelCount || michael@0: !mSampleRate || michael@0: frames.value() > (UINT32_MAX / mChannelCount)) { michael@0: return nullptr; michael@0: } michael@0: AudioDataValue* samples = new AudioDataValue[frames.value() * mChannelCount]; michael@0: // Fill the sound buffer with an A4 tone. michael@0: static const float pi = 3.14159265f; michael@0: static const float noteHz = 440.0f; michael@0: for (int i = 0; i < frames.value(); i++) { michael@0: float f = sin(2 * pi * noteHz * mFrameSum / mSampleRate); michael@0: for (unsigned c = 0; c < mChannelCount; c++) { michael@0: samples[i * mChannelCount + c] = AudioDataValue(f); michael@0: } michael@0: mFrameSum++; michael@0: } michael@0: return new AudioData(aOffsetInStream, michael@0: aDTS, michael@0: aDuration, michael@0: uint32_t(frames.value()), michael@0: samples, michael@0: mChannelCount); michael@0: } michael@0: michael@0: private: michael@0: int64_t mFrameSum; michael@0: uint32_t mChannelCount; michael@0: uint32_t mSampleRate; michael@0: }; michael@0: michael@0: class BlankDecoderModule : public PlatformDecoderModule { michael@0: public: michael@0: michael@0: // Called when the decoders have shutdown. Main thread only. michael@0: virtual nsresult Shutdown() MOZ_OVERRIDE { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Decode thread. michael@0: virtual MediaDataDecoder* CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig, michael@0: layers::LayersBackend aLayersBackend, michael@0: layers::ImageContainer* aImageContainer, michael@0: MediaTaskQueue* aVideoTaskQueue, michael@0: MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE { michael@0: BlankVideoDataCreator* decoder = new BlankVideoDataCreator(aConfig.visible_rect().width(), michael@0: aConfig.visible_rect().height(), michael@0: aImageContainer); michael@0: return new BlankMediaDataDecoder(decoder, michael@0: aVideoTaskQueue, michael@0: aCallback); michael@0: } michael@0: michael@0: // Decode thread. michael@0: virtual MediaDataDecoder* CreateAACDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig, michael@0: MediaTaskQueue* aAudioTaskQueue, michael@0: MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE { michael@0: BlankAudioDataCreator* decoder = michael@0: new BlankAudioDataCreator(ChannelLayoutToChannelCount(aConfig.channel_layout()), michael@0: aConfig.samples_per_second(), michael@0: aConfig.bits_per_channel()); michael@0: return new BlankMediaDataDecoder(decoder, michael@0: aAudioTaskQueue, michael@0: aCallback); michael@0: } michael@0: }; michael@0: michael@0: PlatformDecoderModule* CreateBlankDecoderModule() michael@0: { michael@0: return new BlankDecoderModule(); michael@0: } michael@0: michael@0: } // namespace mozilla