Skip to content

Instantly share code, notes, and snippets.

@FlyingJester
Created June 18, 2014 20:59
Show Gist options
  • Select an option

  • Save FlyingJester/ce062ac376d1577724ab to your computer and use it in GitHub Desktop.

Select an option

Save FlyingJester/ce062ac376d1577724ab to your computer and use it in GitHub Desktop.
/* 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