Bug 866080 - Use Android I420ColorConverter in OMX plugin. r=sotaro, r=doublec, a=lsblakk
authorEdwin Flores <eflores@mozilla.com>
Tue, 17 Dec 2013 11:58:16 +1300
changeset 176060 354c393518fb5a5e00aa9bf5399209e8c7866c4b
parent 176059 79cbd6fb82054dbf34ef637ce37a9753df011900
child 176061 4f692656efddb593fa268fea9cfb94716cd759cd
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro, doublec, lsblakk
bugs866080
milestone28.0a2
Bug 866080 - Use Android I420ColorConverter in OMX plugin. r=sotaro, r=doublec, a=lsblakk
content/media/plugins/MPAPI.h
content/media/plugins/MediaPluginReader.cpp
content/media/plugins/MediaPluginReader.h
media/omx-plugin/Makefile.in
media/omx-plugin/OmxPlugin.cpp
media/omx-plugin/include/ics/I420ColorConverter.h
media/omx-plugin/include/ics/II420ColorConverter.h
media/omx-plugin/lib/ics/libvideoeditorplayer/Makefile.in
media/omx-plugin/lib/ics/libvideoeditorplayer/libvideoeditorplayer.cpp
media/omx-plugin/lib/ics/libvideoeditorplayer/moz.build
toolkit/toolkit.mozbuild
--- a/content/media/plugins/MPAPI.h
+++ b/content/media/plugins/MPAPI.h
@@ -6,17 +6,17 @@
 #if !defined(MPAPI_h_)
 #define MPAPI_h_
 
 #include <stdint.h>
 
 namespace MPAPI {
 
 enum ColorFormat {
-  YCbCr,
+  I420,
   RGB565
 };
 
 /*
  * A callback for the plugin to use to request a buffer owned by gecko. This can
  * save us a copy or two down the line.
  */
 class BufferCallback {
--- a/content/media/plugins/MediaPluginReader.cpp
+++ b/content/media/plugins/MediaPluginReader.cpp
@@ -12,16 +12,17 @@
 #include "MediaPluginHost.h"
 #include "MediaDecoderStateMachine.h"
 #include "ImageContainer.h"
 #include "AbstractMediaDecoder.h"
 
 namespace mozilla {
 
 typedef mozilla::layers::Image Image;
+typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
 MediaPluginReader::MediaPluginReader(AbstractMediaDecoder *aDecoder,
                                      const nsACString& aContentType) :
   MediaDecoderReader(aDecoder),
   mType(aContentType),
   mPlugin(nullptr),
   mHasAudio(false),
   mHasVideo(false),
@@ -164,17 +165,17 @@ bool MediaPluginReader::DecodeVideoFrame
     }
 
     if (frame.mSize == 0)
       return true;
 
     currentImage = bufferCallback.GetImage();
     int64_t pos = mDecoder->GetResource()->Tell();
     nsIntRect picture = mPicture;
- 
+
     nsAutoPtr<VideoData> v;
     if (currentImage) {
       gfxIntSize frameSize = currentImage->GetSize();
       if (frameSize.width != mInitialFrame.width ||
           frameSize.height != mInitialFrame.height) {
         // Frame size is different from what the container reports. This is legal,
         // and we will preserve the ratio of the crop rectangle as it
         // was reported relative to the picture size reported by the container.
@@ -332,42 +333,88 @@ nsresult MediaPluginReader::Seek(int64_t
 
 MediaPluginReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer) :
   mImageContainer(aImageContainer)
 {
 }
 
 void *
 MediaPluginReader::ImageBufferCallback::operator()(size_t aWidth, size_t aHeight,
-                                                     MPAPI::ColorFormat aColorFormat)
+                                                   MPAPI::ColorFormat aColorFormat)
 {
   if (!mImageContainer) {
     NS_WARNING("No image container to construct an image");
     return nullptr;
   }
 
-  nsRefPtr<Image> rgbImage;
+  nsRefPtr<Image> image;
   switch(aColorFormat) {
     case MPAPI::RGB565:
-      rgbImage = mozilla::layers::CreateSharedRGBImage(mImageContainer,
-                                                       nsIntSize(aWidth, aHeight),
-                                                       gfxImageFormatRGB16_565);
-      if (!rgbImage) {
+      image = mozilla::layers::CreateSharedRGBImage(mImageContainer,
+                                                    nsIntSize(aWidth, aHeight),
+                                                    gfxImageFormatRGB16_565);
+      if (!image) {
         NS_WARNING("Could not create rgb image");
         return nullptr;
       }
 
-      mImage = rgbImage;
-      return rgbImage->AsSharedImage()->GetBuffer();
-    case MPAPI::YCbCr:
+      mImage = image;
+      return image->AsSharedImage()->GetBuffer();
+    case MPAPI::I420:
+      return CreateI420Image(aWidth, aHeight);
     default:
       NS_NOTREACHED("Color format not supported");
       return nullptr;
   }
 }
 
+uint8_t *
+MediaPluginReader::ImageBufferCallback::CreateI420Image(size_t aWidth,
+                                                        size_t aHeight)
+{
+  ImageFormat format = PLANAR_YCBCR;
+
+  mImage = mImageContainer->CreateImage(&format, 1 /* numFormats */);
+  PlanarYCbCrImage *yuvImage = static_cast<PlanarYCbCrImage *>(mImage.get());
+
+  if (!yuvImage) {
+    NS_WARNING("Could not create I420 image");
+    return nullptr;
+  }
+
+  size_t frameSize = aWidth * aHeight;
+
+  // Allocate enough for one full resolution Y plane
+  // and two quarter resolution Cb/Cr planes.
+  uint8_t *buffer = yuvImage->AllocateAndGetNewBuffer(frameSize * 3 / 2);
+
+  mozilla::layers::PlanarYCbCrData frameDesc;
+
+  frameDesc.mYChannel = buffer;
+  frameDesc.mCbChannel = buffer + frameSize;
+  frameDesc.mCrChannel = buffer + frameSize * 5 / 4;
+
+  frameDesc.mYSize = gfxIntSize(aWidth, aHeight);
+  frameDesc.mCbCrSize = gfxIntSize(aWidth / 2, aHeight / 2);
+
+  frameDesc.mYStride = aWidth;
+  frameDesc.mCbCrStride = aWidth / 2;
+
+  frameDesc.mYSkip = 0;
+  frameDesc.mCbSkip = 0;
+  frameDesc.mCrSkip = 0;
+
+  frameDesc.mPicX = 0;
+  frameDesc.mPicY = 0;
+  frameDesc.mPicSize = gfxIntSize(aWidth, aHeight);
+
+  yuvImage->SetDataNoCopy(frameDesc);
+
+  return buffer;
+}
+
 already_AddRefed<Image>
 MediaPluginReader::ImageBufferCallback::GetImage()
 {
   return mImage.forget();
 }
 
 } // namespace mozilla
--- a/content/media/plugins/MediaPluginReader.h
+++ b/content/media/plugins/MediaPluginReader.h
@@ -60,24 +60,30 @@ public:
   virtual bool HasVideo()
   {
     return mHasVideo;
   }
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
+
   class ImageBufferCallback : public MPAPI::BufferCallback {
     typedef mozilla::layers::Image Image;
+
   public:
     ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
     void *operator()(size_t aWidth, size_t aHeight,
                      MPAPI::ColorFormat aColorFormat) MOZ_OVERRIDE;
     already_AddRefed<Image> GetImage();
+
   private:
+    uint8_t *CreateI420Image(size_t aWidth, size_t aHeight);
+
     mozilla::layers::ImageContainer *mImageContainer;
     nsRefPtr<Image> mImage;
   };
+
 };
 
 } // namespace mozilla
 
 #endif
--- a/media/omx-plugin/Makefile.in
+++ b/media/omx-plugin/Makefile.in
@@ -48,15 +48,17 @@ INCLUDES        += \
                 -I$(ANDROID_SOURCE)/hardware/libhardware/include \
                 $(NULL)
 else
 EXTRA_DSO_LDOPTS += \
 		-L$(DEPTH)/media/omx-plugin/lib/ics/libutils \
 		-lutils \
 		-L$(DEPTH)/media/omx-plugin/lib/ics/libstagefright \
 		-lstagefright \
+		-L$(DEPTH)/media/omx-plugin/lib/ics/libvideoeditorplayer \
+		-lvideoeditorplayer \
 		$(NULL)
 
 INCLUDES        += \
                 -I$(srcdir)/include/ics \
                 -I$(srcdir)/include/ics/media/stagefright/openmax \
                 $(NULL)
 endif
--- a/media/omx-plugin/OmxPlugin.cpp
+++ b/media/omx-plugin/OmxPlugin.cpp
@@ -32,16 +32,24 @@
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args)
 
 #if defined(MOZ_ANDROID_FROYO) || defined(MOZ_ANDROID_GB)
 // Android versions 2.x.x have common API differences
 #define MOZ_ANDROID_V2_X_X
 #endif
 
+#if !defined(MOZ_ANDROID_V2_X_X) && !defined(MOZ_ANDROID_HC)
+#define MOZ_ANDROID_V4_OR_ABOVE
+#endif
+
+#if defined(MOZ_ANDROID_V4_OR_ABOVE)
+#include <I420ColorConverter.h>
+#endif
+
 using namespace MPAPI;
 
 #if !defined(MOZ_STAGEFRIGHT_OFF_T)
 #define MOZ_STAGEFRIGHT_OFF_T off64_t
 #endif
 
 using namespace android;
 
@@ -63,16 +71,18 @@ class OmxDecoder {
   sp<MediaSource> mAudioSource;
   int32_t mVideoWidth;
   int32_t mVideoHeight;
   int32_t mVideoColorFormat;
   int32_t mVideoStride;
   int32_t mVideoSliceHeight;
   int32_t mVideoCropLeft;
   int32_t mVideoCropTop;
+  int32_t mVideoCropRight;
+  int32_t mVideoCropBottom;
   int32_t mVideoRotation;
   int32_t mAudioChannels;
   int32_t mAudioSampleRate;
   int64_t mDurationUs;
   MediaBuffer *mVideoBuffer;
   VideoFrame mVideoFrame;
   MediaBuffer *mAudioBuffer;
   AudioFrame mAudioFrame;
@@ -87,16 +97,17 @@ class OmxDecoder {
   void ToVideoFrame_YUV420Planar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void ToVideoFrame_CbYCrY(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void ToVideoFrame_YUV420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void ToVideoFrame_YVU420SemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void ToVideoFrame_YUV420PackedSemiPlanar(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   void ToVideoFrame_YVU420PackedSemiPlanar32m4ka(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame);
   bool ToVideoFrame_RGB565(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
   bool ToVideoFrame_ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
+  bool ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
   bool ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback);
   bool ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize,
                     int32_t aAudioChannels, int32_t aAudioSampleRate);
 public:
   OmxDecoder(PluginHost *aPluginHost, Decoder *aDecoder);
   ~OmxDecoder();
 
   bool Init();
@@ -134,16 +145,18 @@ OmxDecoder::OmxDecoder(PluginHost *aPlug
   mDecoder(aDecoder),
   mVideoWidth(0),
   mVideoHeight(0),
   mVideoColorFormat(0),
   mVideoStride(0),
   mVideoSliceHeight(0),
   mVideoCropLeft(0),
   mVideoCropTop(0),
+  mVideoCropRight(0),
+  mVideoCropBottom(0),
   mVideoRotation(0),
   mAudioChannels(-1),
   mAudioSampleRate(-1),
   mDurationUs(-1),
   mVideoBuffer(nullptr),
   mAudioBuffer(nullptr),
   mColorConverter(nullptr),
   mAudioMetadataRead(false)
@@ -221,56 +234,77 @@ static uint32_t GetVideoCreationFlags(Pl
   }
 
   flags |= DEFAULT_STAGEFRIGHT_FLAGS;
 
   return static_cast<uint32_t>(flags);
 #endif
 }
 
+static bool
+IsColorFormatSupported(OMX_COLOR_FORMATTYPE aColorFormat)
+{
+  switch (aColorFormat) {
+    case OMX_COLOR_FormatCbYCrY:
+    case OMX_COLOR_FormatYUV420Planar:
+    case OMX_COLOR_FormatYUV420SemiPlanar:
+    case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka:
+    case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
+    case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
+      LOG("Colour format %#x supported natively.", aColorFormat);
+      return true;
+    default:
+      break;
+  }
+
+#if !defined(MOZ_ANDROID_HC)
+  if (ColorConverter(aColorFormat, OMX_COLOR_Format16bitRGB565).isValid()) {
+    LOG("Colour format %#x supported by Android ColorConverter.", aColorFormat);
+    return true;
+  }
+#endif
+
+#if defined(MOZ_ANDROID_V4_OR_ABOVE)
+  I420ColorConverter yuvConverter;
+
+  if (yuvConverter.isLoaded() &&
+      yuvConverter.getDecoderOutputFormat() == aColorFormat) {
+    LOG("Colour format %#x supported by Android I420ColorConverter.", aColorFormat);
+    return true;
+  }
+#endif
+
+  return false;
+}
+
 static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
                                          const sp<IOMX>& aOmx,
                                          const sp<MediaSource>& aVideoTrack)
 {
   uint32_t flags = GetVideoCreationFlags(aPluginHost);
+
   if (flags == DEFAULT_STAGEFRIGHT_FLAGS) {
     // Let Stagefright choose hardware or software decoder.
     sp<MediaSource> videoSource = OMXCodec::Create(aOmx, aVideoTrack->getFormat(),
                                                    false, aVideoTrack, nullptr, flags);
     if (videoSource == nullptr)
       return nullptr;
 
     // Now that OMXCodec has parsed the video's AVCDecoderConfigurationRecord,
     // check whether we know how to decode this video.
     int32_t videoColorFormat;
     if (videoSource->getFormat()->findInt32(kKeyColorFormat, &videoColorFormat)) {
-      switch (videoColorFormat) {
-        // We know how to convert these color formats.
-        case OMX_COLOR_FormatCbYCrY:
-        case OMX_COLOR_FormatYUV420Planar:
-        case OMX_COLOR_FormatYUV420SemiPlanar:
-        case OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka:
-        case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
-        case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
-          // Use the decoder Stagefright chose for us!
-          return videoSource;
 
-        // Use software decoder for color formats we don't know how to convert.
-        default:
-#ifndef MOZ_ANDROID_HC
-          if (ColorConverter((OMX_COLOR_FORMATTYPE)videoColorFormat,
-                             OMX_COLOR_Format16bitRGB565).isValid()) {
-            return videoSource;
-          }
-#endif
-          // We need to implement a ToVideoFrame_*() color conversion
-          // function for this video color format.
-          LOG("Unknown video color format: %#x", videoColorFormat);
-          break;
+      if (IsColorFormatSupported((OMX_COLOR_FORMATTYPE)videoColorFormat)) {
+        return videoSource;
       }
+
+      // We need to implement a ToVideoFrame_*() color conversion
+      // function for this video color format.
+      LOG("Unknown video color format: %#x", videoColorFormat);
     } else {
       LOG("Video color format not found");
     }
 
     // Throw away the videoSource and try again with new flags.
     LOG("Falling back to software decoder");
     videoSource.clear();
 #if defined(MOZ_ANDROID_V2_X_X)
@@ -467,39 +501,38 @@ bool OmxDecoder::SetVideoFormat() {
     return false;
   }
 
   if (mVideoSliceHeight <= 0) {
     LOG("slice height %d must be positive", mVideoSliceHeight);
     return false;
   }
 
-  int32_t cropRight, cropBottom;
   // Gingerbread does not support the kKeyCropRect key
 #if !defined(MOZ_ANDROID_V2_X_X)
   if (!format->findRect(kKeyCropRect, &mVideoCropLeft, &mVideoCropTop,
-                                      &cropRight, &cropBottom)) {
+                                      &mVideoCropRight, &mVideoCropBottom)) {
 #endif
     mVideoCropLeft = 0;
     mVideoCropTop = 0;
-    cropRight = mVideoStride - 1;
-    cropBottom = mVideoSliceHeight - 1;
+    mVideoCropRight = mVideoStride - 1;
+    mVideoCropBottom = mVideoSliceHeight - 1;
     LOG("crop rect not available, assuming no cropping");
 #if !defined(MOZ_ANDROID_V2_X_X)
   }
 #endif
 
-  if (mVideoCropLeft < 0 || mVideoCropLeft >= cropRight || cropRight >= mVideoStride ||
-      mVideoCropTop < 0 || mVideoCropTop >= cropBottom || cropBottom >= mVideoSliceHeight) {
-    LOG("invalid crop rect %d,%d-%d,%d", mVideoCropLeft, mVideoCropTop, cropRight, cropBottom);
+  if (mVideoCropLeft < 0 || mVideoCropLeft >= mVideoCropRight || mVideoCropRight >= mVideoStride ||
+      mVideoCropTop < 0 || mVideoCropTop >= mVideoCropBottom || mVideoCropBottom >= mVideoSliceHeight) {
+    LOG("invalid crop rect %d,%d-%d,%d", mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom);
     return false;
   }
 
-  mVideoWidth = cropRight - mVideoCropLeft + 1;
-  mVideoHeight = cropBottom - mVideoCropTop + 1;
+  mVideoWidth = mVideoCropRight - mVideoCropLeft + 1;
+  mVideoHeight = mVideoCropBottom - mVideoCropTop + 1;
   MOZ_ASSERT(mVideoWidth > 0 && mVideoWidth <= mVideoStride);
   MOZ_ASSERT(mVideoHeight > 0 && mVideoHeight <= mVideoSliceHeight);
 
 #if !defined(MOZ_ANDROID_FROYO)
   if (!format->findInt32(kKeyRotation, &mVideoRotation)) {
 #endif
     mVideoRotation = 0;
 #if !defined(MOZ_ANDROID_FROYO)
@@ -510,17 +543,17 @@ bool OmxDecoder::SetVideoFormat() {
   if (mVideoRotation != 0 && mVideoRotation != 90 &&
       mVideoRotation != 180 && mVideoRotation != 270) {
     LOG("invalid rotation %d, assuming 0", mVideoRotation);
   }
 
   LOG("width: %d height: %d component: %s format: %#x stride: %d sliceHeight: %d rotation: %d crop: %d,%d-%d,%d",
       mVideoWidth, mVideoHeight, componentName, mVideoColorFormat,
       mVideoStride, mVideoSliceHeight, mVideoRotation,
-      mVideoCropLeft, mVideoCropTop, cropRight, cropBottom);
+      mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom);
 
   return true;
 }
 
 bool OmxDecoder::SetAudioFormat() {
   // If the format changed, update our cached info.
   if (!mAudioSource->getFormat()->findInt32(kKeyChannelCount, &mAudioChannels) ||
       !mAudioSource->getFormat()->findInt32(kKeySampleRate, &mAudioSampleRate)) {
@@ -677,16 +710,50 @@ bool OmxDecoder::ToVideoFrame_ColorConve
                            buffer, mVideoWidth, mVideoHeight,
                            0, 0, mVideoWidth - 1, mVideoHeight - 1);
 #endif
 
   return true;
 #endif
 }
 
+bool OmxDecoder::ToVideoFrame_I420ColorConverter(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback)
+{
+#if defined(MOZ_ANDROID_V4_OR_ABOVE)
+  I420ColorConverter yuvConverter;
+
+  if (!yuvConverter.isLoaded()) {
+    return false;
+  }
+
+  if (yuvConverter.getDecoderOutputFormat() != mVideoColorFormat) {
+    return false;
+  }
+
+  void *buffer = (*aBufferCallback)(mVideoWidth, mVideoHeight, MPAPI::I420);
+
+  ARect crop = { mVideoCropLeft, mVideoCropTop, mVideoCropRight, mVideoCropBottom };
+  int result = yuvConverter.convertDecoderOutputToI420(aData,
+                                                       mVideoWidth,
+                                                       mVideoHeight,
+                                                       crop,
+                                                       buffer);
+
+  // result is 0 on success, -1 otherwise.
+  if (result == OK) {
+    aFrame->mTimeUs = aTimeUs;
+    aFrame->mSize = mVideoWidth * mVideoHeight * 3 / 2;
+  }
+
+  return result == OK;
+#else
+  return false;
+#endif
+}
+
 bool OmxDecoder::ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame, BufferCallback *aBufferCallback) {
   switch (mVideoColorFormat) {
 // Froyo support is best handled with the android color conversion code. I
 // get corrupted videos when using our own routines below.
 #if !defined(MOZ_ANDROID_FROYO)
   case OMX_COLOR_FormatYUV420Planar: // e.g. Asus Transformer, Stagefright's software decoder
     ToVideoFrame_YUV420Planar(aFrame, aTimeUs, aData, aSize, aKeyFrame);
     break;
@@ -705,17 +772,18 @@ bool OmxDecoder::ToVideoFrame(VideoFrame
   case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: // e.g. Galaxy Nexus
     ToVideoFrame_YUV420PackedSemiPlanar(aFrame, aTimeUs, aData, aSize, aKeyFrame);
     break;
   case OMX_COLOR_Format16bitRGB565:
     return ToVideoFrame_RGB565(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback);
     break;
 #endif
   default:
-    if (!ToVideoFrame_ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback)) {
+    if (!ToVideoFrame_ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback) &&
+        !ToVideoFrame_I420ColorConverter(aFrame, aTimeUs, aData, aSize, aKeyFrame, aBufferCallback)) {
       LOG("Unknown video color format: %#x", mVideoColorFormat);
       return false;
     }
   }
   return true;
 }
 
 bool OmxDecoder::ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, int32_t aAudioChannels, int32_t aAudioSampleRate)
new file mode 100644
--- /dev/null
+++ b/media/omx-plugin/include/ics/I420ColorConverter.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef I420_COLOR_CONVERTER_H
+#define I420_COLOR_CONVERTER_H
+
+#include <II420ColorConverter.h>
+
+// This is a wrapper around the I420 color converter functions in
+// II420ColorConverter, which is loaded from a shared library.
+class I420ColorConverter: public II420ColorConverter {
+public:
+    I420ColorConverter();
+    ~I420ColorConverter();
+
+    // Returns true if the converter functions are successfully loaded.
+    bool isLoaded();
+private:
+    void* mHandle;
+};
+
+#endif /* I420_COLOR_CONVERTER_H */
new file mode 100644
--- /dev/null
+++ b/media/omx-plugin/include/ics/II420ColorConverter.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef II420_COLOR_CONVERTER_H
+
+#define II420_COLOR_CONVERTER_H
+
+#include <stdint.h>
+#include <android/rect.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct II420ColorConverter {
+
+    /*
+     * getDecoderOutputFormat
+     * Returns the color format (OMX_COLOR_FORMATTYPE) of the decoder output.
+     * If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed,
+     * and convertDecoderOutputToI420() can be a no-op.
+     */
+    int (*getDecoderOutputFormat)();
+
+    /*
+     * convertDecoderOutputToI420
+     * @Desc     Converts from the decoder output format to I420 format.
+     * @note     Caller (e.g. VideoEditor) owns the buffers
+     * @param    decoderBits   (IN) Pointer to the buffer contains decoder output
+     * @param    decoderWidth  (IN) Buffer width, as reported by the decoder
+     *                              metadata (kKeyWidth)
+     * @param    decoderHeight (IN) Buffer height, as reported by the decoder
+     *                              metadata (kKeyHeight)
+     * @param    decoderRect   (IN) The rectangle of the actual frame, as
+     *                              reported by decoder metadata (kKeyCropRect)
+     * @param    dstBits      (OUT) Pointer to the output I420 buffer
+     * @return   -1 Any error
+     * @return   0  No Error
+     */
+    int (*convertDecoderOutputToI420)(
+        void* decoderBits, int decoderWidth, int decoderHeight,
+        ARect decoderRect, void* dstBits);
+
+    /*
+     * getEncoderIntputFormat
+     * Returns the color format (OMX_COLOR_FORMATTYPE) of the encoder input.
+     * If it is I420 (OMX_COLOR_FormatYUV420Planar), no conversion is needed,
+     * and convertI420ToEncoderInput() and getEncoderInputBufferInfo() can
+     * be no-ops.
+     */
+    int (*getEncoderInputFormat)();
+
+    /* convertI420ToEncoderInput
+     * @Desc     This function converts from I420 to the encoder input format
+     * @note     Caller (e.g. VideoEditor) owns the buffers
+     * @param    srcBits       (IN) Pointer to the input I420 buffer
+     * @param    srcWidth      (IN) Width of the I420 frame
+     * @param    srcHeight     (IN) Height of the I420 frame
+     * @param    encoderWidth  (IN) Encoder buffer width, as calculated by
+     *                              getEncoderBufferInfo()
+     * @param    encoderHeight (IN) Encoder buffer height, as calculated by
+     *                              getEncoderBufferInfo()
+     * @param    encoderRect   (IN) Rect coordinates of the actual frame inside
+     *                              the encoder buffer, as calculated by
+     *                              getEncoderBufferInfo().
+     * @param    encoderBits  (OUT) Pointer to the output buffer. The size of
+     *                              this buffer is calculated by
+     *                              getEncoderBufferInfo()
+     * @return   -1 Any error
+     * @return   0  No Error
+     */
+    int (*convertI420ToEncoderInput)(
+        void* srcBits, int srcWidth, int srcHeight,
+        int encoderWidth, int encoderHeight, ARect encoderRect,
+        void* encoderBits);
+
+    /* getEncoderInputBufferInfo
+     * @Desc     This function returns metadata for the encoder input buffer
+     *           based on the actual I420 frame width and height.
+     * @note     This API should be be used to obtain the necessary information
+     *           before calling convertI420ToEncoderInput().
+     *           VideoEditor knows only the width and height of the I420 buffer,
+     *           but it also needs know the width, height, and size of the
+     *           encoder input buffer. The encoder input buffer width and height
+     *           are used to set the metadata for the encoder.
+     * @param    srcWidth      (IN) Width of the I420 frame
+     * @param    srcHeight     (IN) Height of the I420 frame
+     * @param    encoderWidth  (OUT) Encoder buffer width needed
+     * @param    encoderHeight (OUT) Encoder buffer height needed
+     * @param    encoderRect   (OUT) Rect coordinates of the actual frame inside
+     *                               the encoder buffer
+     * @param    encoderBufferSize  (OUT) The size of the buffer that need to be
+     *                              allocated by the caller before invoking
+     *                              convertI420ToEncoderInput().
+     * @return   -1 Any error
+     * @return   0  No Error
+     */
+    int (*getEncoderInputBufferInfo)(
+        int srcWidth, int srcHeight,
+        int* encoderWidth, int* encoderHeight,
+        ARect* encoderRect, int* encoderBufferSize);
+
+} II420ColorConverter;
+
+/* The only function that the shared library needs to expose: It fills the
+   function pointers in II420ColorConverter */
+void getI420ColorConverter(II420ColorConverter *converter);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif  // II420_COLOR_CONVERTER_H
+
new file mode 100644
--- /dev/null
+++ b/media/omx-plugin/lib/ics/libvideoeditorplayer/Makefile.in
@@ -0,0 +1,25 @@
+# Copyright 2012 Mozilla Foundation and Mozilla contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Don't use STL wrappers; this isn't Gecko code
+STL_FLAGS =
+
+# must link statically with the CRT; this isn't Gecko code
+USE_STATIC_LIBS = 1
+
+include $(topsrcdir)/config/rules.mk
+
+INCLUDES        += \
+                -I$(topsrcdir)/media/omx-plugin/include/ics \
+                $(NULL)
new file mode 100644
--- /dev/null
+++ b/media/omx-plugin/lib/ics/libvideoeditorplayer/libvideoeditorplayer.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "mozilla/Types.h"
+#include "I420ColorConverter.h"
+
+MOZ_EXPORT
+I420ColorConverter::I420ColorConverter()
+{
+}
+
+MOZ_EXPORT
+I420ColorConverter::~I420ColorConverter()
+{
+}
+
+MOZ_EXPORT bool
+I420ColorConverter::isLoaded()
+{
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/media/omx-plugin/lib/ics/libvideoeditorplayer/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+NO_DIST_INSTALL = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+    SOURCES += [
+        'libvideoeditorplayer.cpp',
+    ]
+
+LIBRARY_NAME = 'videoeditorplayer'
+
+FORCE_SHARED_LIB = True
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -70,16 +70,17 @@ if CONFIG['MOZ_WEBRTC']:
         'media/mtransport/build',
         'media/mtransport/standalone',
     ])
 
 if CONFIG['MOZ_OMX_PLUGIN']:
     add_tier_dir('platform', [
         'media/omx-plugin/lib/ics/libutils',
         'media/omx-plugin/lib/ics/libstagefright',
+        'media/omx-plugin/lib/ics/libvideoeditorplayer',
         'media/omx-plugin/lib/gb/libutils',
         'media/omx-plugin/lib/gb/libstagefright',
         'media/omx-plugin/lib/gb/libstagefright_color_conversion',
         'media/omx-plugin/lib/gb235/libstagefright',
         'media/omx-plugin/lib/froyo/libstagefright',
         'media/omx-plugin',
         'media/omx-plugin/gb',
         'media/omx-plugin/gb235',