Bug 1014614 - Use Android MediaCodec for decoding H264 and AAC in MP4 r=cpearce,edwin
authorAndrew Martin McDonough <foolkingcrown@gmail.com>
Tue, 21 Oct 2014 08:53:01 -0500
changeset 211442 9f8755778ca6
parent 211441 44fd6ddf34b9
child 211443 31cc8ab02681
push id50717
push userjwillcox@mozilla.com
push date2014-10-21 13:54 +0000
treeherdermozilla-inbound@112be8f081b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, edwin
bugs1014614
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1014614 - Use Android MediaCodec for decoding H264 and AAC in MP4 r=cpearce,edwin
configure.in
content/media/fmp4/MP4Decoder.cpp
content/media/fmp4/PlatformDecoderModule.cpp
content/media/fmp4/PlatformDecoderModule.h
content/media/fmp4/android/AndroidDecoderModule.cpp
content/media/fmp4/android/AndroidDecoderModule.h
content/media/fmp4/moz.build
mobile/android/app/mobile.js
widget/android/AndroidBridge.cpp
widget/android/AndroidJavaWrappers.cpp
widget/android/GeneratedSDKWrappers.cpp
widget/android/GeneratedSDKWrappers.h
widget/android/moz.build
--- a/configure.in
+++ b/configure.in
@@ -5298,18 +5298,25 @@ MOZ_ARG_DISABLE_BOOL(ffmpeg,
 
 if test -n "$MOZ_FFMPEG"; then
     AC_DEFINE(MOZ_FFMPEG)
 fi;
 
 dnl ========================================================
 dnl = Built-in fragmented MP4 support.
 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
-    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
 fi
 
 MOZ_ARG_DISABLE_BOOL(fmp4,
 [  --disable-fmp4  Disable support for in built Fragmented MP4 parsing],
     MOZ_FMP4=,
     MOZ_FMP4=1)
 
--- a/content/media/fmp4/MP4Decoder.cpp
+++ b/content/media/fmp4/MP4Decoder.cpp
@@ -18,16 +18,19 @@
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 #ifdef MOZ_APPLEMEDIA
 #include "apple/AppleDecoderModule.h"
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
 
 namespace mozilla {
 
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this, new MP4Reader(this));
 }
 
@@ -163,16 +166,20 @@ IsGonkMP4DecoderAvailable()
 static bool
 HavePlatformMPEGDecoders()
 {
   return Preferences::GetBool("media.fragmented-mp4.use-blank-decoder") ||
 #ifdef XP_WIN
          // We have H.264/AAC platform decoders on Windows Vista and up.
          IsVistaOrLater() ||
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+         // We need android.media.MediaCodec which exists in API level 16 and higher.
+         (AndroidBridge::Bridge()->GetAPIVersion() >= 16) ||
+#endif
          IsFFmpegAvailable() ||
          IsAppleAvailable() ||
          IsGonkMP4DecoderAvailable() ||
          // TODO: Other platforms...
          false;
 }
 
 /* static */
--- a/content/media/fmp4/PlatformDecoderModule.cpp
+++ b/content/media/fmp4/PlatformDecoderModule.cpp
@@ -12,52 +12,65 @@
 #include "FFmpegRuntimeLinker.h"
 #endif
 #ifdef MOZ_APPLEMEDIA
 #include "AppleDecoderModule.h"
 #endif
 #ifdef MOZ_GONK_MEDIACODEC
 #include "GonkDecoderModule.h"
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidDecoderModule.h"
+#endif
 
 #include "mozilla/Preferences.h"
 #ifdef MOZ_EME
 #include "EMEDecoderModule.h"
 #include "mozilla/CDMProxy.h"
 #endif
 #include "SharedThreadPool.h"
 #include "MediaTaskQueue.h"
 
 namespace mozilla {
 
 extern PlatformDecoderModule* CreateBlankDecoderModule();
 
 bool PlatformDecoderModule::sUseBlankDecoder = false;
 bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
 bool PlatformDecoderModule::sGonkDecoderEnabled = false;
+bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
+bool PlatformDecoderModule::sAndroidMCDecoderPreferred = false;
 
 /* static */
 void
 PlatformDecoderModule::Init()
 {
   MOZ_ASSERT(NS_IsMainThread());
   static bool alreadyInitialized = false;
   if (alreadyInitialized) {
     return;
   }
   alreadyInitialized = true;
 
   Preferences::AddBoolVarCache(&sUseBlankDecoder,
                                "media.fragmented-mp4.use-blank-decoder");
   Preferences::AddBoolVarCache(&sFFmpegDecoderEnabled,
                                "media.fragmented-mp4.ffmpeg.enabled", false);
+
 #ifdef MOZ_GONK_MEDIACODEC
   Preferences::AddBoolVarCache(&sGonkDecoderEnabled,
                                "media.fragmented-mp4.gonk.enabled", false);
 #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
   WMFDecoderModule::Init();
 #endif
 #ifdef MOZ_APPLEMEDIA
   AppleDecoderModule::Init();
 #endif
 }
 
@@ -117,16 +130,21 @@ PlatformDecoderModule::CreateCDMWrapper(
 
 /* static */
 PlatformDecoderModule*
 PlatformDecoderModule::Create()
 {
   // Note: This runs on the decode thread.
   MOZ_ASSERT(!NS_IsMainThread());
 
+#ifdef MOZ_WIDGET_ANDROID
+  if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled){
+    return new AndroidDecoderModule();
+  }
+#endif
   if (sUseBlankDecoder) {
     return CreateBlankDecoderModule();
   }
 #ifdef XP_WIN
   nsAutoPtr<WMFDecoderModule> m(new WMFDecoderModule());
   if (NS_SUCCEEDED(m->Startup())) {
     return m.forget();
   }
@@ -145,16 +163,21 @@ PlatformDecoderModule::Create()
     return m.forget();
   }
 #endif
 #ifdef MOZ_GONK_MEDIACODEC
   if (sGonkDecoderEnabled) {
     return new GonkDecoderModule();
   }
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+  if(sAndroidMCDecoderEnabled){
+    return new AndroidDecoderModule();
+  }
+#endif
   return nullptr;
 }
 
 bool
 PlatformDecoderModule::SupportsAudioMimeType(const char* aMimeType)
 {
   return !strcmp(aMimeType, "audio/mp4a-latm");
 }
--- a/content/media/fmp4/PlatformDecoderModule.h
+++ b/content/media/fmp4/PlatformDecoderModule.h
@@ -126,16 +126,18 @@ public:
   virtual ~PlatformDecoderModule() {}
 
 protected:
   PlatformDecoderModule() {}
   // Caches pref media.fragmented-mp4.use-blank-decoder
   static bool sUseBlankDecoder;
   static bool sFFmpegDecoderEnabled;
   static bool sGonkDecoderEnabled;
+  static bool sAndroidMCDecoderPreferred;
+  static bool sAndroidMCDecoderEnabled;
 };
 
 // A callback used by MediaDataDecoder to return output/errors to the
 // MP4Reader. Implementation is threadsafe, and can be called on any thread.
 class MediaDataDecoderCallback {
 public:
   virtual ~MediaDataDecoderCallback() {}
 
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/android/AndroidDecoderModule.cpp
@@ -0,0 +1,497 @@
+/* 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;
+
+namespace mozilla {
+
+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;
+}
+
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/fmp4/android/AndroidDecoderModule.h
@@ -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
--- a/content/media/fmp4/moz.build
+++ b/content/media/fmp4/moz.build
@@ -20,17 +20,17 @@ SOURCES += [
     'MP4Reader.cpp',
 ]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_EME']:
     DIRS += ['eme']
-    
+
 if CONFIG['MOZ_FFMPEG']:
     EXPORTS += [
         'ffmpeg/FFmpegRuntimeLinker.h',
     ]
     UNIFIED_SOURCES += [
         'ffmpeg/FFmpegLog.cpp',
         'ffmpeg/FFmpegRuntimeLinker.cpp',
     ]
@@ -62,14 +62,22 @@ if CONFIG['MOZ_APPLEMEDIA']:
   ]
 
 if CONFIG['ANDROID_VERSION'] >= '18'and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     DEFINES['MOZ_GONK_MEDIACODEC'] = True
     DIRS += ['gonk']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+    EXPORTS += [
+        'android/AndroidDecoderModule.h',
+    ]
+    UNIFIED_SOURCES += [
+        'android/AndroidDecoderModule.cpp',
+    ]
+
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -555,16 +555,22 @@ pref("media.preload.auto", 2);    // pre
 // Number of video frames we buffer while decoding video.
 // On Android this is decided by a similar value which varies for
 // each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
 // number must be less than the OMX equivalent or gecko will think it is
 // chronically starved of video frames. All decoders seen so far have a value
 // of at least 4.
 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
 pref("image.mem.decodeondraw", true);
 pref("image.mem.min_discard_timeout_ms", 10000);
 
 #ifdef NIGHTLY_BUILD
 // Shumway component (SWF player) is disabled by default. Also see bug 904346.
 pref("shumway.disabled", true);
 #endif
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -36,16 +36,17 @@
 #include "nsIDOMClientRect.h"
 #include "StrongPointer.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsPrintfCString.h"
 #include "NativeJSContainer.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsIHttpChannel.h"
+#include "GeneratedSDKWrappers.h"
 
 using namespace mozilla;
 using namespace mozilla::widget::android;
 using namespace mozilla::gfx;
 
 AndroidBridge* AndroidBridge::sBridge;
 pthread_t AndroidBridge::sJavaUiThread = -1;
 static unsigned sJavaEnvThreadIndex = 0;
@@ -215,16 +216,21 @@ AndroidBridge::Init(JNIEnv *jEnv)
     jByteBufferRead = jEnv->GetMethodID(jReadableByteChannel, "read", "(Ljava/nio/ByteBuffer;)I");
 
     jInputStream = getClassGlobalRef("java/io/InputStream");
     jClose = jEnv->GetMethodID(jInputStream, "close", "()V");
     jAvailable = jEnv->GetMethodID(jInputStream, "available", "()I");
 
     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
     // is not valid for the real gecko main thread, which is set
     // at SetMainThread time.
 
     return true;
 }
 
 bool
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -6,16 +6,17 @@
 #include "AndroidJavaWrappers.h"
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIWidget.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
+#include "GeneratedSDKWrappers.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget::android;
 
 jclass AndroidGeckoEvent::jGeckoEventClass = 0;
 jfieldID AndroidGeckoEvent::jActionField = 0;
 jfieldID AndroidGeckoEvent::jTypeField = 0;
new file mode 100644
--- /dev/null
+++ b/widget/android/GeneratedSDKWrappers.cpp
@@ -0,0 +1,1859 @@
+// GENERATED CODE
+// 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.
+
+#include "GeneratedSDKWrappers.h"
+#include "AndroidBridgeUtilities.h"
+#include "nsXPCOMStrings.h"
+#include "AndroidBridge.h"
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace widget {
+namespace android {
+jclass MediaCodec::mMediaCodecClass = 0;
+jmethodID MediaCodec::jConfigure = 0;
+jmethodID MediaCodec::jCreateByCodecName = 0;
+jmethodID MediaCodec::jCreateDecoderByType = 0;
+jmethodID MediaCodec::jCreateEncoderByType = 0;
+jmethodID MediaCodec::jDequeueInputBuffer = 0;
+jmethodID MediaCodec::jDequeueOutputBuffer = 0;
+jmethodID MediaCodec::jFinalize = 0;
+jmethodID MediaCodec::jFlush = 0;
+jmethodID MediaCodec::jGetInputBuffers = 0;
+jmethodID MediaCodec::jGetOutputBuffers = 0;
+jmethodID MediaCodec::jGetOutputFormat = 0;
+jmethodID MediaCodec::jQueueInputBuffer = 0;
+jmethodID MediaCodec::jQueueSecureInputBuffer = 0;
+jmethodID MediaCodec::jRelease = 0;
+jmethodID MediaCodec::jReleaseOutputBuffer = 0;
+jmethodID MediaCodec::jSetVideoScalingMode = 0;
+jmethodID MediaCodec::jStart = 0;
+jmethodID MediaCodec::jStop = 0;
+jfieldID MediaCodec::jBUFFER_FLAG_CODEC_CONFIG = 0;
+jfieldID MediaCodec::jBUFFER_FLAG_END_OF_STREAM = 0;
+jfieldID MediaCodec::jBUFFER_FLAG_SYNC_FRAME = 0;
+jfieldID MediaCodec::jCONFIGURE_FLAG_ENCODE = 0;
+jfieldID MediaCodec::jCRYPTO_MODE_AES_CTR = 0;
+jfieldID MediaCodec::jCRYPTO_MODE_UNENCRYPTED = 0;
+jfieldID MediaCodec::jINFO_OUTPUT_BUFFERS_CHANGED = 0;
+jfieldID MediaCodec::jINFO_OUTPUT_FORMAT_CHANGED = 0;
+jfieldID MediaCodec::jINFO_TRY_AGAIN_LATER = 0;
+jfieldID MediaCodec::jVIDEO_SCALING_MODE_SCALE_TO_FIT = 0;
+jfieldID MediaCodec::jVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 0;
+void MediaCodec::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mMediaCodecClass = getClassGlobalRef("android/media/MediaCodec");
+    jConfigure = getMethod("configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V");
+    jCreateByCodecName = getStaticMethod("createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;");
+    jCreateDecoderByType = getStaticMethod("createDecoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;");
+    jCreateEncoderByType = getStaticMethod("createEncoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;");
+    jDequeueInputBuffer = getMethod("dequeueInputBuffer", "(J)I");
+    jDequeueOutputBuffer = getMethod("dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I");
+    jFinalize = getMethod("finalize", "()V");
+    jFlush = getMethod("flush", "()V");
+    jGetInputBuffers = getMethod("getInputBuffers", "()[Ljava/nio/ByteBuffer;");
+    jGetOutputBuffers = getMethod("getOutputBuffers", "()[Ljava/nio/ByteBuffer;");
+    jGetOutputFormat = getMethod("getOutputFormat", "()Landroid/media/MediaFormat;");
+    jQueueInputBuffer = getMethod("queueInputBuffer", "(IIIJI)V");
+    jQueueSecureInputBuffer = getMethod("queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V");
+    jRelease = getMethod("release", "()V");
+    jReleaseOutputBuffer = getMethod("releaseOutputBuffer", "(IZ)V");
+    jSetVideoScalingMode = getMethod("setVideoScalingMode", "(I)V");
+    jStart = getMethod("start", "()V");
+    jStop = getMethod("stop", "()V");
+    jBUFFER_FLAG_CODEC_CONFIG = getStaticField("BUFFER_FLAG_CODEC_CONFIG", "I");
+    jBUFFER_FLAG_END_OF_STREAM = getStaticField("BUFFER_FLAG_END_OF_STREAM", "I");
+    jBUFFER_FLAG_SYNC_FRAME = getStaticField("BUFFER_FLAG_SYNC_FRAME", "I");
+    jCONFIGURE_FLAG_ENCODE = getStaticField("CONFIGURE_FLAG_ENCODE", "I");
+    jCRYPTO_MODE_AES_CTR = getStaticField("CRYPTO_MODE_AES_CTR", "I");
+    jCRYPTO_MODE_UNENCRYPTED = getStaticField("CRYPTO_MODE_UNENCRYPTED", "I");
+    jINFO_OUTPUT_BUFFERS_CHANGED = getStaticField("INFO_OUTPUT_BUFFERS_CHANGED", "I");
+    jINFO_OUTPUT_FORMAT_CHANGED = getStaticField("INFO_OUTPUT_FORMAT_CHANGED", "I");
+    jINFO_TRY_AGAIN_LATER = getStaticField("INFO_TRY_AGAIN_LATER", "I");
+    jVIDEO_SCALING_MODE_SCALE_TO_FIT = getStaticField("VIDEO_SCALING_MODE_SCALE_TO_FIT", "I");
+    jVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = getStaticField("VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING", "I");
+}
+
+MediaCodec* MediaCodec::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    MediaCodec* ret = new MediaCodec(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+void MediaCodec::Configure(jobject a0, jobject a1, jobject a2, int32_t a3) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(3) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[4];
+    args[0].l = a0;
+    args[1].l = a1;
+    args[2].l = a2;
+    args[3].i = a3;
+
+    env->CallVoidMethodA(wrapped_obj, jConfigure, args);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+jobject MediaCodec::CreateByCodecName(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallStaticObjectMethod(mMediaCodecClass, jCreateByCodecName, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject MediaCodec::CreateDecoderByType(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallStaticObjectMethod(mMediaCodecClass, jCreateDecoderByType, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject MediaCodec::CreateEncoderByType(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallStaticObjectMethod(mMediaCodecClass, jCreateEncoderByType, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+int32_t MediaCodec::DequeueInputBuffer(int64_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jDequeueInputBuffer, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t MediaCodec::DequeueOutputBuffer(jobject a0, int64_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jDequeueOutputBuffer, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+void MediaCodec::Finalize() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jFinalize);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::Flush() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jFlush);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+jobjectArray MediaCodec::GetInputBuffers() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGetInputBuffers);
+    AndroidBridge::HandleUncaughtException(env);
+    jobjectArray ret = static_cast<jobjectArray>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobjectArray MediaCodec::GetOutputBuffers() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGetOutputBuffers);
+    AndroidBridge::HandleUncaughtException(env);
+    jobjectArray ret = static_cast<jobjectArray>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject MediaCodec::GetOutputFormat() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGetOutputFormat);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+void MediaCodec::QueueInputBuffer(int32_t a0, int32_t a1, int32_t a2, int64_t a3, int32_t a4) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(5) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[5];
+    args[0].i = a0;
+    args[1].i = a1;
+    args[2].i = a2;
+    args[3].j = a3;
+    args[4].i = a4;
+
+    env->CallVoidMethodA(wrapped_obj, jQueueInputBuffer, args);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::QueueSecureInputBuffer(int32_t a0, int32_t a1, jobject a2, int64_t a3, int32_t a4) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[5];
+    args[0].i = a0;
+    args[1].i = a1;
+    args[2].l = a2;
+    args[3].j = a3;
+    args[4].i = a4;
+
+    env->CallVoidMethodA(wrapped_obj, jQueueSecureInputBuffer, args);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::Release() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jRelease);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::ReleaseOutputBuffer(int32_t a0, bool a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jReleaseOutputBuffer, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::SetVideoScalingMode(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jSetVideoScalingMode, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::Start() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jStart);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaCodec::Stop() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, jStop);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+int32_t MediaCodec::getBUFFER_FLAG_CODEC_CONFIG() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jBUFFER_FLAG_CODEC_CONFIG);
+}
+
+int32_t MediaCodec::getBUFFER_FLAG_END_OF_STREAM() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jBUFFER_FLAG_END_OF_STREAM);
+}
+
+int32_t MediaCodec::getBUFFER_FLAG_SYNC_FRAME() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jBUFFER_FLAG_SYNC_FRAME);
+}
+
+int32_t MediaCodec::getCONFIGURE_FLAG_ENCODE() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jCONFIGURE_FLAG_ENCODE);
+}
+
+int32_t MediaCodec::getCRYPTO_MODE_AES_CTR() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jCRYPTO_MODE_AES_CTR);
+}
+
+int32_t MediaCodec::getCRYPTO_MODE_UNENCRYPTED() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jCRYPTO_MODE_UNENCRYPTED);
+}
+
+int32_t MediaCodec::getINFO_OUTPUT_BUFFERS_CHANGED() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jINFO_OUTPUT_BUFFERS_CHANGED);
+}
+
+int32_t MediaCodec::getINFO_OUTPUT_FORMAT_CHANGED() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jINFO_OUTPUT_FORMAT_CHANGED);
+}
+
+int32_t MediaCodec::getINFO_TRY_AGAIN_LATER() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jINFO_TRY_AGAIN_LATER);
+}
+
+int32_t MediaCodec::getVIDEO_SCALING_MODE_SCALE_TO_FIT() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jVIDEO_SCALING_MODE_SCALE_TO_FIT);
+}
+
+int32_t MediaCodec::getVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetStaticIntField(mMediaCodecClass, jVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
+}
+jclass MediaFormat::mMediaFormatClass = 0;
+jmethodID MediaFormat::jMediaFormat = 0;
+jmethodID MediaFormat::jContainsKey = 0;
+jmethodID MediaFormat::jCreateAudioFormat = 0;
+jmethodID MediaFormat::jCreateVideoFormat = 0;
+jmethodID MediaFormat::jGetByteBuffer = 0;
+jmethodID MediaFormat::jGetFloat = 0;
+jmethodID MediaFormat::jGetInteger = 0;
+jmethodID MediaFormat::jGetLong = 0;
+jmethodID MediaFormat::jGetString = 0;
+jmethodID MediaFormat::jSetByteBuffer = 0;
+jmethodID MediaFormat::jSetFloat = 0;
+jmethodID MediaFormat::jSetInteger = 0;
+jmethodID MediaFormat::jSetLong = 0;
+jmethodID MediaFormat::jSetString = 0;
+jmethodID MediaFormat::jToString = 0;
+jfieldID MediaFormat::jKEY_AAC_PROFILE = 0;
+jfieldID MediaFormat::jKEY_BIT_RATE = 0;
+jfieldID MediaFormat::jKEY_CHANNEL_COUNT = 0;
+jfieldID MediaFormat::jKEY_CHANNEL_MASK = 0;
+jfieldID MediaFormat::jKEY_COLOR_FORMAT = 0;
+jfieldID MediaFormat::jKEY_DURATION = 0;
+jfieldID MediaFormat::jKEY_FLAC_COMPRESSION_LEVEL = 0;
+jfieldID MediaFormat::jKEY_FRAME_RATE = 0;
+jfieldID MediaFormat::jKEY_HEIGHT = 0;
+jfieldID MediaFormat::jKEY_IS_ADTS = 0;
+jfieldID MediaFormat::jKEY_I_FRAME_INTERVAL = 0;
+jfieldID MediaFormat::jKEY_MAX_INPUT_SIZE = 0;
+jfieldID MediaFormat::jKEY_MIME = 0;
+jfieldID MediaFormat::jKEY_SAMPLE_RATE = 0;
+jfieldID MediaFormat::jKEY_WIDTH = 0;
+void MediaFormat::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mMediaFormatClass = getClassGlobalRef("android/media/MediaFormat");
+    jMediaFormat = getMethod("<init>", "()V");
+    jContainsKey = getMethod("containsKey", "(Ljava/lang/String;)Z");
+    jCreateAudioFormat = getStaticMethod("createAudioFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
+    jCreateVideoFormat = getStaticMethod("createVideoFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;");
+    jGetByteBuffer = getMethod("getByteBuffer", "(Ljava/lang/String;)Ljava/nio/ByteBuffer;");
+    jGetFloat = getMethod("getFloat", "(Ljava/lang/String;)F");
+    jGetInteger = getMethod("getInteger", "(Ljava/lang/String;)I");
+    jGetLong = getMethod("getLong", "(Ljava/lang/String;)J");
+    jGetString = getMethod("getString", "(Ljava/lang/String;)Ljava/lang/String;");
+    jSetByteBuffer = getMethod("setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V");
+    jSetFloat = getMethod("setFloat", "(Ljava/lang/String;F)V");
+    jSetInteger = getMethod("setInteger", "(Ljava/lang/String;I)V");
+    jSetLong = getMethod("setLong", "(Ljava/lang/String;J)V");
+    jSetString = getMethod("setString", "(Ljava/lang/String;Ljava/lang/String;)V");
+    jToString = getMethod("toString", "()Ljava/lang/String;");
+    jKEY_AAC_PROFILE = getStaticField("KEY_AAC_PROFILE", "Ljava/lang/String;");
+    jKEY_BIT_RATE = getStaticField("KEY_BIT_RATE", "Ljava/lang/String;");
+    jKEY_CHANNEL_COUNT = getStaticField("KEY_CHANNEL_COUNT", "Ljava/lang/String;");
+    jKEY_CHANNEL_MASK = getStaticField("KEY_CHANNEL_MASK", "Ljava/lang/String;");
+    jKEY_COLOR_FORMAT = getStaticField("KEY_COLOR_FORMAT", "Ljava/lang/String;");
+    jKEY_DURATION = getStaticField("KEY_DURATION", "Ljava/lang/String;");
+    jKEY_FLAC_COMPRESSION_LEVEL = getStaticField("KEY_FLAC_COMPRESSION_LEVEL", "Ljava/lang/String;");
+    jKEY_FRAME_RATE = getStaticField("KEY_FRAME_RATE", "Ljava/lang/String;");
+    jKEY_HEIGHT = getStaticField("KEY_HEIGHT", "Ljava/lang/String;");
+    jKEY_IS_ADTS = getStaticField("KEY_IS_ADTS", "Ljava/lang/String;");
+    jKEY_I_FRAME_INTERVAL = getStaticField("KEY_I_FRAME_INTERVAL", "Ljava/lang/String;");
+    jKEY_MAX_INPUT_SIZE = getStaticField("KEY_MAX_INPUT_SIZE", "Ljava/lang/String;");
+    jKEY_MIME = getStaticField("KEY_MIME", "Ljava/lang/String;");
+    jKEY_SAMPLE_RATE = getStaticField("KEY_SAMPLE_RATE", "Ljava/lang/String;");
+    jKEY_WIDTH = getStaticField("KEY_WIDTH", "Ljava/lang/String;");
+}
+
+MediaFormat* MediaFormat::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    MediaFormat* ret = new MediaFormat(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+MediaFormat::MediaFormat() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    Init(env->NewObject(mMediaFormatClass, jMediaFormat), env);
+    env->PopLocalFrame(nullptr);
+}
+
+bool MediaFormat::ContainsKey(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    bool temp = env->CallBooleanMethod(wrapped_obj, jContainsKey, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jobject MediaFormat::CreateAudioFormat(const nsAString& a0, int32_t a1, int32_t a2) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[3];
+    args[0].l = AndroidBridge::NewJavaString(env, a0);
+    args[1].i = a1;
+    args[2].i = a2;
+
+    jobject temp = env->CallStaticObjectMethodA(mMediaFormatClass, jCreateAudioFormat, args);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject MediaFormat::CreateVideoFormat(const nsAString& a0, int32_t a1, int32_t a2) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[3];
+    args[0].l = AndroidBridge::NewJavaString(env, a0);
+    args[1].i = a1;
+    args[2].i = a2;
+
+    jobject temp = env->CallStaticObjectMethodA(mMediaFormatClass, jCreateVideoFormat, args);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject MediaFormat::GetByteBuffer(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGetByteBuffer, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jfloat MediaFormat::GetFloat(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jfloat temp = env->CallFloatMethod(wrapped_obj, jGetFloat, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t MediaFormat::GetInteger(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jGetInteger, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int64_t MediaFormat::GetLong(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    int64_t temp = env->CallLongMethod(wrapped_obj, jGetLong, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jstring MediaFormat::GetString(const nsAString& a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGetString, j0);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+void MediaFormat::SetByteBuffer(const nsAString& a0, jobject a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    env->CallVoidMethod(wrapped_obj, jSetByteBuffer, j0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaFormat::SetFloat(const nsAString& a0, jfloat a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    env->CallVoidMethod(wrapped_obj, jSetFloat, j0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaFormat::SetInteger(const nsAString& a0, int32_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    env->CallVoidMethod(wrapped_obj, jSetInteger, j0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaFormat::SetLong(const nsAString& a0, int64_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+
+    env->CallVoidMethod(wrapped_obj, jSetLong, j0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+void MediaFormat::SetString(const nsAString& a0, const nsAString& a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j0 = AndroidBridge::NewJavaString(env, a0);
+    jstring j1 = AndroidBridge::NewJavaString(env, a1);
+
+    env->CallVoidMethod(wrapped_obj, jSetString, j0, j1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+jstring MediaFormat::ToString() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jToString);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jstring MediaFormat::getKEY_AAC_PROFILE() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_AAC_PROFILE));
+}
+
+jstring MediaFormat::getKEY_BIT_RATE() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_BIT_RATE));
+}
+
+jstring MediaFormat::getKEY_CHANNEL_COUNT() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_CHANNEL_COUNT));
+}
+
+jstring MediaFormat::getKEY_CHANNEL_MASK() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_CHANNEL_MASK));
+}
+
+jstring MediaFormat::getKEY_COLOR_FORMAT() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_COLOR_FORMAT));
+}
+
+jstring MediaFormat::getKEY_DURATION() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_DURATION));
+}
+
+jstring MediaFormat::getKEY_FLAC_COMPRESSION_LEVEL() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_FLAC_COMPRESSION_LEVEL));
+}
+
+jstring MediaFormat::getKEY_FRAME_RATE() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_FRAME_RATE));
+}
+
+jstring MediaFormat::getKEY_HEIGHT() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_HEIGHT));
+}
+
+jstring MediaFormat::getKEY_IS_ADTS() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_IS_ADTS));
+}
+
+jstring MediaFormat::getKEY_I_FRAME_INTERVAL() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_I_FRAME_INTERVAL));
+}
+
+jstring MediaFormat::getKEY_MAX_INPUT_SIZE() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_MAX_INPUT_SIZE));
+}
+
+jstring MediaFormat::getKEY_MIME() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_MIME));
+}
+
+jstring MediaFormat::getKEY_SAMPLE_RATE() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_SAMPLE_RATE));
+}
+
+jstring MediaFormat::getKEY_WIDTH() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jstring>(env->GetStaticObjectField(mMediaFormatClass, jKEY_WIDTH));
+}
+jclass ByteBuffer::mByteBufferClass = 0;
+jmethodID ByteBuffer::j_get = 0;
+jmethodID ByteBuffer::j_put = 0;
+jmethodID ByteBuffer::jAllocate = 0;
+jmethodID ByteBuffer::jAllocateDirect = 0;
+jmethodID ByteBuffer::jArray = 0;
+jmethodID ByteBuffer::jArray1 = 0;
+jmethodID ByteBuffer::jArrayOffset = 0;
+jmethodID ByteBuffer::jAsCharBuffer = 0;
+jmethodID ByteBuffer::jAsDoubleBuffer = 0;
+jmethodID ByteBuffer::jAsFloatBuffer = 0;
+jmethodID ByteBuffer::jAsIntBuffer = 0;
+jmethodID ByteBuffer::jAsLongBuffer = 0;
+jmethodID ByteBuffer::jAsReadOnlyBuffer = 0;
+jmethodID ByteBuffer::jAsShortBuffer = 0;
+jmethodID ByteBuffer::jCompact = 0;
+jmethodID ByteBuffer::jCompareTo = 0;
+jmethodID ByteBuffer::jCompareTo1 = 0;
+jmethodID ByteBuffer::jDuplicate = 0;
+jmethodID ByteBuffer::jEquals = 0;
+jmethodID ByteBuffer::jGet = 0;
+jmethodID ByteBuffer::jGet1 = 0;
+jmethodID ByteBuffer::jGet10 = 0;
+jmethodID ByteBuffer::jGet11 = 0;
+jmethodID ByteBuffer::jGetChar = 0;
+jmethodID ByteBuffer::jGetChar1 = 0;
+jmethodID ByteBuffer::jGetDouble = 0;
+jmethodID ByteBuffer::jGetDouble1 = 0;
+jmethodID ByteBuffer::jGetFloat = 0;
+jmethodID ByteBuffer::jGetFloat1 = 0;
+jmethodID ByteBuffer::jGetInt = 0;
+jmethodID ByteBuffer::jGetInt1 = 0;
+jmethodID ByteBuffer::jGetLong = 0;
+jmethodID ByteBuffer::jGetLong1 = 0;
+jmethodID ByteBuffer::jGetShort = 0;
+jmethodID ByteBuffer::jGetShort1 = 0;
+jmethodID ByteBuffer::jHasArray = 0;
+jmethodID ByteBuffer::jHashCode = 0;
+jmethodID ByteBuffer::jIsDirect = 0;
+jmethodID ByteBuffer::jOrder = 0;
+jmethodID ByteBuffer::jOrder1 = 0;
+jmethodID ByteBuffer::jPut = 0;
+jmethodID ByteBuffer::jPut1 = 0;
+jmethodID ByteBuffer::jPut12 = 0;
+jmethodID ByteBuffer::jPut13 = 0;
+jmethodID ByteBuffer::jPut14 = 0;
+jmethodID ByteBuffer::jPutChar = 0;
+jmethodID ByteBuffer::jPutChar1 = 0;
+jmethodID ByteBuffer::jPutDouble = 0;
+jmethodID ByteBuffer::jPutDouble1 = 0;
+jmethodID ByteBuffer::jPutFloat = 0;
+jmethodID ByteBuffer::jPutFloat1 = 0;
+jmethodID ByteBuffer::jPutInt = 0;
+jmethodID ByteBuffer::jPutInt1 = 0;
+jmethodID ByteBuffer::jPutLong = 0;
+jmethodID ByteBuffer::jPutLong1 = 0;
+jmethodID ByteBuffer::jPutShort = 0;
+jmethodID ByteBuffer::jPutShort1 = 0;
+jmethodID ByteBuffer::jSlice = 0;
+jmethodID ByteBuffer::jToString = 0;
+jmethodID ByteBuffer::jWrap = 0;
+jmethodID ByteBuffer::jWrap1 = 0;
+jfieldID ByteBuffer::jBigEndian = 0;
+jfieldID ByteBuffer::jHb = 0;
+jfieldID ByteBuffer::jIsReadOnly = 0;
+jfieldID ByteBuffer::jNativeByteOrder = 0;
+jfieldID ByteBuffer::jOffset = 0;
+void ByteBuffer::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mByteBufferClass = getClassGlobalRef("java/nio/ByteBuffer");
+    //j_get = getMethod("_get", "(I)B");
+    //j_put = getMethod("_put", "(IB)V");
+    jAllocate = getStaticMethod("allocate", "(I)Ljava/nio/ByteBuffer;");
+    jAllocateDirect = getStaticMethod("allocateDirect", "(I)Ljava/nio/ByteBuffer;");
+    jArray = getMethod("array", "()Ljava/lang/Object;");
+    jArray1 = getMethod("array", "()[B");
+    jArrayOffset = getMethod("arrayOffset", "()I");
+    jAsCharBuffer = getMethod("asCharBuffer", "()Ljava/nio/CharBuffer;");
+    jAsDoubleBuffer = getMethod("asDoubleBuffer", "()Ljava/nio/DoubleBuffer;");
+    jAsFloatBuffer = getMethod("asFloatBuffer", "()Ljava/nio/FloatBuffer;");
+    jAsIntBuffer = getMethod("asIntBuffer", "()Ljava/nio/IntBuffer;");
+    jAsLongBuffer = getMethod("asLongBuffer", "()Ljava/nio/LongBuffer;");
+    jAsReadOnlyBuffer = getMethod("asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
+    jAsShortBuffer = getMethod("asShortBuffer", "()Ljava/nio/ShortBuffer;");
+    jCompact = getMethod("compact", "()Ljava/nio/ByteBuffer;");
+    jCompareTo = getMethod("compareTo", "(Ljava/lang/Object;)I");
+    jCompareTo1 = getMethod("compareTo", "(Ljava/nio/ByteBuffer;)I");
+    jDuplicate = getMethod("duplicate", "()Ljava/nio/ByteBuffer;");
+    jEquals = getMethod("equals", "(Ljava/lang/Object;)Z");
+    jGet = getMethod("get", "()B");
+    jGet1 = getMethod("get", "(I)B");
+    jGet10 = getMethod("get", "([B)Ljava/nio/ByteBuffer;");
+    jGet11 = getMethod("get", "([BII)Ljava/nio/ByteBuffer;");
+    jGetChar = getMethod("getChar", "()C");
+    jGetChar1 = getMethod("getChar", "(I)C");
+    jGetDouble = getMethod("getDouble", "()D");
+    jGetDouble1 = getMethod("getDouble", "(I)D");
+    jGetFloat = getMethod("getFloat", "()F");
+    jGetFloat1 = getMethod("getFloat", "(I)F");
+    jGetInt = getMethod("getInt", "()I");
+    jGetInt1 = getMethod("getInt", "(I)I");
+    jGetLong = getMethod("getLong", "()J");
+    jGetLong1 = getMethod("getLong", "(I)J");
+    jGetShort = getMethod("getShort", "()S");
+    jGetShort1 = getMethod("getShort", "(I)S");
+    jHasArray = getMethod("hasArray", "()Z");
+    jHashCode = getMethod("hashCode", "()I");
+    jIsDirect = getMethod("isDirect", "()Z");
+    jOrder = getMethod("order", "()Ljava/nio/ByteOrder;");
+    jOrder1 = getMethod("order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
+    jPut = getMethod("put", "(B)Ljava/nio/ByteBuffer;");
+    jPut1 = getMethod("put", "(IB)Ljava/nio/ByteBuffer;");
+    jPut12 = getMethod("put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;");
+    jPut13 = getMethod("put", "([B)Ljava/nio/ByteBuffer;");
+    jPut14 = getMethod("put", "([BII)Ljava/nio/ByteBuffer;");
+    jPutChar = getMethod("putChar", "(C)Ljava/nio/ByteBuffer;");
+    jPutChar1 = getMethod("putChar", "(IC)Ljava/nio/ByteBuffer;");
+    jPutDouble = getMethod("putDouble", "(D)Ljava/nio/ByteBuffer;");
+    jPutDouble1 = getMethod("putDouble", "(ID)Ljava/nio/ByteBuffer;");
+    jPutFloat = getMethod("putFloat", "(F)Ljava/nio/ByteBuffer;");
+    jPutFloat1 = getMethod("putFloat", "(IF)Ljava/nio/ByteBuffer;");
+    jPutInt = getMethod("putInt", "(I)Ljava/nio/ByteBuffer;");
+    jPutInt1 = getMethod("putInt", "(II)Ljava/nio/ByteBuffer;");
+    jPutLong = getMethod("putLong", "(IJ)Ljava/nio/ByteBuffer;");
+    jPutLong1 = getMethod("putLong", "(J)Ljava/nio/ByteBuffer;");
+    jPutShort = getMethod("putShort", "(IS)Ljava/nio/ByteBuffer;");
+    jPutShort1 = getMethod("putShort", "(S)Ljava/nio/ByteBuffer;");
+    jSlice = getMethod("slice", "()Ljava/nio/ByteBuffer;");
+    jToString = getMethod("toString", "()Ljava/lang/String;");
+    jWrap = getStaticMethod("wrap", "([B)Ljava/nio/ByteBuffer;");
+    jWrap1 = getStaticMethod("wrap", "([BII)Ljava/nio/ByteBuffer;");
+    /*
+    jBigEndian = getField("bigEndian", "Z");
+    jHb = getField("hb", "[B");
+    jIsReadOnly = getField("isReadOnly", "Z");
+    jNativeByteOrder = getField("nativeByteOrder", "Z");
+    jOffset = getField("offset", "I");
+  */
+}
+
+ByteBuffer* ByteBuffer::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    ByteBuffer* ret = new ByteBuffer(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+int8_t ByteBuffer::_get(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int8_t temp = env->CallByteMethod(wrapped_obj, j_get, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+void ByteBuffer::_put(int32_t a0, int8_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    env->CallVoidMethod(wrapped_obj, j_put, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+jobject ByteBuffer::Allocate(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mByteBufferClass, jAllocate, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AllocateDirect(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mByteBufferClass, jAllocateDirect, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Array() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jArray);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jbyteArray ByteBuffer::Array1() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jArray1);
+    AndroidBridge::HandleUncaughtException(env);
+    jbyteArray ret = static_cast<jbyteArray>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+int32_t ByteBuffer::ArrayOffset() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jArrayOffset);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jstring ByteBuffer::AsCharBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsCharBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsDoubleBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsDoubleBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsFloatBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsFloatBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsIntBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsIntBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsLongBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsLongBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsReadOnlyBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsReadOnlyBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::AsShortBuffer() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jAsShortBuffer);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Compact() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jCompact);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+int32_t ByteBuffer::CompareTo(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jCompareTo, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t ByteBuffer::CompareTo1(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jCompareTo1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jobject ByteBuffer::Duplicate() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jDuplicate);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+bool ByteBuffer::Equals(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    bool temp = env->CallBooleanMethod(wrapped_obj, jEquals, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int8_t ByteBuffer::Get() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int8_t temp = env->CallByteMethod(wrapped_obj, jGet);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int8_t ByteBuffer::Get1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int8_t temp = env->CallByteMethod(wrapped_obj, jGet1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jobject ByteBuffer::Get1(jbyteArray a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jGet10, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Get1(jbyteArray a0, int32_t a1, int32_t a2) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[3];
+    args[0].l = a0;
+    args[1].i = a1;
+    args[2].i = a2;
+
+    jobject temp = env->CallObjectMethodA(wrapped_obj, jGet11, args);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+uint16_t ByteBuffer::GetChar() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    uint16_t temp = env->CallCharMethod(wrapped_obj, jGetChar);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+uint16_t ByteBuffer::GetChar1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    uint16_t temp = env->CallCharMethod(wrapped_obj, jGetChar1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jdouble ByteBuffer::GetDouble() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jdouble temp = env->CallDoubleMethod(wrapped_obj, jGetDouble);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jdouble ByteBuffer::GetDouble1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jdouble temp = env->CallDoubleMethod(wrapped_obj, jGetDouble1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jfloat ByteBuffer::GetFloat() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jfloat temp = env->CallFloatMethod(wrapped_obj, jGetFloat);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jfloat ByteBuffer::GetFloat1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jfloat temp = env->CallFloatMethod(wrapped_obj, jGetFloat1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t ByteBuffer::GetInt() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jGetInt);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t ByteBuffer::GetInt1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jGetInt1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int64_t ByteBuffer::GetLong() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int64_t temp = env->CallLongMethod(wrapped_obj, jGetLong);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int64_t ByteBuffer::GetLong1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int64_t temp = env->CallLongMethod(wrapped_obj, jGetLong1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int16_t ByteBuffer::GetShort() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int16_t temp = env->CallShortMethod(wrapped_obj, jGetShort);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int16_t ByteBuffer::GetShort1(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int16_t temp = env->CallShortMethod(wrapped_obj, jGetShort1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+bool ByteBuffer::HasArray() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    bool temp = env->CallBooleanMethod(wrapped_obj, jHasArray);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+int32_t ByteBuffer::HashCode() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    int32_t temp = env->CallIntMethod(wrapped_obj, jHashCode);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+bool ByteBuffer::IsDirect() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    bool temp = env->CallBooleanMethod(wrapped_obj, jIsDirect);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+jobject ByteBuffer::Order() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jOrder);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Order1(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jOrder1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Put(int8_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPut, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Put1(int32_t a0, int8_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPut1, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Put1(jobject a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPut12, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Put1(jbyteArray a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPut13, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Put1(jbyteArray a0, int32_t a1, int32_t a2) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[3];
+    args[0].l = a0;
+    args[1].i = a1;
+    args[2].i = a2;
+
+    jobject temp = env->CallObjectMethodA(wrapped_obj, jPut14, args);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutChar(uint16_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutChar, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutChar1(int32_t a0, uint16_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutChar1, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutDouble(jdouble a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutDouble, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutDouble1(int32_t a0, jdouble a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutDouble1, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutFloat(jfloat a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutFloat, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutFloat1(int32_t a0, jfloat a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutFloat1, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutInt(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutInt, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutInt1(int32_t a0, int32_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutInt1, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutLong(int32_t a0, int64_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutLong, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutLong1(int64_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutLong1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutShort(int32_t a0, int16_t a1) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutShort, a0, a1);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::PutShort1(int16_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jPutShort1, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Slice() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jSlice);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jstring ByteBuffer::ToString() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallObjectMethod(wrapped_obj, jToString);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Wrap1(jbyteArray a0) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mByteBufferClass, jWrap, a0);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+jobject ByteBuffer::Wrap2(jbyteArray a0, int32_t a1, int32_t a2) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(2) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[3];
+    args[0].l = a0;
+    args[1].i = a1;
+    args[2].i = a2;
+
+    jobject temp = env->CallStaticObjectMethodA(mByteBufferClass, jWrap1, args);
+    AndroidBridge::HandleUncaughtException(env);
+    jobject ret = static_cast<jobject>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+bool ByteBuffer::getBigEndian() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetBooleanField(wrapped_obj, jBigEndian);
+}
+
+void ByteBuffer::setBigEndian(bool a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetBooleanField(wrapped_obj, jBigEndian, a0);
+}
+
+jbyteArray ByteBuffer::getHb() {
+    JNIEnv *env = GetJNIForThread();
+    return static_cast<jbyteArray>(env->GetObjectField(wrapped_obj, jHb));
+}
+
+bool ByteBuffer::getIsReadOnly() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetBooleanField(wrapped_obj, jIsReadOnly);
+}
+
+void ByteBuffer::setIsReadOnly(bool a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetBooleanField(wrapped_obj, jIsReadOnly, a0);
+}
+
+bool ByteBuffer::getNativeByteOrder() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetBooleanField(wrapped_obj, jNativeByteOrder);
+}
+
+void ByteBuffer::setNativeByteOrder(bool a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetBooleanField(wrapped_obj, jNativeByteOrder, a0);
+}
+
+int32_t ByteBuffer::getOffset() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetIntField(wrapped_obj, jOffset);
+}
+
+jclass BufferInfo::mBufferInfoClass = 0;
+jmethodID BufferInfo::jBufferInfo = 0;
+jmethodID BufferInfo::jSet = 0;
+jfieldID BufferInfo::jFlags = 0;
+jfieldID BufferInfo::jOffset = 0;
+jfieldID BufferInfo::jPresentationTimeUs = 0;
+jfieldID BufferInfo::jSize = 0;
+void BufferInfo::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mBufferInfoClass = getClassGlobalRef("android/media/MediaCodec$BufferInfo");
+    jBufferInfo = getMethod("<init>", "()V");
+    jSet = getMethod("set", "(IIJI)V");
+    jFlags = getField("flags", "I");
+    jOffset = getField("offset", "I");
+    jPresentationTimeUs = getField("presentationTimeUs", "J");
+    jSize = getField("size", "I");
+}
+
+BufferInfo* BufferInfo::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    BufferInfo* ret = new BufferInfo(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+BufferInfo::BufferInfo() {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    Init(env->NewObject(mBufferInfoClass, jBufferInfo), env);
+    env->PopLocalFrame(nullptr);
+}
+
+void BufferInfo::Set(int32_t a0, int32_t a1, int64_t a2, int32_t a3) {
+    JNIEnv *env = GetJNIForThread();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jvalue args[4];
+    args[0].i = a0;
+    args[1].i = a1;
+    args[2].j = a2;
+    args[3].i = a3;
+
+    env->CallVoidMethodA(wrapped_obj, jSet, args);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+}
+
+int32_t BufferInfo::getFlags() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetIntField(wrapped_obj, jFlags);
+}
+
+void BufferInfo::setFlags(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetIntField(wrapped_obj, jFlags, a0);
+}
+
+int32_t BufferInfo::getOffset() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetIntField(wrapped_obj, jOffset);
+}
+
+void BufferInfo::setOffset(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetIntField(wrapped_obj, jOffset, a0);
+}
+
+int64_t BufferInfo::getPresentationTimeUs() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetLongField(wrapped_obj, jPresentationTimeUs);
+}
+
+void BufferInfo::setPresentationTimeUs(int64_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetLongField(wrapped_obj, jPresentationTimeUs, a0);
+}
+
+int32_t BufferInfo::getSize() {
+    JNIEnv *env = GetJNIForThread();
+    return env->GetIntField(wrapped_obj, jSize);
+}
+
+void BufferInfo::setSize(int32_t a0) {
+    JNIEnv *env = GetJNIForThread();
+    env->SetIntField(wrapped_obj, jSize, a0);
+}
+
+void InitSDKStubs(JNIEnv *jEnv) {
+    MediaCodec::InitStubs(jEnv);
+    MediaFormat::InitStubs(jEnv);
+    ByteBuffer::InitStubs(jEnv);
+    BufferInfo::InitStubs(jEnv);
+}
+} /* android */
+} /* widget */
+} /* mozilla */
new file mode 100644
--- /dev/null
+++ b/widget/android/GeneratedSDKWrappers.h
@@ -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
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -10,27 +10,29 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'widget_android'
 
 EXPORTS += [
     'AndroidBridge.h',
     'AndroidJavaWrappers.h',
     'AndroidJNIWrapper.h',
     'GeneratedJNIWrappers.h',
+    'GeneratedSDKWrappers.h',
 ]
 
 SOURCES += [
     'AndroidBridge.cpp',
     'AndroidDirectTexture.cpp',
     'AndroidGraphicBuffer.cpp',
     'AndroidJavaWrappers.cpp',
     'AndroidJNI.cpp',
     'AndroidJNIWrapper.cpp',
     'APZCCallbackHandler.cpp',
     'GeneratedJNIWrappers.cpp',
+    'GeneratedSDKWrappers.cpp',
     'GfxInfo.cpp',
     'NativeJSContainer.cpp',
     'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
     'nsDeviceContextAndroid.cpp',
     'nsIdleServiceAndroid.cpp',
     'nsIMEPicker.cpp',