Bug 1014614 - Use Android MediaCodec for decoding H264 and AAC in MP4 r=cpearce,edwin,snorp

--HG--
extra : rebase_source : 5f104f687b6a68d4d4100abf8a8a28039fbb324d
extra : histedit_source : 6f609ebacd51fffcdcfec92c99329b758c6fdbce
This commit is contained in:
Andrew Martin McDonough 2014-10-17 10:35:12 -05:00
parent 01348c1f39
commit d1679cdb9c
13 changed files with 2858 additions and 2 deletions

View File

@ -5301,8 +5301,15 @@ fi;
dnl ======================================================== dnl ========================================================
dnl = Built-in fragmented MP4 support. dnl = Built-in fragmented MP4 support.
dnl ======================================================== dnl ========================================================
if test "$OS_TARGET" = Android; then
MOZ_FMP4=1
fi
if test -n "$MOZ_WMF" -o -n "$MOZ_FFMPEG" -o -n "$MOZ_APPLEMEDIA"; then if test -n "$MOZ_WMF" -o -n "$MOZ_FFMPEG" -o -n "$MOZ_APPLEMEDIA"; then
dnl Enable fragmented MP4 parser on platforms with decoder support. dnl Enable fragmented MP4 parser on Windows by default.
dnl We will also need to enable it on other platforms as we implement
dnl platform decoder support there too.
MOZ_FMP4=1 MOZ_FMP4=1
fi fi

View File

@ -23,6 +23,9 @@
#ifdef MOZ_APPLEMEDIA #ifdef MOZ_APPLEMEDIA
#include "apple/AppleDecoderModule.h" #include "apple/AppleDecoderModule.h"
#endif #endif
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
namespace mozilla { namespace mozilla {
@ -167,6 +170,10 @@ HavePlatformMPEGDecoders()
#ifdef XP_WIN #ifdef XP_WIN
// We have H.264/AAC platform decoders on Windows Vista and up. // We have H.264/AAC platform decoders on Windows Vista and up.
IsVistaOrLater() || IsVistaOrLater() ||
#endif
#ifdef MOZ_WIDGET_ANDROID
// We need android.media.MediaCodec which exists in API level 16 and higher.
(AndroidBridge::Bridge()->GetAPIVersion() >= 16) ||
#endif #endif
IsFFmpegAvailable() || IsFFmpegAvailable() ||
IsAppleAvailable() || IsAppleAvailable() ||

View File

@ -17,6 +17,9 @@
#ifdef MOZ_GONK_MEDIACODEC #ifdef MOZ_GONK_MEDIACODEC
#include "GonkDecoderModule.h" #include "GonkDecoderModule.h"
#endif #endif
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidDecoderModule.h"
#endif
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#ifdef MOZ_EME #ifdef MOZ_EME
@ -33,6 +36,8 @@ extern PlatformDecoderModule* CreateBlankDecoderModule();
bool PlatformDecoderModule::sUseBlankDecoder = false; bool PlatformDecoderModule::sUseBlankDecoder = false;
bool PlatformDecoderModule::sFFmpegDecoderEnabled = false; bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
bool PlatformDecoderModule::sGonkDecoderEnabled = false; bool PlatformDecoderModule::sGonkDecoderEnabled = false;
bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
bool PlatformDecoderModule::sAndroidMCDecoderPreferred = false;
/* static */ /* static */
void void
@ -49,10 +54,18 @@ PlatformDecoderModule::Init()
"media.fragmented-mp4.use-blank-decoder"); "media.fragmented-mp4.use-blank-decoder");
Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled, Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
"media.fragmented-mp4.ffmpeg.enabled", false); "media.fragmented-mp4.ffmpeg.enabled", false);
#ifdef MOZ_GONK_MEDIACODEC #ifdef MOZ_GONK_MEDIACODEC
Preferences::AddBoolVarCache(&sGonkDecoderEnabled, Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
"media.fragmented-mp4.gonk.enabled", false); "media.fragmented-mp4.gonk.enabled", false);
#endif #endif
#ifdef MOZ_WIDGET_ANDROID
Preferences::AddBoolVarCache(&sAndroidMCDecoderEnabled,
"media.fragmented-mp4.android-media-codec.enabled", false);
Preferences::AddBoolVarCache(&sAndroidMCDecoderPreferred,
"media.fragmented-mp4.android-media-codec.preferred", false);
#endif
#ifdef XP_WIN #ifdef XP_WIN
WMFDecoderModule::Init(); WMFDecoderModule::Init();
#endif #endif
@ -122,6 +135,11 @@ PlatformDecoderModule::Create()
// Note: This runs on the decode thread. // Note: This runs on the decode thread.
MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!NS_IsMainThread());
#ifdef MOZ_WIDGET_ANDROID
if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled){
return new AndroidDecoderModule();
}
#endif
if (sUseBlankDecoder) { if (sUseBlankDecoder) {
return CreateBlankDecoderModule(); return CreateBlankDecoderModule();
} }
@ -149,6 +167,11 @@ PlatformDecoderModule::Create()
if (sGonkDecoderEnabled) { if (sGonkDecoderEnabled) {
return new GonkDecoderModule(); return new GonkDecoderModule();
} }
#endif
#ifdef MOZ_WIDGET_ANDROID
if(sAndroidMCDecoderEnabled){
return new AndroidDecoderModule();
}
#endif #endif
return nullptr; return nullptr;
} }

View File

@ -131,6 +131,8 @@ protected:
static bool sUseBlankDecoder; static bool sUseBlankDecoder;
static bool sFFmpegDecoderEnabled; static bool sFFmpegDecoderEnabled;
static bool sGonkDecoderEnabled; static bool sGonkDecoderEnabled;
static bool sAndroidMCDecoderPreferred;
static bool sAndroidMCDecoderEnabled;
}; };
// A callback used by MediaDataDecoder to return output/errors to the // A callback used by MediaDataDecoder to return output/errors to the

View File

@ -0,0 +1,494 @@
/* 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 "PlatformDecoderModule.h"
#include "GeneratedJNIWrappers.h"
#include "GeneratedSDKWrappers.h"
#include "AndroidBridge.h"
#include "MediaTaskQueue.h"
#include "SharedThreadPool.h"
#include "TexturePoolOGL.h"
#include "GLImages.h"
#include "MediaData.h"
#include "mp4_demuxer/AnnexB.h"
#include "mp4_demuxer/DecoderData.h"
#include "nsThreadUtils.h"
#include "nsAutoPtr.h"
#include <jni.h>
using namespace mozilla;
using namespace mozilla::gl;
using namespace mozilla::widget::android;
static MediaCodec* CreateDecoder(JNIEnv* aEnv, const char* aMimeType)
{
if (!aMimeType) {
return nullptr;
}
nsAutoString mimeType;
mimeType.AssignASCII(aMimeType);
jobject decoder = MediaCodec::CreateDecoderByType(mimeType);
return new MediaCodec(decoder, aEnv);
}
class VideoDataDecoder : public MediaCodecDataDecoder {
public:
VideoDataDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
MediaFormat* aFormat, MediaDataDecoderCallback* aCallback,
layers::ImageContainer* aImageContainer)
: MediaCodecDataDecoder(MediaData::Type::VIDEO_FRAME, aConfig.mime_type, aFormat, aCallback)
, mImageContainer(aImageContainer)
, mConfig(aConfig)
{
}
nsresult Init() MOZ_OVERRIDE {
mSurfaceTexture = AndroidSurfaceTexture::Create();
if (!mSurfaceTexture) {
printf_stderr("Failed to create SurfaceTexture for video decode\n");
return NS_ERROR_FAILURE;
}
return InitDecoder(mSurfaceTexture->JavaSurface());
}
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
mp4_demuxer::AnnexB::ConvertSample(aSample, mConfig.annex_b);
return MediaCodecDataDecoder::Input(aSample);
}
virtual nsresult PostOutput(BufferInfo* aInfo, Microseconds aDuration) MOZ_OVERRIDE {
VideoInfo videoInfo;
videoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
bool isSync = false;
if (MediaCodec::getBUFFER_FLAG_SYNC_FRAME() & aInfo->getFlags()) {
isSync = true;
}
nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
layers::SurfaceTextureImage::Data data;
data.mSurfTex = mSurfaceTexture.get();
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
data.mInverted = true;
layers::SurfaceTextureImage* typedImg = static_cast<layers::SurfaceTextureImage*>(img.get());
typedImg->SetData(data);
mCallback->Output(VideoData::CreateFromImage(videoInfo, mImageContainer, aInfo->getOffset(),
aInfo->getPresentationTimeUs(),
aDuration,
img, isSync,
aInfo->getPresentationTimeUs(),
gfx::IntRect(0, 0,
mConfig.display_width,
mConfig.display_height)));
return NS_OK;
}
protected:
layers::ImageContainer* mImageContainer;
const mp4_demuxer::VideoDecoderConfig& mConfig;
nsRefPtr<AndroidSurfaceTexture> mSurfaceTexture;
};
class AudioDataDecoder : public MediaCodecDataDecoder {
public:
AudioDataDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaFormat* aFormat, MediaDataDecoderCallback* aCallback)
: MediaCodecDataDecoder(MediaData::Type::AUDIO_SAMPLES, aConfig.mime_type, aFormat, aCallback)
, mConfig(aConfig)
{
MOZ_ASSERT(mConfig.bits_per_sample == 16, "We only support 16-bit audio");
}
nsresult Output(BufferInfo* aInfo, void* aBuffer, Microseconds aDuration) {
// The output on Android is always 16-bit signed
uint32_t numChannels = mConfig.channel_count;
uint32_t numFrames = (aInfo->getSize() / numChannels) / 2;
AudioDataValue* audio = new AudioDataValue[aInfo->getSize()];
PodCopy(audio, static_cast<AudioDataValue*>(aBuffer), aInfo->getSize());
mCallback->Output(new AudioData(aInfo->getOffset(), aInfo->getPresentationTimeUs(),
aDuration,
numFrames,
audio,
numChannels,
mConfig.samples_per_second));
return NS_OK;
}
protected:
const mp4_demuxer::AudioDecoderConfig& mConfig;
};
bool AndroidDecoderModule::SupportsAudioMimeType(const char* aMimeType) {
JNIEnv* env = GetJNIForThread();
MediaCodec* decoder = CreateDecoder(env, aMimeType);
bool supports = (decoder != nullptr);
delete decoder;
return supports;
}
already_AddRefed<MediaDataDecoder>
AndroidDecoderModule::CreateH264Decoder(
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsAutoString mimeType;
mimeType.AssignASCII(aConfig.mime_type);
jobject jFormat = MediaFormat::CreateVideoFormat(mimeType,
aConfig.display_width,
aConfig.display_height);
if (!jFormat) {
return nullptr;
}
MediaFormat* format = MediaFormat::Wrap(jFormat);
if (!format) {
return nullptr;
}
nsRefPtr<MediaDataDecoder> decoder =
new VideoDataDecoder(aConfig, format, aCallback, aImageContainer);
return decoder.forget();
}
already_AddRefed<MediaDataDecoder>
AndroidDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback)
{
nsAutoString mimeType;
mimeType.AssignASCII(aConfig.mime_type);
jobject jFormat = MediaFormat::CreateAudioFormat(mimeType,
aConfig.samples_per_second,
aConfig.channel_count);
if (jFormat == nullptr)
return nullptr;
MediaFormat* format = MediaFormat::Wrap(jFormat);
if(format == nullptr)
return nullptr;
JNIEnv* env = GetJNIForThread();
if (!format->GetByteBuffer(NS_LITERAL_STRING("csd-0"))) {
uint8_t* csd0 = new uint8_t[2];
csd0[0] = aConfig.audio_specific_config[0];
csd0[1] = aConfig.audio_specific_config[1];
jobject buffer = env->NewDirectByteBuffer(csd0, 2);
format->SetByteBuffer(NS_LITERAL_STRING("csd-0"), buffer);
env->DeleteLocalRef(buffer);
}
if (mimeType.EqualsLiteral("audio/mp4a-latm")) {
format->SetInteger(NS_LITERAL_STRING("is-adts"), 1);
}
nsRefPtr<MediaDataDecoder> decoder =
new AudioDataDecoder(aConfig, format, aCallback);
return decoder.forget();
}
nsresult AndroidDecoderModule::Shutdown()
{
return NS_OK;
}
MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
const char* aMimeType,
MediaFormat* aFormat,
MediaDataDecoderCallback* aCallback)
: mType(aType)
, mMimeType(strdup(aMimeType))
, mFormat(aFormat)
, mCallback(aCallback)
, mInputBuffers(nullptr)
, mOutputBuffers(nullptr)
, mMonitor("MediaCodecDataDecoder::mMonitor")
, mDraining(false)
, mStopping(false)
{
}
MediaCodecDataDecoder::~MediaCodecDataDecoder()
{
JNIEnv* env = GetJNIForThread();
Shutdown();
if (mInputBuffers) {
env->DeleteGlobalRef(mInputBuffers);
mInputBuffers = nullptr;
}
if (mOutputBuffers) {
env->DeleteGlobalRef(mOutputBuffers);
mOutputBuffers = nullptr;
}
}
nsresult MediaCodecDataDecoder::Init()
{
return InitDecoder();
}
nsresult MediaCodecDataDecoder::InitDecoder(jobject aSurface)
{
JNIEnv* env = GetJNIForThread();
mDecoder = CreateDecoder(env, mMimeType);
if (!mDecoder) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
mDecoder->Configure(mFormat->wrappedObject(), aSurface, nullptr, 0);
mDecoder->Start();
ResetInputBuffers();
ResetOutputBuffers();
NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread),
NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop));
return NS_OK;
}
// This is in usec, so that's 10ms
#define DECODER_TIMEOUT 10000
void MediaCodecDataDecoder::DecoderLoop()
{
bool outputDone = false;
JNIEnv* env = GetJNIForThread();
mp4_demuxer::MP4Sample* sample = nullptr;
for (;;) {
{
MonitorAutoLock lock(mMonitor);
while (!mStopping && !mDraining && mQueue.empty()) {
if (mQueue.empty()) {
// We could be waiting here forever if we don't signal that we need more input
mCallback->InputExhausted();
}
lock.Wait();
}
if (mStopping) {
// Get out of the loop. This is the only exit point.
break;
}
if (mDraining) {
mDecoder->Flush();
ClearQueue();
mDraining = false;
lock.Notify();
continue;
}
// We're not stopping or draining, so try to get a sample
if (!mQueue.empty()) {
sample = mQueue.front();
}
}
if (sample) {
// We have a sample, try to feed it to the decoder
int inputIndex = mDecoder->DequeueInputBuffer(DECODER_TIMEOUT);
if (inputIndex >= 0) {
jobject buffer = env->GetObjectArrayElement(mInputBuffers, inputIndex);
void* directBuffer = env->GetDirectBufferAddress(buffer);
// We're feeding this to the decoder, so remove it from the queue
mMonitor.Lock();
mQueue.pop();
mMonitor.Unlock();
MOZ_ASSERT(env->GetDirectBufferCapacity(buffer) >= sample->size,
"Decoder buffer is not large enough for sample");
PodCopy((uint8_t*)directBuffer, sample->data, sample->size);
mDecoder->QueueInputBuffer(inputIndex, 0, sample->size, sample->composition_timestamp, 0);
mDurations.push(sample->duration);
delete sample;
sample = nullptr;
outputDone = false;
env->DeleteLocalRef(buffer);
}
}
if (!outputDone) {
BufferInfo bufferInfo;
int outputStatus = mDecoder->DequeueOutputBuffer(bufferInfo.wrappedObject(), DECODER_TIMEOUT);
if (outputStatus == MediaCodec::getINFO_TRY_AGAIN_LATER()) {
// We might want to call mCallback->InputExhausted() here, but there seems to be
// some possible bad interactions here with the threading
} else if (outputStatus == MediaCodec::getINFO_OUTPUT_BUFFERS_CHANGED()) {
ResetOutputBuffers();
} else if (outputStatus == MediaCodec::getINFO_OUTPUT_FORMAT_CHANGED()) {
// Don't care, we use SurfaceTexture for video
} else if (outputStatus < 0) {
printf_stderr("unknown error from decoder! %d\n", outputStatus);
mCallback->Error();
} else {
// We have a valid buffer index >= 0 here
if (bufferInfo.getFlags() & MediaCodec::getBUFFER_FLAG_END_OF_STREAM()) {
outputDone = true;
}
MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued");
Microseconds duration = 0;
if (!mDurations.empty()) {
duration = mDurations.front();
mDurations.pop();
}
jobject buffer = env->GetObjectArrayElement(mOutputBuffers, outputStatus);
if (buffer) {
// The buffer will be null on Android L if we are decoding to a Surface
void* directBuffer = env->GetDirectBufferAddress(buffer);
Output(&bufferInfo, directBuffer, duration);
}
// The Surface will be updated at this point (for video)
mDecoder->ReleaseOutputBuffer(outputStatus, true);
PostOutput(&bufferInfo, duration);
if (buffer) {
env->DeleteLocalRef(buffer);
}
}
}
}
// We're done
mMonitor.Lock();
mStopping = false;
mMonitor.Notify();
mMonitor.Unlock();
}
void MediaCodecDataDecoder::ClearQueue()
{
mMonitor.AssertCurrentThreadOwns();
while (!mQueue.empty()) {
delete mQueue.front();
mQueue.pop();
}
while (!mDurations.empty()) {
mDurations.pop();
}
}
nsresult MediaCodecDataDecoder::Input(mp4_demuxer::MP4Sample* aSample) {
MonitorAutoLock lock(mMonitor);
mQueue.push(aSample);
lock.NotifyAll();
return NS_OK;
}
void MediaCodecDataDecoder::ResetInputBuffers()
{
JNIEnv* env = GetJNIForThread();
if (mInputBuffers) {
env->DeleteGlobalRef(mInputBuffers);
}
mInputBuffers = (jobjectArray) env->NewGlobalRef(mDecoder->GetInputBuffers());
}
void MediaCodecDataDecoder::ResetOutputBuffers()
{
JNIEnv* env = GetJNIForThread();
if (mOutputBuffers) {
env->DeleteGlobalRef(mOutputBuffers);
}
mOutputBuffers = (jobjectArray) env->NewGlobalRef(mDecoder->GetOutputBuffers());
}
nsresult MediaCodecDataDecoder::Flush() {
Drain();
return NS_OK;
}
nsresult MediaCodecDataDecoder::Drain() {
MonitorAutoLock lock(mMonitor);
mDraining = true;
lock.Notify();
while (mDraining) {
lock.Wait();
}
mCallback->DrainComplete();
return NS_OK;
}
nsresult MediaCodecDataDecoder::Shutdown() {
MonitorAutoLock lock(mMonitor);
if (!mThread || mStopping) {
// Already shutdown or in the process of doing so
return NS_OK;
}
mStopping = true;
lock.Notify();
while (mStopping) {
lock.Wait();
}
mThread->Shutdown();
mThread = nullptr;
mDecoder->Stop();
mDecoder->Release();
return NS_OK;
}

View File

@ -0,0 +1,109 @@
/* 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/. */
#ifndef AndroidDecoderModule_h_
#define AndroidDecoderModule_h_
#include "PlatformDecoderModule.h"
#include "AndroidJavaWrappers.h"
#include "AndroidSurfaceTexture.h"
#include "GeneratedSDKWrappers.h"
#include "mozilla/Monitor.h"
#include <queue>
namespace mozilla {
typedef std::queue<mp4_demuxer::MP4Sample*> SampleQueue;
namespace widget {
namespace android {
class MediaCodec;
class MediaFormat;
class ByteBuffer;
}
}
class MediaCodecDataDecoder;
class AndroidDecoderModule : public PlatformDecoderModule {
public:
virtual nsresult Shutdown() MOZ_OVERRIDE;
virtual already_AddRefed<MediaDataDecoder>
CreateH264Decoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
virtual already_AddRefed<MediaDataDecoder>
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
MediaTaskQueue* aAudioTaskQueue,
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
AndroidDecoderModule() {}
virtual ~AndroidDecoderModule() {}
virtual bool SupportsAudioMimeType(const char* aMimeType) MOZ_OVERRIDE;
};
class MediaCodecDataDecoder : public MediaDataDecoder {
public:
MediaCodecDataDecoder(MediaData::Type aType,
const char* aMimeType,
mozilla::widget::android::MediaFormat* aFormat,
MediaDataDecoderCallback* aCallback);
virtual ~MediaCodecDataDecoder();
virtual nsresult Init() MOZ_OVERRIDE;
virtual nsresult Flush() MOZ_OVERRIDE;
virtual nsresult Drain() MOZ_OVERRIDE;
virtual nsresult Shutdown() MOZ_OVERRIDE;
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
protected:
friend class AndroidDecoderModule;
MediaData::Type mType;
nsAutoPtr<char> mMimeType;
nsAutoPtr<mozilla::widget::android::MediaFormat> mFormat;
MediaDataDecoderCallback* mCallback;
nsAutoPtr<mozilla::widget::android::MediaCodec> mDecoder;
jobjectArray mInputBuffers;
jobjectArray mOutputBuffers;
nsCOMPtr<nsIThread> mThread;
// Only these members are protected by mMonitor.
Monitor mMonitor;
bool mDraining;
bool mStopping;
SampleQueue mQueue;
std::queue<Microseconds> mDurations;
virtual nsresult InitDecoder(jobject aSurface = nullptr);
virtual nsresult Output(mozilla::widget::android::BufferInfo* aInfo, void* aBuffer, Microseconds aDuration) { return NS_OK; }
virtual nsresult PostOutput(mozilla::widget::android::BufferInfo* aInfo, Microseconds aDuration) { return NS_OK; }
void ResetInputBuffers();
void ResetOutputBuffers();
void DecoderLoop();
virtual void ClearQueue();
};
} // namwspace mozilla
#endif

View File

@ -25,7 +25,7 @@ if CONFIG['MOZ_WMF']:
if CONFIG['MOZ_EME']: if CONFIG['MOZ_EME']:
DIRS += ['eme'] DIRS += ['eme']
if CONFIG['MOZ_FFMPEG']: if CONFIG['MOZ_FFMPEG']:
EXPORTS += [ EXPORTS += [
'ffmpeg/FFmpegRuntimeLinker.h', 'ffmpeg/FFmpegRuntimeLinker.h',
@ -67,6 +67,14 @@ if CONFIG['ANDROID_VERSION'] >= '18'and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
include('/ipc/chromium/chromium-config.mozbuild') include('/ipc/chromium/chromium-config.mozbuild')
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
EXPORTS += [
'android/AndroidDecoderModule.h',
]
UNIFIED_SOURCES += [
'android/AndroidDecoderModule.cpp',
]
FINAL_LIBRARY = 'xul' FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True FAIL_ON_WARNINGS = True

View File

@ -560,6 +560,12 @@ pref("media.preload.auto", 2); // preload metadata if preload=auto
// of at least 4. // of at least 4.
pref("media.video-queue.default-size", 3); pref("media.video-queue.default-size", 3);
// Enable the MediaCodec PlatformDecoderModule by default.
pref("media.fragmented-mp4.exposed", true);
pref("media.fragmented-mp4.enabled", true);
pref("media.fragmented-mp4.android-media-codec.enabled", true);
pref("media.fragmented-mp4.android-media-codec.preferred", true);
// optimize images memory usage // optimize images memory usage
pref("image.mem.decodeondraw", true); pref("image.mem.decodeondraw", true);
pref("image.mem.min_discard_timeout_ms", 10000); pref("image.mem.min_discard_timeout_ms", 10000);

View File

@ -41,6 +41,7 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
#include "nsIHttpChannel.h" #include "nsIHttpChannel.h"
#include "GeneratedSDKWrappers.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::widget::android; using namespace mozilla::widget::android;
@ -220,6 +221,11 @@ AndroidBridge::Init(JNIEnv *jEnv)
InitAndroidJavaWrappers(jEnv); InitAndroidJavaWrappers(jEnv);
if (mAPIVersion >= 16 /* Jelly Bean */) {
// We only use this for MediaCodec right now
InitSDKStubs(jEnv);
}
// jEnv should NOT be cached here by anything -- the jEnv here // jEnv should NOT be cached here by anything -- the jEnv here
// is not valid for the real gecko main thread, which is set // is not valid for the real gecko main thread, which is set
// at SetMainThread time. // at SetMainThread time.

View File

@ -11,6 +11,7 @@
#include "mozilla/BasicEvents.h" #include "mozilla/BasicEvents.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "mozilla/TouchEvents.h" #include "mozilla/TouchEvents.h"
#include "GeneratedSDKWrappers.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,332 @@
// GENERATED CODE
// NOTE: This code has been doctored. The JarClassProcessor is still a work in progress,
// and so additions and deletions have been made to make this file valid.
// Generated by the Java program at /build/jarClassProcessors at compile time from
// a given set of jars and a set of requested methods. To update, change the annotations
// on the corresponding Java methods and rerun the build. Manually updating this file
// will cause your build to fail.
#ifndef GeneratedSDKWrappers_h__
#define GeneratedSDKWrappers_h__
#include "nsXPCOMStrings.h"
#include "AndroidJavaWrappers.h"
namespace mozilla {
namespace widget {
namespace android {
void InitSDKStubs(JNIEnv *jEnv);
class MediaCodec : public AutoGlobalWrappedJavaObject {
public:
static void InitStubs(JNIEnv *jEnv);
static MediaCodec* Wrap(jobject obj);
MediaCodec(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
void Configure(jobject a0, jobject a1, jobject a2, int32_t a3);
static jobject CreateByCodecName(const nsAString& a0);
static jobject CreateDecoderByType(const nsAString& a0);
static jobject CreateEncoderByType(const nsAString& a0);
int32_t DequeueInputBuffer(int64_t a0);
int32_t DequeueOutputBuffer(jobject a0, int64_t a1);
void Finalize();
void Flush();
jobjectArray GetInputBuffers();
jobjectArray GetOutputBuffers();
jobject GetOutputFormat();
void QueueInputBuffer(int32_t a0, int32_t a1, int32_t a2, int64_t a3, int32_t a4);
void QueueSecureInputBuffer(int32_t a0, int32_t a1, jobject a2, int64_t a3, int32_t a4);
void Release();
void ReleaseOutputBuffer(int32_t a0, bool a1);
void SetVideoScalingMode(int32_t a0);
void Start();
void Stop();
static int32_t getBUFFER_FLAG_CODEC_CONFIG();
static int32_t getBUFFER_FLAG_END_OF_STREAM();
static int32_t getBUFFER_FLAG_SYNC_FRAME();
static int32_t getCONFIGURE_FLAG_ENCODE();
static int32_t getCRYPTO_MODE_AES_CTR();
static int32_t getCRYPTO_MODE_UNENCRYPTED();
static int32_t getINFO_OUTPUT_BUFFERS_CHANGED();
static int32_t getINFO_OUTPUT_FORMAT_CHANGED();
static int32_t getINFO_TRY_AGAIN_LATER();
static int32_t getVIDEO_SCALING_MODE_SCALE_TO_FIT();
static int32_t getVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING();
MediaCodec() : AutoGlobalWrappedJavaObject() {};
protected:
static jclass mMediaCodecClass;
static jmethodID jConfigure;
static jmethodID jCreateByCodecName;
static jmethodID jCreateDecoderByType;
static jmethodID jCreateEncoderByType;
static jmethodID jDequeueInputBuffer;
static jmethodID jDequeueOutputBuffer;
static jmethodID jFinalize;
static jmethodID jFlush;
static jmethodID jGetInputBuffers;
static jmethodID jGetOutputBuffers;
static jmethodID jGetOutputFormat;
static jmethodID jQueueInputBuffer;
static jmethodID jQueueSecureInputBuffer;
static jmethodID jRelease;
static jmethodID jReleaseOutputBuffer;
static jmethodID jSetVideoScalingMode;
static jmethodID jStart;
static jmethodID jStop;
static jfieldID jBUFFER_FLAG_CODEC_CONFIG;
static jfieldID jBUFFER_FLAG_END_OF_STREAM;
static jfieldID jBUFFER_FLAG_SYNC_FRAME;
static jfieldID jCONFIGURE_FLAG_ENCODE;
static jfieldID jCRYPTO_MODE_AES_CTR;
static jfieldID jCRYPTO_MODE_UNENCRYPTED;
static jfieldID jINFO_OUTPUT_BUFFERS_CHANGED;
static jfieldID jINFO_OUTPUT_FORMAT_CHANGED;
static jfieldID jINFO_TRY_AGAIN_LATER;
static jfieldID jVIDEO_SCALING_MODE_SCALE_TO_FIT;
static jfieldID jVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
};
class MediaFormat : public AutoGlobalWrappedJavaObject {
public:
static void InitStubs(JNIEnv *jEnv);
static MediaFormat* Wrap(jobject obj);
MediaFormat(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
MediaFormat();
bool ContainsKey(const nsAString& a0);
static jobject CreateAudioFormat(const nsAString& a0, int32_t a1, int32_t a2);
static jobject CreateVideoFormat(const nsAString& a0, int32_t a1, int32_t a2);
jobject GetByteBuffer(const nsAString& a0);
jfloat GetFloat(const nsAString& a0);
int32_t GetInteger(const nsAString& a0);
int64_t GetLong(const nsAString& a0);
jstring GetString(const nsAString& a0);
void SetByteBuffer(const nsAString& a0, jobject a1);
void SetFloat(const nsAString& a0, jfloat a1);
void SetInteger(const nsAString& a0, int32_t a1);
void SetLong(const nsAString& a0, int64_t a1);
void SetString(const nsAString& a0, const nsAString& a1);
jstring ToString();
static jstring getKEY_AAC_PROFILE();
static jstring getKEY_BIT_RATE();
static jstring getKEY_CHANNEL_COUNT();
static jstring getKEY_CHANNEL_MASK();
static jstring getKEY_COLOR_FORMAT();
static jstring getKEY_DURATION();
static jstring getKEY_FLAC_COMPRESSION_LEVEL();
static jstring getKEY_FRAME_RATE();
static jstring getKEY_HEIGHT();
static jstring getKEY_IS_ADTS();
static jstring getKEY_I_FRAME_INTERVAL();
static jstring getKEY_MAX_INPUT_SIZE();
static jstring getKEY_MIME();
static jstring getKEY_SAMPLE_RATE();
static jstring getKEY_WIDTH();
protected:
static jclass mMediaFormatClass;
static jmethodID jMediaFormat;
static jmethodID jContainsKey;
static jmethodID jCreateAudioFormat;
static jmethodID jCreateVideoFormat;
static jmethodID jGetByteBuffer;
static jmethodID jGetFloat;
static jmethodID jGetInteger;
static jmethodID jGetLong;
static jmethodID jGetString;
static jmethodID jSetByteBuffer;
static jmethodID jSetFloat;
static jmethodID jSetInteger;
static jmethodID jSetLong;
static jmethodID jSetString;
static jmethodID jToString;
static jfieldID jKEY_AAC_PROFILE;
static jfieldID jKEY_BIT_RATE;
static jfieldID jKEY_CHANNEL_COUNT;
static jfieldID jKEY_CHANNEL_MASK;
static jfieldID jKEY_COLOR_FORMAT;
static jfieldID jKEY_DURATION;
static jfieldID jKEY_FLAC_COMPRESSION_LEVEL;
static jfieldID jKEY_FRAME_RATE;
static jfieldID jKEY_HEIGHT;
static jfieldID jKEY_IS_ADTS;
static jfieldID jKEY_I_FRAME_INTERVAL;
static jfieldID jKEY_MAX_INPUT_SIZE;
static jfieldID jKEY_MIME;
static jfieldID jKEY_SAMPLE_RATE;
static jfieldID jKEY_WIDTH;
};
class ByteBuffer : public AutoGlobalWrappedJavaObject {
public:
static void InitStubs(JNIEnv *jEnv);
static ByteBuffer* Wrap(jobject obj);
ByteBuffer(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
int8_t _get(int32_t a0);
void _put(int32_t a0, int8_t a1);
static jobject Allocate(int32_t a0);
static jobject AllocateDirect(int32_t a0);
jobject Array();
jbyteArray Array1();
int32_t ArrayOffset();
jstring AsCharBuffer();
jobject AsDoubleBuffer();
jobject AsFloatBuffer();
jobject AsIntBuffer();
jobject AsLongBuffer();
jobject AsReadOnlyBuffer();
jobject AsShortBuffer();
jobject Compact();
int32_t CompareTo(jobject a0);
int32_t CompareTo1(jobject a0);
jobject Duplicate();
bool Equals(jobject a0);
int8_t Get();
int8_t Get1(int32_t a0);
jobject Get1(jbyteArray a0);
jobject Get1(jbyteArray a0, int32_t a1, int32_t a2);
uint16_t GetChar();
uint16_t GetChar1(int32_t a0);
jdouble GetDouble();
jdouble GetDouble1(int32_t a0);
jfloat GetFloat();
jfloat GetFloat1(int32_t a0);
int32_t GetInt();
int32_t GetInt1(int32_t a0);
int64_t GetLong();
int64_t GetLong1(int32_t a0);
int16_t GetShort();
int16_t GetShort1(int32_t a0);
bool HasArray();
int32_t HashCode();
bool IsDirect();
jobject Order();
jobject Order1(jobject a0);
jobject Put(int8_t a0);
jobject Put1(int32_t a0, int8_t a1);
jobject Put1(jobject a0);
jobject Put1(jbyteArray a0);
jobject Put1(jbyteArray a0, int32_t a1, int32_t a2);
jobject PutChar(uint16_t a0);
jobject PutChar1(int32_t a0, uint16_t a1);
jobject PutDouble(jdouble a0);
jobject PutDouble1(int32_t a0, jdouble a1);
jobject PutFloat(jfloat a0);
jobject PutFloat1(int32_t a0, jfloat a1);
jobject PutInt(int32_t a0);
jobject PutInt1(int32_t a0, int32_t a1);
jobject PutLong(int32_t a0, int64_t a1);
jobject PutLong1(int64_t a0);
jobject PutShort(int32_t a0, int16_t a1);
jobject PutShort1(int16_t a0);
jobject Slice();
jstring ToString();
static jobject Wrap1(jbyteArray a0);
static jobject Wrap2(jbyteArray a0, int32_t a1, int32_t a2);
bool getBigEndian();
void setBigEndian(bool a0);
jbyteArray getHb();
bool getIsReadOnly();
void setIsReadOnly(bool a0);
bool getNativeByteOrder();
void setNativeByteOrder(bool a0);
int32_t getOffset();
ByteBuffer() : AutoGlobalWrappedJavaObject() {};
protected:
static jclass mByteBufferClass;
static jmethodID j_get;
static jmethodID j_put;
static jmethodID jAllocate;
static jmethodID jAllocateDirect;
static jmethodID jArray;
static jmethodID jArray1;
static jmethodID jArrayOffset;
static jmethodID jAsCharBuffer;
static jmethodID jAsDoubleBuffer;
static jmethodID jAsFloatBuffer;
static jmethodID jAsIntBuffer;
static jmethodID jAsLongBuffer;
static jmethodID jAsReadOnlyBuffer;
static jmethodID jAsShortBuffer;
static jmethodID jCompact;
static jmethodID jCompareTo;
static jmethodID jCompareTo1;
static jmethodID jDuplicate;
static jmethodID jEquals;
static jmethodID jGet;
static jmethodID jGet1;
static jmethodID jGet10;
static jmethodID jGet11;
static jmethodID jGetChar;
static jmethodID jGetChar1;
static jmethodID jGetDouble;
static jmethodID jGetDouble1;
static jmethodID jGetFloat;
static jmethodID jGetFloat1;
static jmethodID jGetInt;
static jmethodID jGetInt1;
static jmethodID jGetLong;
static jmethodID jGetLong1;
static jmethodID jGetShort;
static jmethodID jGetShort1;
static jmethodID jHasArray;
static jmethodID jHashCode;
static jmethodID jIsDirect;
static jmethodID jOrder;
static jmethodID jOrder1;
static jmethodID jPut;
static jmethodID jPut1;
static jmethodID jPut12;
static jmethodID jPut13;
static jmethodID jPut14;
static jmethodID jPutChar;
static jmethodID jPutChar1;
static jmethodID jPutDouble;
static jmethodID jPutDouble1;
static jmethodID jPutFloat;
static jmethodID jPutFloat1;
static jmethodID jPutInt;
static jmethodID jPutInt1;
static jmethodID jPutLong;
static jmethodID jPutLong1;
static jmethodID jPutShort;
static jmethodID jPutShort1;
static jmethodID jSlice;
static jmethodID jToString;
static jmethodID jWrap;
static jmethodID jWrap1;
static jfieldID jBigEndian;
static jfieldID jHb;
static jfieldID jIsReadOnly;
static jfieldID jNativeByteOrder;
static jfieldID jOffset;
};
class BufferInfo : public AutoGlobalWrappedJavaObject {
public:
static void InitStubs(JNIEnv *jEnv);
static BufferInfo* Wrap(jobject obj);
BufferInfo(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
BufferInfo();
void Set(int32_t a0, int32_t a1, int64_t a2, int32_t a3);
int32_t getFlags();
void setFlags(int32_t a0);
int32_t getOffset();
void setOffset(int32_t a0);
int64_t getPresentationTimeUs();
void setPresentationTimeUs(int64_t a0);
int32_t getSize();
void setSize(int32_t a0);
protected:
static jclass mBufferInfoClass;
static jmethodID jBufferInfo;
static jmethodID jSet;
static jfieldID jFlags;
static jfieldID jOffset;
static jfieldID jPresentationTimeUs;
static jfieldID jSize;
};
} /* android */
} /* widget */
} /* mozilla */
#endif

View File

@ -15,6 +15,7 @@ EXPORTS += [
'AndroidJavaWrappers.h', 'AndroidJavaWrappers.h',
'AndroidJNIWrapper.h', 'AndroidJNIWrapper.h',
'GeneratedJNIWrappers.h', 'GeneratedJNIWrappers.h',
'GeneratedSDKWrappers.h',
] ]
SOURCES += [ SOURCES += [
@ -26,6 +27,7 @@ SOURCES += [
'AndroidJNIWrapper.cpp', 'AndroidJNIWrapper.cpp',
'APZCCallbackHandler.cpp', 'APZCCallbackHandler.cpp',
'GeneratedJNIWrappers.cpp', 'GeneratedJNIWrappers.cpp',
'GeneratedSDKWrappers.cpp',
'GfxInfo.cpp', 'GfxInfo.cpp',
'NativeJSContainer.cpp', 'NativeJSContainer.cpp',
'nsAndroidProtocolHandler.cpp', 'nsAndroidProtocolHandler.cpp',