Bug 1014614 - Use Android MediaCodec for decoding H264 and AAC in MP4 r=cpearce,edwin a=lsblakk
authorAndrew Martin McDonough <foolkingcrown@gmail.com>
Wed, 05 Nov 2014 10:17:51 -0600
changeset 225941 47ea294898a0
parent 225940 c82e88a99ca3
child 225942 2973ae13faaa
push id4076
push userjwillcox@mozilla.com
push date2014-11-05 21:30 +0000
treeherdermozilla-beta@5811de401315 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, edwin, lsblakk
bugs1014614
milestone34.0
Bug 1014614 - Use Android MediaCodec for decoding H264 and AAC in MP4 r=cpearce,edwin a=lsblakk --HG-- extra : rebase_source : 7e7d1fc38d13b52e2294c0237479b78b31bdeafb
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
@@ -5175,18 +5175,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
@@ -19,16 +19,19 @@
 #endif
 #ifdef MOZ_FFMPEG
 #include "FFmpegRuntimeLinker.h"
 #endif
 #ifdef MOZ_APPLEMEDIA
 #include "apple/AppleCMLinker.h"
 #include "apple/AppleVTLinker.h"
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
 
 namespace mozilla {
 
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this, new MP4Reader(this));
 }
 
@@ -174,16 +177,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',
     ]
@@ -60,14 +60,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
@@ -591,16 +591,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',