Created
June 18, 2014 20:59
-
-
Save FlyingJester/ce062ac376d1577724ab to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * 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/. */ | |
| #include "AndroidDecoderModule.h" | |
| #include "AndroidDecoderUtils.h" | |
| #include "PlatformDecoderModule.h" | |
| #include "GeneratedJNIWrappers_2.h" | |
| #include "AndroidBridge.h" | |
| #include "MediaTaskQueue.h" | |
| #include "MediaData.h" | |
| #include "nsThreadUtils.h" | |
| #include "nsAutoPtr.h" | |
| #include <jni.h> | |
| //TODO: Figure out if these should be set from global strings or not. | |
| #define H264_MIME "video/avc" | |
| #define AAC_MIME "audio/mp4a-latm" | |
| #include <cstring> | |
| namespace mozilla { | |
| using mozilla::mcdecode::Decoder; | |
| Decoder *AndroidDecoderModule::GetDecoder(const char *aMimeType) | |
| { | |
| if (aMimeType == nullptr) | |
| MOZ_CRASH("Null string passed."); | |
| if (aMimeType[0] == '\0') | |
| MOZ_CRASH("Zero length string passed."); | |
| nsAutoString lMimeType; | |
| lMimeType.AssignASCII(aMimeType); | |
| jobject decoder = MediaCodec::CreateDecoderByType(lMimeType); | |
| return MediaCodec::Wrap(decoder); | |
| } | |
| // Creates an H.264 decoder. The layers backend is passed in so that | |
| // decoders can determine whether hardware accelerated decoding can be used. | |
| // Asynchronous decoding of video should be done in runnables dispatched | |
| // to aVideoTaskQueue. If the task queue isn't needed, the decoder should | |
| // not hold a reference to it. | |
| // Output and errors should be returned to the reader via aCallback. | |
| // On Windows the task queue's threads in have MSCOM initialized with | |
| // COINIT_MULTITHREADED. | |
| // Returns nullptr if the decoder can't be created. | |
| // It is safe to store a reference to aConfig. | |
| // Called on decode thread. | |
| MediaDataDecoder* AndroidDecoderModule::CreateH264Decoder( | |
| const mp4_demuxer::VideoDecoderConfig& aConfig, | |
| layers::LayersBackend aLayersBackend, | |
| layers::ImageContainer* aImageContainer, | |
| MediaTaskQueue* aVideoTaskQueue, | |
| MediaDataDecoderCallback* aCallback) | |
| { | |
| nsAutoString lMimeType; | |
| lMimeType.AssignASCII(aConfig.mime_type); | |
| jobject lJFormat = MediaFormat::CreateVideoFormat(lMimeType, | |
| aConfig.display_width, | |
| aConfig.display_height); | |
| if(lJFormat == nullptr) | |
| return nullptr; | |
| mcdecode::Format *lFormat = MediaFormat::Wrap(lJFormat); | |
| if(lFormat == nullptr) | |
| return nullptr; | |
| MediaDataDecoder *mcdecoder = new MCDataDecoder(MediaData::Type::VIDEO_FRAME, aConfig.mime_type, lFormat, aCallback, aVideoTaskQueue); | |
| return mcdecoder; | |
| } | |
| // Creates an AAC decoder with the specified properties. | |
| // Asynchronous decoding of audio should be done in runnables dispatched to | |
| // aAudioTaskQueue. If the task queue isn't needed, the decoder should | |
| // not hold a reference to it. | |
| // Output and errors should be returned to the reader via aCallback. | |
| // Returns nullptr if the decoder can't be created. | |
| // On Windows the task queue's threads in have MSCOM initialized with | |
| // COINIT_MULTITHREADED. | |
| // It is safe to store a reference to aConfig. | |
| // Called on decode thread. | |
| MediaDataDecoder* AndroidDecoderModule::CreateAACDecoder( | |
| const mp4_demuxer::AudioDecoderConfig& aConfig, | |
| MediaTaskQueue* aAudioTaskQueue, | |
| MediaDataDecoderCallback* aCallback) | |
| { | |
| nsAutoString lMimeType; | |
| lMimeType.AssignASCII(aConfig.mime_type); | |
| jobject lJFormat = MediaFormat::CreateAudioFormat(lMimeType, | |
| aConfig.samples_per_second, | |
| aConfig.channel_count); | |
| if(lJFormat == nullptr) | |
| return nullptr; | |
| mcdecode::Format *lFormat = MediaFormat::Wrap(lJFormat); | |
| if(lFormat == nullptr) | |
| return nullptr; | |
| const nsJNIString bitratekey(MediaFormat::getKEY_BIT_RATE(), GetJNIForThread()); | |
| lFormat->SetInteger(bitratekey, aConfig.bits_per_sample*aConfig.samples_per_second); | |
| MediaDataDecoder *mcdecoder = new MCDataDecoder(MediaData::Type::AUDIO_SAMPLES, aConfig.mime_type, lFormat, aCallback, aAudioTaskQueue); | |
| return mcdecoder; | |
| } | |
| nsresult AndroidDecoderModule::Shutdown() | |
| { | |
| return NS_OK; | |
| } | |
| // MediaDataDecoder is the interface exposed by decoders created by the | |
| // PlatformDecoderModule's Create*Decoder() functions. The type of | |
| // media data that the decoder accepts as valid input and produces as | |
| // output is determined when the MediaDataDecoder is created. | |
| // | |
| // All functions must be threadsafe, and be able to be called on an | |
| // arbitrary thread. | |
| // | |
| // Decoding is done asynchronously. Any async work can be done on the | |
| // MediaTaskQueue passed into the PlatformDecoderModules's Create*Decoder() | |
| // function. This may not be necessary for platforms with async APIs | |
| // for decoding. | |
| MCDataDecoder::MCDataDecoder() | |
| { | |
| mMimeType = nullptr; | |
| } | |
| MCDataDecoder::MCDataDecoder(MediaData::Type aType, const char *aMimeType, | |
| mcdecode::Format *aFormat, MediaDataDecoderCallback *aCallback, | |
| MediaTaskQueue* aTaskQueue) | |
| : mCallback(aCallback) | |
| , mMimeType(aMimeType) | |
| , mFormat(aFormat) | |
| , mTaskQueue(aTaskQueue) | |
| , mType(aType) | |
| { | |
| } | |
| MCDataDecoder::~MCDataDecoder(){} | |
| // 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 or PlatformDecoderModule::Create*Decoder(), | |
| // so that if the MP4Reader needs to shutdown during initialization, | |
| // it can call Shutdown() to cancel this operation. Any initialization | |
| // that requires blocking the calling thread in this function *must* | |
| // be done here so that it can be canceled by calling Shutdown()! | |
| nsresult MCDataDecoder::Init() | |
| { | |
| //TODO: Another part of a workaround for the not-yet-finished binding tool. | |
| MCByteBuffer::MCInit(GetJNIForThread()); | |
| mJEnv = GetJNIForThread(); | |
| mDecoder = AndroidDecoderModule::GetDecoder(mMimeType); | |
| if(HasDecoder()!=NS_OK) | |
| return HasDecoder(); | |
| mDecoder->Configure(mFormat->wrappedObject(), nullptr, nullptr, 0); | |
| mDecoder->Start(); | |
| mInputBuffers = mDecoder->GetInputBuffers(); | |
| mOutputBuffers = mDecoder->GetOutputBuffers(); | |
| printf_stderr("Media Codec: For future reference, the Input buffer is %p and the Output buffer is %p.\n", mInputBuffers, mOutputBuffers); | |
| return NS_OK; | |
| } | |
| // Inserts a sample into the decoder's decode pipeline. The decoder must | |
| // delete the sample once its been decoded. If Input() returns an error, | |
| // aSample will be deleted by the caller. | |
| nsresult MCDataDecoder::Input(mp4_demuxer::MP4Sample* aSample){ | |
| printf_stderr("Media Codec: Taking input for media of type %s.\n", mMimeType); | |
| RefPtr<nsIRunnable> r(new AndroidInputLoader(mInputBuffers, mOutputBuffers, | |
| aSample, mDecoder,mCallback, | |
| mType)); | |
| mTaskQueue->Dispatch(r); | |
| // AutoPtr queues for us. | |
| return NS_OK; | |
| } | |
| // Causes all samples in the decoding pipeline to be discarded. When | |
| // this function returns, the decoder must be ready to accept new input | |
| // for decoding. This function is called when the demuxer seeks, before | |
| // decoding resumes after the seek. | |
| // While the reader calls Flush(), it ignores all output sent to it; | |
| // it is safe (but pointless) to send output while Flush is called. | |
| // The MP4Reader will not call Input() while it's calling Flush(). | |
| nsresult MCDataDecoder::Flush(){ | |
| //mDecoder->Flush(); | |
| return NS_OK; | |
| } | |
| // Causes all complete samples in the pipeline that can be decoded to be | |
| // output. If the decoder can't produce samples from the current output, | |
| // it drops the input samples. The decoder may be holding onto samples | |
| // that are required to decode samples that it expects to get in future. | |
| // This is called when the demuxer reaches end of stream. | |
| // The MP4Reader will not call Input() while it's calling Drain(). | |
| nsresult MCDataDecoder::Drain(){ | |
| //mDecoder->Flush(); | |
| return NS_OK; | |
| } | |
| // Cancels all init/input/drain operations, and shuts down the | |
| // decoder. The platform decoder should clean up any resources it's using | |
| // and release memory etc. Shutdown() must block until the decoder has | |
| // completed shutdown. The reader calls Flush() before calling Shutdown(). | |
| // The reader will delete the decoder once Shutdown() returns. | |
| // The MediaDataDecoderCallback *must* not be called after Shutdown() has | |
| // returned. | |
| nsresult MCDataDecoder::Shutdown(){ | |
| //mDecoder->Stop(); | |
| //mDecoder->Release(); | |
| return NS_OK; | |
| } | |
| } // namespace mozilla |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment