Bug 757341 - Camera direct texturing with async video. r=cjones
authorKan-Ru Chen <kanru@kanru.info>
Fri, 17 Aug 2012 18:56:55 +0800
changeset 108638 a4c49da8ed4e3f2c8df09f0ca7ad14a2cb7d0d0e
parent 108637 e1ebc55a599e4fda72aaab6c50de13c26a054d80
child 108639 22ce0a41fe217d074a2837ef9edbd04a70f2658d
push id214
push userakeybl@mozilla.com
push dateWed, 14 Nov 2012 20:38:59 +0000
treeherdermozilla-release@c8b08ec8e1aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs757341
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 757341 - Camera direct texturing with async video. r=cjones
dom/camera/GonkCameraControl.cpp
dom/camera/GonkCameraControl.h
dom/camera/GonkCameraHwMgr.cpp
dom/camera/GonkCameraHwMgr.h
dom/camera/GonkCameraPreview.cpp
dom/camera/GonkCameraPreview.h
dom/camera/GonkNativeWindow.cpp
dom/camera/GonkNativeWindow.h
dom/camera/Makefile.in
gfx/layers/GonkIOSurfaceImage.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/ImageBridgeParent.cpp
gfx/layers/ipc/ImageBridgeParent.h
gfx/layers/ipc/ImageContainerChild.cpp
gfx/layers/ipc/ImageContainerChild.h
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/layers/ipc/PGrallocBuffer.ipdl
gfx/layers/ipc/PImageBridge.ipdl
gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
gfx/layers/ipc/ShadowLayerUtilsGralloc.h
gfx/layers/ipc/SharedImageUtils.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -10,16 +10,17 @@
  * 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.
  */
 
 #include <string.h>
+#include "base/basictypes.h"
 #include "libcameraservice/CameraHardwareInterface.h"
 #include "camera/CameraParameters.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "nsMemory.h"
 #include "jsapi.h"
 #include "nsThread.h"
 #include "nsPrintfCString.h"
@@ -562,24 +563,24 @@ nsGonkCameraControl::StartRecordingImpl(
 
 nsresult
 nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 void
-nsGonkCameraControl::ReceiveFrame(PRUint8* aData, PRUint32 aLength)
+nsGonkCameraControl::ReceiveFrame(layers::GraphicBufferLocked *aBuffer)
 {
   nsCOMPtr<CameraPreview> preview = mPreview;
 
   if (preview) {
     GonkCameraPreview* p = static_cast<GonkCameraPreview* >(preview.get());
     MOZ_ASSERT(p);
-    p->ReceiveFrame(aData, aLength);
+    p->ReceiveFrame(aBuffer);
   }
 }
 
 // Gonk callback handlers.
 namespace mozilla {
 
 void
 ReceiveImage(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength)
@@ -589,14 +590,14 @@ ReceiveImage(nsGonkCameraControl* gc, PR
 
 void
 AutoFocusComplete(nsGonkCameraControl* gc, bool success)
 {
   gc->AutoFocusComplete(success);
 }
 
 void
-ReceiveFrame(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength)
+ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked *aBuffer)
 {
-  gc->ReceiveFrame(aData, aLength);
+  gc->ReceiveFrame(aBuffer);
 }
 
 } // namespace mozilla
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -21,32 +21,36 @@
 #include "prrwlock.h"
 #include "CameraControl.h"
 
 #define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
 namespace mozilla {
 
+namespace layers {
+class GraphicBufferLocked;
+}
+
 class nsGonkCameraControl : public nsCameraControl
 {
 public:
   nsGonkCameraControl(PRUint32 aCameraId, nsIThread* aCameraThread);
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(PRUint32 aKey);
   double GetParameterDouble(PRUint32 aKey);
   void GetParameter(PRUint32 aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(PRUint32 aKey, const char* aValue);
   void SetParameter(PRUint32 aKey, double aValue);
   void SetParameter(PRUint32 aKey, const nsTArray<dom::CameraRegion>& aRegions);
   void PushParameters();
 
-  void ReceiveFrame(PRUint8 *aData, PRUint32 aLength);
+  void ReceiveFrame(layers::GraphicBufferLocked* aBuffer);
 
 protected:
   ~nsGonkCameraControl();
 
   nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
   nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
   nsresult TakePictureImpl(TakePictureTask* aTakePicture);
   nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
@@ -64,13 +68,13 @@ protected:
 private:
   nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
   nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;
 };
 
 // camera driver callbacks
 void ReceiveImage(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength);
 void AutoFocusComplete(nsGonkCameraControl* gc, bool success);
-void ReceiveFrame(nsGonkCameraControl* gc, PRUint8* aData, PRUint32 aLength);
+void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERACONTROL_H
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -9,24 +9,27 @@
  *
  * 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.
  */
 
+#include "base/basictypes.h"
 #include "nsDebug.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkNativeWindow.h"
 
 #define DOM_CAMERA_LOG_LEVEL        3
 #include "CameraCommon.h"
 
 using namespace mozilla;
+using namespace mozilla::layers;
+using namespace android;
 
 #if GIHM_TIMING_RECEIVEFRAME
 #define INCLUDE_TIME_H                  1
 #endif
 #if GIHM_TIMING_OVERALL
 #define INCLUDE_TIME_H                  1
 #endif
 
@@ -54,16 +57,27 @@ GonkCameraHardware::GonkCameraHardware(G
   , mNumFrames(0)
   , mTarget(aTarget)
   , mInitialized(false)
 {
   DOM_CAMERA_LOGI( "%s: this = %p (aTarget = %p)\n", __func__, (void* )this, (void* )aTarget );
   init();
 }
 
+void
+GonkCameraHardware::OnNewFrame()
+{
+  if (mClosing) {
+    return;
+  }
+  GonkNativeWindow* window = static_cast<GonkNativeWindow*>(mWindow.get());
+  nsRefPtr<GraphicBufferLocked> buffer = window->getCurrentBuffer();
+  ReceiveFrame(mTarget, buffer);
+}
+
 // Android data callback
 void
 GonkCameraHardware::DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser)
 {
   GonkCameraHardware* hw = GetHardware((PRUint32)aUser);
   if (!hw) {
     DOM_CAMERA_LOGW("%s:aUser = %d resolved to no camera hw\n", __func__, (PRUint32)aUser);
     return;
@@ -71,17 +85,17 @@ GonkCameraHardware::DataCallback(int32_t
   if (hw->mClosing) {
     return;
   }
 
   GonkCamera* camera = hw->mTarget;
   if (camera) {
     switch (aMsgType) {
       case CAMERA_MSG_PREVIEW_FRAME:
-        ReceiveFrame(camera, (PRUint8*)aDataPtr->pointer(), aDataPtr->size());
+        // Do nothing
         break;
 
       case CAMERA_MSG_COMPRESSED_IMAGE:
         ReceiveImage(camera, (PRUint8*)aDataPtr->pointer(), aDataPtr->size());
         break;
 
       default:
         DOM_CAMERA_LOGE("Unhandled data callback event %d\n", aMsgType);
@@ -144,17 +158,17 @@ GonkCameraHardware::init()
   char cameraDeviceName[4];
   snprintf(cameraDeviceName, sizeof(cameraDeviceName), "%d", mCamera);
   mHardware = new CameraHardwareInterface(cameraDeviceName);
   if (mHardware->initialize(&mModule->common) != OK) {
     mHardware.clear();
     return;
   }
 
-  mWindow = new android::GonkNativeWindow();
+  mWindow = new GonkNativeWindow(this);
 
   if (sHwHandle == 0) {
     sHwHandle = 1;  // don't use 0
   }
   mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, NULL, (void*)sHwHandle);
 
   // initialize the local camera parameter database
   mParams = mHardware->getParameters();
@@ -183,16 +197,18 @@ GonkCameraHardware::ReleaseHandle(PRUint
   }
 
   DOM_CAMERA_LOGI("%s: before: sHwHandle = %d\n", __func__, sHwHandle);
   sHwHandle += 1; // invalidate old handles before deleting
   hw->mClosing = true;
   hw->mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
   hw->mHardware->stopPreview();
   hw->mHardware->release();
+  GonkNativeWindow* window = static_cast<GonkNativeWindow*>(hw->mWindow.get());
+  window->abandon();
   DOM_CAMERA_LOGI("%s: after: sHwHandle = %d\n", __func__, sHwHandle);
   delete hw;     // destroy the camera hardware instance
 }
 
 PRUint32
 GonkCameraHardware::GetHandle(GonkCamera* aTarget, PRUint32 aCamera)
 {
   ReleaseHandle(sHwHandle);
@@ -363,18 +379,16 @@ GonkCameraHardware::PullParameters(PRUin
   }
 }
 
 int
 GonkCameraHardware::StartPreview()
 {
   const char* format;
 
-  mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-
   DOM_CAMERA_LOGI("Preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS));
 
   // try to set preferred image format and frame rate
   const char* const PREVIEW_FORMAT = "yuv420p";
   const char* const BAD_PREVIEW_FORMAT = "yuv420sp";
   mParams.setPreviewFormat(PREVIEW_FORMAT);
   mParams.setPreviewFrameRate(mFps);
   mHardware->setParameters(mParams);
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -21,38 +21,42 @@
 #include "binder/IMemory.h"
 #include "mozilla/ReentrantMonitor.h"
 
 #include "GonkCameraControl.h"
 
 #define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
+#include "GonkNativeWindow.h"
+
 // config
 #define GIHM_TIMING_RECEIVEFRAME    0
 #define GIHM_TIMING_OVERALL         1
 
 using namespace mozilla;
 using namespace android;
 
 namespace mozilla {
 
 typedef class nsGonkCameraControl GonkCamera;
 
-class GonkCameraHardware
+class GonkCameraHardware : GonkNativeWindowNewFrameCallback
 {
 protected:
   GonkCameraHardware(GonkCamera* aTarget, PRUint32 aCamera);
   ~GonkCameraHardware();
   void init();
 
   static void                   DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser);
   static void                   NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser);
 
 public:
+  virtual void OnNewFrame() MOZ_OVERRIDE;
+
   static void                   ReleaseHandle(PRUint32 aHwHandle);
   static PRUint32               GetHandle(GonkCamera* aTarget, PRUint32 aCamera);
   static PRUint32               GetFps(PRUint32 aHwHandle);
   static void                   GetPreviewSize(PRUint32 aHwHandle, PRUint32* aWidth, PRUint32* aHeight);
   static void                   SetPreviewSize(PRUint32 aHwHandle, PRUint32 aWidth, PRUint32 aHeight);
   static int                    AutoFocus(PRUint32 aHwHandle);
   static void                   CancelAutoFocus(PRUint32 aHwHandle);
   static int                    TakePicture(PRUint32 aHwHandle);
--- a/dom/camera/GonkCameraPreview.cpp
+++ b/dom/camera/GonkCameraPreview.cpp
@@ -9,174 +9,62 @@
  *
  * 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.
  */
 
+#include "base/basictypes.h"
 #include "VideoUtils.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkCameraPreview.h"
 
 #define DOM_CAMERA_LOG_LEVEL  2
 #include "CameraCommon.h"
 
+#include "GonkIOSurfaceImage.h"
+
 using namespace mozilla;
 
-/**
- * This big macro takes two 32-bit input blocks of interlaced u and
- * v data (from a yuv420sp frame) in 's0' and 's1', and deinterlaces
- * them into pairs of contiguous 32-bit blocks, the u plane data in
- * 'u', and the v plane data in 'v' (i.e. for a yuv420p frame).
- *
- * yuv420sp:
- *  [ y-data ][ uv-data ]
- *    [ uv-data ]: [u0][v0][u1][v1][u2][v2]...
- *
- * yuv420p:
- *  [ y-data ][ u-data ][ v-data ]
- *    [ u-data ]: [u0][u1][u2]...
- *    [ v-data ]: [v0][v1][v2]...
- *
- * Doing this in 32-bit blocks is significantly faster than using
- * byte-wise operations on ARM.  (In some cases, the byte-wise
- * de-interlacing can be too slow to keep up with the preview frames
- * coming from the driver.
- */
-#define DEINTERLACE( u, v, s0, s1 )                             \
-  u = ( (s0) & 0xFF00UL ) >> 8 | ( (s0) & 0xFF000000UL ) >> 16; \
-  u |= ( (s1) & 0xFF00UL ) << 8 | ( (s1) & 0xFF000000UL );      \
-  v = ( (s0) & 0xFFUL ) | ( (s0) & 0xFF0000UL ) >> 8;           \
-  v |= ( (s1) & 0xFFUL ) << 16 | ( (s1) & 0xFF0000UL ) << 8;
-
 void
-GonkCameraPreview::ReceiveFrame(PRUint8 *aData, PRUint32 aLength)
+GonkCameraPreview::ReceiveFrame(mozilla::layers::GraphicBufferLocked* aBuffer)
 {
   DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  if (mInput->HaveEnoughBuffered(TRACK_VIDEO)) {
-    if (mDiscardedFrameCount == 0) {
-      DOM_CAMERA_LOGI("mInput has enough data buffered, starting to discard\n");
-    }
-    ++mDiscardedFrameCount;
+  if (!aBuffer)
     return;
-  } else if (mDiscardedFrameCount) {
-    DOM_CAMERA_LOGI("mInput needs more data again; discarded %d frames in a row\n", mDiscardedFrameCount);
-    mDiscardedFrameCount = 0;
-  }
-
-  switch (mFormat) {
-    case GonkCameraHardware::PREVIEW_FORMAT_YUV420SP:
-      {
-        // de-interlace the u and v planes
-        uint8_t* y = aData;
-        uint32_t yN = mWidth * mHeight;
-
-        NS_ASSERTION((yN & 0x3) == 0, "Invalid image dimensions!");
-
-        uint32_t uvN = yN / 4;
-        uint32_t* src = (uint32_t*)( y + yN );
-        uint32_t* d = new uint32_t[ uvN / 2 ];
-        uint32_t* u = d;
-        uint32_t* v = u + uvN / 4;
-
-        // we're handling pairs of 32-bit words, so divide by 8
-        NS_ASSERTION((uvN & 0x7) == 0, "Invalid image dimensions!");
-        uvN /= 8;
-
-        while (uvN--) {
-          uint32_t src0 = *src++;
-          uint32_t src1 = *src++;
-
-          uint32_t u0;
-          uint32_t v0;
-          uint32_t u1;
-          uint32_t v1;
-
-          DEINTERLACE( u0, v0, src0, src1 );
-
-          src0 = *src++;
-          src1 = *src++;
-
-          DEINTERLACE( u1, v1, src0, src1 );
 
-          *u++ = u0;
-          *u++ = u1;
-          *v++ = v0;
-          *v++ = v1;
-        }
-
-        memcpy(y + yN, d, yN / 2);
-        delete[] d;
-      }
-      break;
-
-    case GonkCameraHardware::PREVIEW_FORMAT_YUV420P:
-      // no transformating required
-      break;
-
-    default:
-      // in a format we don't handle, get out of here
-      return;
-  }
-
-  Image::Format format = Image::PLANAR_YCBCR;
+  Image::Format format = Image::GONK_IO_SURFACE;
   nsRefPtr<Image> image = mImageContainer->CreateImage(&format, 1);
-  image->AddRef();
-  PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(image.get());
+  GonkIOSurfaceImage* videoImage = static_cast<GonkIOSurfaceImage*>(image.get());
+  GonkIOSurfaceImage::Data data;
+  data.mGraphicBuffer = aBuffer;
+  data.mPicSize = gfxIntSize(mWidth, mHeight);
+  videoImage->SetData(data);
 
-  /**
-   * If you change either lumaBpp or chromaBpp, make sure the
-   * assertions below still hold.
-   */
-  const PRUint8 lumaBpp = 8;
-  const PRUint8 chromaBpp = 4;
-  PlanarYCbCrImage::Data data;
-  data.mYChannel = aData;
-  data.mYSize = gfxIntSize(mWidth, mHeight);
-
-  data.mYStride = mWidth * lumaBpp;
-  NS_ASSERTION((data.mYStride & 0x7) == 0, "Invalid image dimensions!");
-  data.mYStride /= 8;
-
-  data.mCbCrStride = mWidth * chromaBpp;
-  NS_ASSERTION((data.mCbCrStride & 0x7) == 0, "Invalid image dimensions!");
-  data.mCbCrStride /= 8;
-
-  data.mCbChannel = aData + mHeight * data.mYStride;
-  data.mCrChannel = data.mCbChannel + mHeight * data.mCbCrStride / 2;
-  data.mCbCrSize = gfxIntSize(mWidth / 2, mHeight / 2);
-  data.mPicX = 0;
-  data.mPicY = 0;
-  data.mPicSize = gfxIntSize(mWidth, mHeight);
-  data.mStereoMode = mozilla::layers::STEREO_MODE_MONO;
-  videoImage->SetData(data); // copies buffer
-
-  mVideoSegment.AppendFrame(videoImage, 1, gfxIntSize(mWidth, mHeight));
+  // AppendFrame() takes over image's reference
+  mVideoSegment.AppendFrame(image.forget(), 1, gfxIntSize(mWidth, mHeight));
   mInput->AppendToTrack(TRACK_VIDEO, &mVideoSegment);
 
   mFrameCount += 1;
 
   if ((mFrameCount % 10) == 0) {
     DOM_CAMERA_LOGI("%s:%d : mFrameCount = %d\n", __func__, __LINE__, mFrameCount);
   }
 }
 
 nsresult
 GonkCameraPreview::StartImpl()
 {
   DOM_CAMERA_LOGI("%s:%d : this=%p\n", __func__, __LINE__, this);
 
   /**
    * We set and then immediately get the preview size, in case the camera
-   * driver has decided to ignore our given dimensions.  We need to know
-   * the dimensions the driver is using so that, if needed, we can properly
-   * de-interlace the yuv420sp format in ReceiveFrame() above.
+   * driver has decided to ignore our given dimensions.
    */
   GonkCameraHardware::SetPreviewSize(mHwHandle, mWidth, mHeight);
   GonkCameraHardware::GetPreviewSize(mHwHandle, &mWidth, &mHeight);
   SetFrameRate(GonkCameraHardware::GetFps(mHwHandle));
 
   if (GonkCameraHardware::StartPreview(mHwHandle) != OK) {
     DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
     return NS_ERROR_FAILURE;
--- a/dom/camera/GonkCameraPreview.h
+++ b/dom/camera/GonkCameraPreview.h
@@ -18,28 +18,34 @@
 #define DOM_CAMERA_GONKCAMERAPREVIEW_H
 
 #include "CameraPreview.h"
 
 #define DOM_CAMERA_LOG_LEVEL  3
 #include "CameraCommon.h"
 
 namespace mozilla {
+namespace layers {
+class GraphicBufferLocked;
+} // namespace layers
+} // namespace mozilla
+
+namespace mozilla {
 
 class GonkCameraPreview : public CameraPreview
 {
 public:
   GonkCameraPreview(nsIThread* aCameraThread, PRUint32 aHwHandle, PRUint32 aWidth, PRUint32 aHeight)
     : CameraPreview(aCameraThread, aWidth, aHeight)
     , mHwHandle(aHwHandle)
     , mDiscardedFrameCount(0)
     , mFormat(GonkCameraHardware::PREVIEW_FORMAT_UNKNOWN)
   { }
 
-  void ReceiveFrame(PRUint8 *aData, PRUint32 aLength);
+  void ReceiveFrame(layers::GraphicBufferLocked* aBuffer);
 
   nsresult StartImpl();
   nsresult StopImpl();
 
 protected:
   ~GonkCameraPreview()
   {
     Stop();
--- a/dom/camera/GonkNativeWindow.cpp
+++ b/dom/camera/GonkNativeWindow.cpp
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 et sw=4 tw=80: */
 /*
  * Copyright (C) 2010 The Android Open Source Project
  * Copyright (C) 2012 Mozilla Foundation
  *
  * 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
  *
@@ -10,41 +12,60 @@
  *
  * 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.
  */
 
+#include "base/basictypes.h"
+#include "GonkCameraHwMgr.h"
+#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include "mozilla/layers/ImageBridgeChild.h"
 #include "GonkNativeWindow.h"
 #include "nsDebug.h"
 
 // enable debug logging by setting to 1
 #define CNW_DEBUG 0
 #if CNW_DEBUG
 #define CNW_LOGD(...) {(void)printf_stderr(__VA_ARGS__);}
 #else
 #define CNW_LOGD(...) ((void)0)
 #endif
 
 #define CNW_LOGE(...) {(void)printf_stderr(__VA_ARGS__);}
 
 using namespace android;
+using namespace mozilla::layers;
+
+GonkNativeWindow::GonkNativeWindow(GonkNativeWindowNewFrameCallback* aCallback)
+  : mNewFrameCallback(aCallback)
+{
+    GonkNativeWindow::init();
+}
 
 GonkNativeWindow::GonkNativeWindow()
+  : mNewFrameCallback(nullptr)
 {
     GonkNativeWindow::init();
 }
 
 GonkNativeWindow::~GonkNativeWindow()
 {
     freeAllBuffersLocked();
 }
 
+void GonkNativeWindow::abandon()
+{
+    Mutex::Autolock lock(mMutex);
+    freeAllBuffersLocked();
+    mDequeueCondition.signal();
+}
+
 void GonkNativeWindow::init()
 {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = hook_setSwapInterval;
     ANativeWindow::dequeueBuffer    = hook_dequeueBuffer;
     ANativeWindow::cancelBuffer     = hook_cancelBuffer;
     ANativeWindow::lockBuffer       = hook_lockBuffer;
     ANativeWindow::queueBuffer      = hook_queueBuffer;
@@ -107,21 +128,27 @@ int GonkNativeWindow::hook_perform(ANati
     va_list args;
     va_start(args, operation);
     GonkNativeWindow* c = getSelf(window);
     return c->perform(operation, args);
 }
 
 void GonkNativeWindow::freeBufferLocked(int i)
 {
+    ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
     if (mSlots[i].mGraphicBuffer != NULL) {
-        mSlots[i].mGraphicBuffer.clear();
+        // Don't destroy the gralloc buffer if it is still in the
+        // video stream awaiting rendering.
+        if (mSlots[i].mBufferState != BufferSlot::RENDERING) {
+            ibc->DeallocSurfaceDescriptorGralloc(mSlots[i].mSurfaceDescriptor);
+        }
         mSlots[i].mGraphicBuffer = NULL;
+        mSlots[i].mBufferState = BufferSlot::FREE;
+        mSlots[i].mFrameNumber = 0;
     }
-    mSlots[i].mBufferState = BufferSlot::FREE;
 }
 
 void GonkNativeWindow::freeAllBuffersLocked()
 {
     for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
         freeBufferLocked(i);
     }
 }
@@ -141,19 +168,21 @@ int GonkNativeWindow::setBufferCount(int
     }
 
     if (bufferCount < MIN_BUFFER_SLOTS) {
         CNW_LOGE("setBufferCount: requested buffer count (%d) is less than "
                 "minimum (%d)", bufferCount, MIN_BUFFER_SLOTS);
         return BAD_VALUE;
     }
 
-    // Error out if the user has dequeued buffers
+    // Error out if the user has dequeued buffers or sent buffers to
+    // video stream
     for (int i=0 ; i<mBufferCount ; i++) {
-        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) {
+        if (mSlots[i].mBufferState == BufferSlot::DEQUEUED ||
+            mSlots[i].mBufferState == BufferSlot::RENDERING) {
             CNW_LOGE("setBufferCount: client owns some buffers");
             return -EINVAL;
         }
     }
 
     if (bufferCount > mBufferCount) {
         // easy, we just have more buffers
         mBufferCount = bufferCount;
@@ -200,17 +229,19 @@ int GonkNativeWindow::dequeueBuffer(andr
                 }
             }
         }
 
         // we're in synchronous mode and didn't find a buffer, we need to
         // wait for some buffers to be consumed
         tryAgain = (found == INVALID_BUFFER_SLOT);
         if (tryAgain) {
+            CNW_LOGD("dequeueBuffer: Try again");
             mDequeueCondition.wait(mMutex);
+            CNW_LOGD("dequeueBuffer: Now");
         }
     }
 
     if (found == INVALID_BUFFER_SLOT) {
         // This should not happen.
         CNW_LOGE("dequeueBuffer: no available buffer slots");
         return -EBUSY;
     }
@@ -219,23 +250,35 @@ int GonkNativeWindow::dequeueBuffer(andr
 
     // buffer is now in DEQUEUED
     mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
 
     const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer);
 
     if (gbuf == NULL) {
         status_t error;
-        sp<GraphicBuffer> graphicBuffer( new GraphicBuffer( mDefaultWidth, mDefaultHeight, mPixelFormat, mUsage));
+        SurfaceDescriptor buffer;
+        ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
+        ibc->AllocSurfaceDescriptorGralloc(gfxIntSize(mDefaultWidth, mDefaultHeight),
+                                           mPixelFormat,
+                                           mUsage,
+                                           &buffer);
+        sp<GraphicBuffer> graphicBuffer =
+          GrallocBufferActor::GetFrom(buffer.get_SurfaceDescriptorGralloc());
+        if (!graphicBuffer.get()) {
+            return -ENOMEM;
+        }
         error = graphicBuffer->initCheck();
         if (error != NO_ERROR) {
             CNW_LOGE("dequeueBuffer: createGraphicBuffer failed with error %d",error);
             return error;
         }
         mSlots[buf].mGraphicBuffer = graphicBuffer;
+        mSlots[buf].mSurfaceDescriptor = buffer;
+        mSlots[buf].mSurfaceDescriptor.get_SurfaceDescriptorGralloc().external() = true;
     }
     *buffer = mSlots[buf].mGraphicBuffer.get();
 
     CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
             mSlots[buf].mGraphicBuffer->handle );
 
     CNW_LOGD("dequeueBuffer: X");
     return NO_ERROR;
@@ -255,48 +298,105 @@ int GonkNativeWindow::getSlotFromBufferL
         }
     }
     CNW_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
     return BAD_VALUE;
 }
 
 int GonkNativeWindow::queueBuffer(ANativeWindowBuffer* buffer)
 {
-    Mutex::Autolock lock(mMutex);
-    CNW_LOGD("queueBuffer: E");
-    int buf = getSlotFromBufferLocked(buffer);
+    {
+        Mutex::Autolock lock(mMutex);
+        CNW_LOGD("queueBuffer: E");
+        int buf = getSlotFromBufferLocked(buffer);
 
-    if (buf < 0 || buf >= mBufferCount) {
-        CNW_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
-                mBufferCount, buf);
-        return -EINVAL;
-    } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
-        CNW_LOGE("queueBuffer: slot %d is not owned by the client "
-                "(state=%d)", buf, mSlots[buf].mBufferState);
-        return -EINVAL;
+        if (buf < 0 || buf >= mBufferCount) {
+            CNW_LOGE("queueBuffer: slot index out of range [0, %d]: %d",
+                     mBufferCount, buf);
+            return -EINVAL;
+        } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+            CNW_LOGE("queueBuffer: slot %d is not owned by the client "
+                     "(state=%d)", buf, mSlots[buf].mBufferState);
+            return -EINVAL;
+        }
+
+        int64_t timestamp;
+        if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
+            timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+        } else {
+            timestamp = mTimestamp;
+        }
+
+        mSlots[buf].mBufferState = BufferSlot::QUEUED;
+        mSlots[buf].mTimestamp = timestamp;
+        mFrameCounter++;
+        mSlots[buf].mFrameNumber = mFrameCounter;
+
+        mDequeueCondition.signal();
     }
 
-    int64_t timestamp;
-    if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
-        timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
-    } else {
-        timestamp = mTimestamp;
+    // OnNewFrame might call lockCurrentBuffer so we must release the
+    // mutex first.
+    if (mNewFrameCallback) {
+      mNewFrameCallback->OnNewFrame();
     }
+    CNW_LOGD("queueBuffer: X");
+    return OK;
+}
+
+
+already_AddRefed<GraphicBufferLocked>
+GonkNativeWindow::getCurrentBuffer()
+{
+  CNW_LOGD("GonkNativeWindow::lockCurrentBuffer");
+  Mutex::Autolock lock(mMutex);
+
+  int found = -1;
+  for (int i = 0; i < mBufferCount; i++) {
+    const int state = mSlots[i].mBufferState;
+    if (state == BufferSlot::QUEUED) {
+      if (found < 0 ||
+          mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
+        found = i;
+      }
+    }
+  }
 
-    // Set the state to FREE as there are no operations on the queued buffer
-    // And, so that the buffer can be dequeued when needed.
-    mSlots[buf].mBufferState = BufferSlot::FREE;
-    mSlots[buf].mTimestamp = timestamp;
-    mFrameCounter++;
-    mSlots[buf].mFrameNumber = mFrameCounter;
+  if (found < 0) {
+    mDequeueCondition.signal();
+    return NULL;
+  }
+
+  mSlots[found].mBufferState = BufferSlot::RENDERING;
+
+  nsRefPtr<GraphicBufferLocked> ret =
+    new CameraGraphicBuffer(this, found, mSlots[found].mSurfaceDescriptor);
+  mDequeueCondition.signal();
+  return ret.forget();
+}
 
-    mDequeueCondition.signal();
-    CNW_LOGD("queueBuffer: X");
+void
+GonkNativeWindow::returnBuffer(uint32_t aIndex)
+{
+  CNW_LOGD("GonkNativeWindow::freeBuffer");
+  Mutex::Autolock lock(mMutex);
 
-    return OK;
+  if (aIndex < 0 || aIndex >= mBufferCount) {
+    CNW_LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
+             mBufferCount, aIndex);
+    return;
+  } else if (mSlots[aIndex].mBufferState != BufferSlot::RENDERING) {
+    printf_stderr("cancelBuffer: slot %d is not owned by the compositor (state=%d)",
+                  aIndex, mSlots[aIndex].mBufferState);
+    return;
+  }
+
+  mSlots[aIndex].mBufferState = BufferSlot::FREE;
+  mDequeueCondition.signal();
+  return;
 }
 
 int GonkNativeWindow::lockBuffer(ANativeWindowBuffer* buffer)
 {
     CNW_LOGD("GonkNativeWindow::lockBuffer");
     Mutex::Autolock lock(mMutex);
     return OK;
 }
--- a/dom/camera/GonkNativeWindow.h
+++ b/dom/camera/GonkNativeWindow.h
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 et sw=4 tw=80: */
 /*
  * Copyright (C) 2010 The Android Open Source Project
  * Copyright (C) 2012 Mozilla Foundation
  *
  * 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
  *
@@ -26,37 +28,61 @@
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "GonkIOSurfaceImage.h"
+
 namespace android {
 
+// The user of GonkNativeWindow who wants to receive notification of
+// new frames should implement this interface.
+class GonkNativeWindowNewFrameCallback {
+public:
+    virtual void OnNewFrame() = 0;
+};
+
 class GonkNativeWindow : public EGLNativeBase<ANativeWindow, GonkNativeWindow, RefBase>
 {
+    typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor;
 public:
     enum { MIN_UNDEQUEUED_BUFFERS = 2 };
     enum { MIN_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS };
     enum { NUM_BUFFER_SLOTS = 32 };
 
     GonkNativeWindow();
+    GonkNativeWindow(GonkNativeWindowNewFrameCallback* aCallback);
     ~GonkNativeWindow(); // this class cannot be overloaded
 
     // ANativeWindow hooks
     static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
     static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer);
     static int hook_lockBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
     static int hook_perform(ANativeWindow* window, int operation, ...);
     static int hook_query(const ANativeWindow* window, int what, int* value);
     static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer);
     static int hook_setSwapInterval(ANativeWindow* window, int interval);
 
+    // Get next frame from the queue and mark it as RENDERING, caller
+    // owns the returned buffer.
+    already_AddRefed<GraphicBufferLocked> getCurrentBuffer();
+
+    // Return the buffer to the queue and mark it as FREE. After that
+    // the buffer is useable again for the decoder.
+    void returnBuffer(uint32_t index);
+
+    // Release all internal buffers
+    void abandon();
+
 protected:
     virtual int cancelBuffer(ANativeWindowBuffer* buffer);
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer);
     virtual int lockBuffer(ANativeWindowBuffer* buffer);
     virtual int perform(int operation, va_list args);
     virtual int query(int what, int* value) const;
     virtual int queueBuffer(ANativeWindowBuffer* buffer);
     virtual int setSwapInterval(int interval);
@@ -98,16 +124,19 @@ private:
               mTimestamp(0),
               mFrameNumber(0){
         }
 
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
         // if no buffer has been allocated.
         sp<GraphicBuffer> mGraphicBuffer;
 
+        // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer.
+        SurfaceDescriptor mSurfaceDescriptor;
+
         // BufferState represents the different states in which a buffer slot
         // can be.
         enum BufferState {
             // FREE indicates that the buffer is not currently being used and
             // will not be used in the future until it gets dequeued and
             // subsequently queued by the client.
             FREE = 0,
 
@@ -126,16 +155,22 @@ private:
             // QUEUED indicates that the buffer has been queued by the client,
             // and has not since been made available for the client to dequeue.
             // Attaching the buffer to the texture does NOT transition the
             // buffer away from the QUEUED state. However, in Synchronous mode
             // the current buffer may be dequeued by the client under some
             // circumstances. See the note about the current buffer in the
             // documentation for DEQUEUED.
             QUEUED = 2,
+
+            // RENDERING indicates that the buffer has been sent to
+            // the compositor, and has not yet available for the
+            // client to dequeue. When the compositor has finished its
+            // job, the buffer will be returned to FREE state.
+            RENDERING = 3,
         };
 
         // mBufferState is the current state of this buffer slot.
         BufferState mBufferState;
 
         // mTimestamp is the current timestamp for this buffer slot. This gets
         // to set by queueBuffer each time this slot is queued.
         int64_t mTimestamp;
@@ -180,13 +215,57 @@ private:
     int mBufferCount;
 
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables. It must be locked whenever the member variables are accessed.
     mutable Mutex mMutex;
 
     // mFrameCounter is the free running counter, incremented for every buffer queued
     uint64_t mFrameCounter;
+
+    GonkNativeWindowNewFrameCallback* mNewFrameCallback;
+};
+
+
+// CameraGraphicBuffer maintains the buffer returned from GonkNativeWindow
+class CameraGraphicBuffer : public mozilla::layers::GraphicBufferLocked {
+    typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor;
+public:
+    CameraGraphicBuffer(GonkNativeWindow* aNativeWindow,
+                        uint32_t aIndex,
+                        SurfaceDescriptor aBuffer)
+        : GraphicBufferLocked(aBuffer)
+          , mNativeWindow(aNativeWindow)
+          , mIndex(aIndex)
+          , mLocked(true)
+    {}
+
+    virtual ~CameraGraphicBuffer() {}
+
+    // Unlock either returns the buffer to the native window or
+    // destroys the buffer if the window is already released.
+    virtual void Unlock()  MOZ_OVERRIDE
+    {
+        if (mLocked) {
+            // The window might has been destroyed. The buffer is no longer
+            // valid at that point.
+            sp<GonkNativeWindow> window = mNativeWindow.promote();
+            if (window.get()) {
+                window->returnBuffer(mIndex);
+                mLocked = false;
+            } else {
+                // If the window doesn't exist any more, release the buffer by
+                // ourself.
+                ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
+                ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor);
+            }
+        }
+    }
+
+protected:
+    wp<GonkNativeWindow> mNativeWindow;
+    uint32_t mIndex;
+    bool mLocked;
 };
 
 }; // namespace android
 
 #endif // DOM_CAMERA_GONKNATIVEWINDOW_H
--- a/dom/camera/Makefile.in
+++ b/dom/camera/Makefile.in
@@ -45,8 +45,9 @@ XPIDLSRCS = \
   nsIDOMCameraManager.idl \
   $(NULL)
 
 EXPORTS = \
   DOMCameraManager.h \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/gfx/layers/GonkIOSurfaceImage.h
+++ b/gfx/layers/GonkIOSurfaceImage.h
@@ -3,16 +3,17 @@
  * 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 GONKIOSURFACEIMAGE_H
 #define GONKIOSURFACEIMAGE_H
 
 #ifdef MOZ_WIDGET_GONK
 
+#include "mozilla/layers/LayersSurfaces.h"
 #include "ImageLayers.h"
 
 #include <ui/GraphicBuffer.h>
 
 namespace mozilla {
 namespace layers {
 
 /**
@@ -23,40 +24,39 @@ namespace layers {
  * buffer content is guaranteed to not change until Unlock() is
  * called. Each producer must maintain their own buffer queue and
  * implement the GraphicBufferLocked::Unlock() interface.
  */
 class GraphicBufferLocked {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphicBufferLocked)
 
 public:
-  GraphicBufferLocked(android::GraphicBuffer* aGraphicBuffer)
-    : mGraphicBuffer(aGraphicBuffer)
+  GraphicBufferLocked(SurfaceDescriptor aGraphicBuffer)
+    : mSurfaceDescriptor(aGraphicBuffer)
   {}
 
   virtual ~GraphicBufferLocked() {}
 
   virtual void Unlock() {}
 
-  virtual void* GetNativeBuffer()
+  SurfaceDescriptor GetSurfaceDescriptor()
   {
-    return mGraphicBuffer->getNativeBuffer();
+    return mSurfaceDescriptor;
   }
 
 protected:
-  android::GraphicBuffer* mGraphicBuffer;
+  SurfaceDescriptor mSurfaceDescriptor;
 };
 
 class THEBES_API GonkIOSurfaceImage : public Image {
 public:
   struct Data {
     nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
     gfxIntSize mPicSize;
   };
-
   GonkIOSurfaceImage()
     : Image(NULL, GONK_IO_SURFACE)
     , mSize(0, 0)
     {}
 
   virtual ~GonkIOSurfaceImage()
   {
     mGraphicBuffer->Unlock();
@@ -76,17 +76,22 @@ public:
   virtual already_AddRefed<gfxASurface> GetAsSurface()
   {
     // We need to fix this and return a ASurface at some point.
     return nullptr;
   }
 
   void* GetNativeBuffer()
   {
-    return mGraphicBuffer->GetNativeBuffer();
+    return GrallocBufferActor::GetFrom(GetSurfaceDescriptor())->getNativeBuffer();
+  }
+
+  SurfaceDescriptor GetSurfaceDescriptor()
+  {
+    return mGraphicBuffer->GetSurfaceDescriptor();
   }
 
 private:
   nsRefPtr<GraphicBufferLocked> mGraphicBuffer;
   gfxIntSize mSize;
 };
 
 } // namespace layers
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -5,16 +5,17 @@
 
 #include "ImageBridgeChild.h"
 #include "ImageContainerChild.h"
 #include "CompositorParent.h"
 #include "ImageBridgeParent.h"
 #include "gfxSharedImageSurface.h"
 #include "ImageLayers.h"
 #include "base/thread.h"
+#include "mozilla/Monitor.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/layers/ShadowLayers.h"
 
 using namespace base;
 
 namespace mozilla {
 namespace layers {
 
@@ -65,16 +66,60 @@ static void CreateContainerChildSync(nsR
                                      bool *aDone)
 {
   ReentrantMonitorAutoEnter autoMon(*barrier);
   *result = sImageBridgeChildSingleton->CreateImageContainerChildNow();
   *aDone = true;
   barrier->NotifyAll();
 }
 
+struct GrallocParam {
+  gfxIntSize size;
+  uint32_t format;
+  uint32_t usage;
+  SurfaceDescriptor* buffer;
+
+  GrallocParam(const gfxIntSize& aSize,
+               const uint32_t& aFormat,
+               const uint32_t& aUsage,
+               SurfaceDescriptor* aBuffer)
+    : size(aSize)
+    , format(aFormat)
+    , usage(aUsage)
+    , buffer(aBuffer)
+  {}
+};
+
+// dispatched function
+static void AllocSurfaceDescriptorGrallocSync(const GrallocParam& aParam,
+                                              Monitor* aBarrier,
+                                              bool* aDone)
+{
+  MonitorAutoLock autoMon(*aBarrier);
+
+  sImageBridgeChildSingleton->AllocSurfaceDescriptorGrallocNow(aParam.size,
+                                                               aParam.format,
+                                                               aParam.usage,
+                                                               aParam.buffer);
+  *aDone = true;
+  aBarrier->NotifyAll();
+}
+
+// dispatched function
+static void DeallocSurfaceDescriptorGrallocSync(const SurfaceDescriptor& aBuffer,
+                                                Monitor* aBarrier,
+                                                bool* aDone)
+{
+  MonitorAutoLock autoMon(*aBarrier);
+
+  sImageBridgeChildSingleton->DeallocSurfaceDescriptorGrallocNow(aBuffer);
+  *aDone = true;
+  aBarrier->NotifyAll();
+}
+
 // dispatched function
 static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
 {
   MessageLoop *parentMsgLoop = parent->GetMessageLoop();
   ipc::AsyncChannel *parentChannel = parent->GetIPCChannel();
   child->Open(parentChannel, parentMsgLoop, mozilla::ipc::AsyncChannel::Child);
 }
 
@@ -216,10 +261,120 @@ already_AddRefed<ImageContainerChild> Im
 {
   nsRefPtr<ImageContainerChild> ctnChild = new ImageContainerChild();
   PRUint64 id = 0;
   SendPImageContainerConstructor(ctnChild, &id);
   ctnChild->SetID(id);
   return ctnChild.forget();
 }
 
+PGrallocBufferChild*
+ImageBridgeChild::AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&,
+                                      MaybeMagicGrallocBufferHandle*)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  return GrallocBufferActor::Create();
+#else
+  NS_RUNTIMEABORT("No gralloc buffers for you");
+  return nullptr;
+#endif
+}
+
+bool
+ImageBridgeChild::DeallocPGrallocBuffer(PGrallocBufferChild* actor)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  delete actor;
+  return true;
+#else
+  NS_RUNTIMEABORT("Um, how did we get here?");
+  return false;
+#endif
+}
+
+bool
+ImageBridgeChild::AllocSurfaceDescriptorGralloc(const gfxIntSize& aSize,
+                                                const uint32_t& aFormat,
+                                                const uint32_t& aUsage,
+                                                SurfaceDescriptor* aBuffer)
+{
+  if (InImageBridgeChildThread()) {
+    return ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(aSize, aFormat, aUsage, aBuffer);
+  }
+
+  Monitor barrier("AllocSurfaceDescriptorGralloc Lock");
+  MonitorAutoLock autoMon(barrier);
+  bool done = false;
+
+  GetMessageLoop()->PostTask(
+    FROM_HERE,
+    NewRunnableFunction(&AllocSurfaceDescriptorGrallocSync,
+                        GrallocParam(aSize, aFormat, aUsage, aBuffer), &barrier, &done));
+
+  while (!done) {
+    barrier.Wait();
+  }
+  return true;
+}
+
+bool
+ImageBridgeChild::AllocSurfaceDescriptorGrallocNow(const gfxIntSize& aSize,
+                                                   const uint32_t& aFormat,
+                                                   const uint32_t& aUsage,
+                                                   SurfaceDescriptor* aBuffer)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  MaybeMagicGrallocBufferHandle handle;
+  PGrallocBufferChild* gc = SendPGrallocBufferConstructor(aSize, aFormat, aUsage, &handle);
+  if (handle.Tnull_t == handle.type()) {
+    PGrallocBufferChild::Send__delete__(gc);
+    return false;
+  }
+
+  GrallocBufferActor* gba = static_cast<GrallocBufferActor*>(gc);
+  gba->InitFromHandle(handle.get_MagicGrallocBufferHandle());
+
+  *aBuffer = SurfaceDescriptorGralloc(nullptr, gc, /* external */ false);
+  return true;
+#else
+  NS_RUNTIMEABORT("No gralloc buffers for you");
+  return false;
+#endif
+}
+
+bool
+ImageBridgeChild::DeallocSurfaceDescriptorGralloc(const SurfaceDescriptor& aBuffer)
+{
+  if (InImageBridgeChildThread()) {
+    return ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(aBuffer);
+  }
+
+  Monitor barrier("DeallocSurfaceDescriptor Lock");
+  MonitorAutoLock autoMon(barrier);
+  bool done = false;
+
+  GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&DeallocSurfaceDescriptorGrallocSync,
+                                                            aBuffer, &barrier, &done));
+
+  while (!done) {
+    barrier.Wait();
+  }
+
+  return true;
+}
+
+bool
+ImageBridgeChild::DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  PGrallocBufferChild* gbp =
+    aBuffer.get_SurfaceDescriptorGralloc().bufferChild();
+  PGrallocBufferChild::Send__delete__(gbp);
+
+  return true;
+#else
+  NS_RUNTIMEABORT("Um, how did we get here?");
+  return false;
+#endif
+}
+
 } // layers
 } // mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -154,16 +154,62 @@ public:
 
   /**
    * Part of the creation of ImageCOntainerChild that is executed on the 
    * ImageBridgeChild thread after invoking CreateImageContainerChild
    *
    * Must be called from the ImageBridgeChild thread.
    */
   already_AddRefed<ImageContainerChild> CreateImageContainerChildNow();
+
+  virtual PGrallocBufferChild*
+  AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&,
+                      MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPGrallocBuffer(PGrallocBufferChild* actor) MOZ_OVERRIDE;
+
+  /**
+   * Allocate a gralloc SurfaceDescriptor remotely.
+   */
+  bool
+  AllocSurfaceDescriptorGralloc(const gfxIntSize& aSize,
+                                const uint32_t& aFormat,
+                                const uint32_t& aUsage,
+                                SurfaceDescriptor* aBuffer);
+
+  /**
+   * Part of the allocation of gralloc SurfaceDescriptor that is
+   * executed on the ImageBridgeChild thread after invoking
+   * AllocSurfaceDescriptorGralloc.
+   *
+   * Must be called from the ImageBridgeChild thread.
+   */
+  bool
+  AllocSurfaceDescriptorGrallocNow(const gfxIntSize& aSize,
+                                   const uint32_t& aContent,
+                                   const uint32_t& aUsage,
+                                   SurfaceDescriptor* aBuffer);
+
+  /**
+   * Deallocate a remotely allocated gralloc buffer.
+   */
+  bool
+  DeallocSurfaceDescriptorGralloc(const SurfaceDescriptor& aBuffer);
+
+  /**
+   * Part of the deallocation of gralloc SurfaceDescriptor that is
+   * executed on the ImageBridgeChild thread after invoking
+   * DeallocSurfaceDescriptorGralloc.
+   *
+   * Must be called from the ImageBridgeChild thread.
+   */
+  bool
+  DeallocSurfaceDescriptorGrallocNow(const SurfaceDescriptor& aBuffer);
+
 protected:
   
   ImageBridgeChild() {};
 
   /**
    * Creates an ImageContainerChild and it's associated ImageContainerParent.
    *
    * The creation happens synchronously on the ImageBridgeChild thread, so if 
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -38,16 +38,42 @@ bool ImageBridgeParent::RecvStop()
 
 static  PRUint64 GenImageContainerID() {
   static PRUint64 sNextImageID = 1;
   
   ++sNextImageID;
   return sNextImageID;
 }
   
+PGrallocBufferParent*
+ImageBridgeParent::AllocPGrallocBuffer(const gfxIntSize& aSize,
+                                       const uint32_t& aFormat,
+                                       const uint32_t& aUsage,
+                                       MaybeMagicGrallocBufferHandle* aOutHandle)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  return GrallocBufferActor::Create(aSize, aFormat, aUsage, aOutHandle);
+#else
+  NS_RUNTIMEABORT("No gralloc buffers for you");
+  return nullptr;
+#endif
+}
+
+bool
+ImageBridgeParent::DeallocPGrallocBuffer(PGrallocBufferParent* actor)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  delete actor;
+  return true;
+#else
+  NS_RUNTIMEABORT("Um, how did we get here?");
+  return false;
+#endif
+}
+
 PImageContainerParent* ImageBridgeParent::AllocPImageContainer(PRUint64* aID)
 {
   PRUint64 id = GenImageContainerID();
   *aID = id;
   return new ImageContainerParent(this, id);
 }
 
 bool ImageBridgeParent::DeallocPImageContainer(PImageContainerParent* toDealloc)
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -18,16 +18,24 @@ class CompositorParent;
  */
 class ImageBridgeParent : public PImageBridgeParent
 {
 public:
 
   ImageBridgeParent(MessageLoop* aLoop);
   ~ImageBridgeParent();
 
+
+  virtual PGrallocBufferParent*
+  AllocPGrallocBuffer(const gfxIntSize&, const uint32_t&, const uint32_t&,
+                      MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPGrallocBuffer(PGrallocBufferParent* actor) MOZ_OVERRIDE;
+
   // Overriden from PImageBridgeParent.
   PImageContainerParent* AllocPImageContainer(PRUint64* aID) MOZ_OVERRIDE;
   // Overriden from PImageBridgeParent.
   bool DeallocPImageContainer(PImageContainerParent* toDealloc) MOZ_OVERRIDE;
   // Overriden from PImageBridgeParent.
   bool RecvStop() MOZ_OVERRIDE;
 
   MessageLoop * GetMessageLoop();
--- a/gfx/layers/ipc/ImageContainerChild.cpp
+++ b/gfx/layers/ipc/ImageContainerChild.cpp
@@ -5,16 +5,17 @@
 
 
 #include "ImageContainerChild.h"
 #include "gfxSharedImageSurface.h"
 #include "ShadowLayers.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/SharedImageUtils.h"
 #include "ImageLayers.h"
+#include "GonkIOSurfaceImage.h"
 
 namespace mozilla {
 namespace layers {
 
 /*
  * - POOL_MAX_SHARED_IMAGES is the maximum number number of shared images to
  * store in the ImageContainerChild's pool.
  *
@@ -93,16 +94,20 @@ void ImageContainerChild::StopChild()
   mStop = true;    
 
   DispatchDestroy();
 }
 
 bool ImageContainerChild::RecvReturnImage(const SharedImage& aImage)
 {
   SharedImage* img = new SharedImage(aImage);
+  // Remove oldest image from the queue.
+  if (mImageQueue.Length() > 0) {
+    mImageQueue.RemoveElementAt(0);
+  }
   if (!AddSharedImageToPool(img) || mStop) {
     DestroySharedImage(*img);
     delete img;
   }
   return true;
 }
 
 void ImageContainerChild::DestroySharedImage(const SharedImage& aImage)
@@ -191,16 +196,22 @@ SharedImage* ImageContainerChild::Create
     SharedImage* result = new SharedImage( 
               YUVImage(tempBufferY->GetShmem(),
                        tempBufferU->GetShmem(),
                        tempBufferV->GetShmem(),
                        data->GetPictureRect()));
     NS_ABORT_IF_FALSE(result->type() == SharedImage::TYUVImage,
                       "SharedImage type not set correctly");
     return result;
+#ifdef MOZ_WIDGET_GONK
+  } else if (image->GetFormat() == Image::GONK_IO_SURFACE) {
+    GonkIOSurfaceImage* gonkImage = static_cast<GonkIOSurfaceImage*>(image);
+    SharedImage* result = new SharedImage(gonkImage->GetSurfaceDescriptor());
+    return result;
+#endif
   } else {
     NS_RUNTIMEABORT("TODO: Only YUVImage is supported here right now.");
   }
   return nullptr;
 }
 
 bool ImageContainerChild::AddSharedImageToPool(SharedImage* img)
 {
@@ -314,16 +325,19 @@ SharedImage* ImageContainerChild::ImageT
   NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
                     "Should be in ImageBridgeChild thread.");
   SharedImage *img = GetSharedImageFor(aImage);
   if (img) {
     CopyDataIntoSharedImage(aImage, img);  
   } else {
     img = CreateSharedImageFromData(aImage);
   }
+  // Keep a reference to the image we sent to compositor to maintain a
+  // correct reference count.
+  mImageQueue.AppendElement(aImage);
   return img;
 }
 
 void ImageContainerChild::SendImageAsync(ImageContainer* aContainer,
                                          Image* aImage)
 {
   if(!aContainer || !aImage) {
       return;
--- a/gfx/layers/ipc/ImageContainerChild.h
+++ b/gfx/layers/ipc/ImageContainerChild.h
@@ -187,16 +187,26 @@ protected:
    * Allocates a SharedImage and copy aImage's data into it.
    * Called by ImageToSharedImage.
    */
   SharedImage * CreateSharedImageFromData(Image* aImage);
 
 private:
   PRUint64 mImageContainerID;
   nsTArray<SharedImage*> mSharedImagePool;
+
+  /**
+   * Save a reference to the outgoing images and remove the reference
+   * once the image is returned from the compositor.
+   * GonkIOSurfaceImage needs to know when to return the buffer to the
+   * producing thread. The buffer is returned when GonkIOSurfaceImage
+   * destructs.
+   */
+  nsTArray<nsRefPtr<Image> > mImageQueue;
+
   int mActiveImageCount;
   bool mStop;
   bool mDispatchedDestroy;
 };
 
 
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -40,16 +40,25 @@ struct SharedTextureDescriptor {
   TextureShareType shareType;
   SharedTextureHandle handle;
   nsIntSize size;
   bool inverted;
 };
 
 struct SurfaceDescriptorGralloc {
   PGrallocBuffer buffer;
+
+  /**
+   * We can have one source producing gralloc buffers and sharing them
+   * with another source that may also produce its own gralloc buffers.
+   * This happens for camera preview buffers sent to video code.  When
+   * that happens, the producer can mark the buffer as "external" to
+   * prevent its consumer from mistakenly freeing the buffer.
+   */
+  bool external;
 };
 
 struct SharedImageID {
   PRUint64 id;
 };
 
 union SurfaceDescriptor {
   Shmem;
--- a/gfx/layers/ipc/PGrallocBuffer.ipdl
+++ b/gfx/layers/ipc/PGrallocBuffer.ipdl
@@ -1,29 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 protocol PImageBridge;
 include protocol PImageContainer;
 include protocol PLayers;
 
 namespace mozilla {
 namespace layers {
 
 /**
  * This is a trivial protocol that's used to track gralloc buffers
  * across thread contexts.  A live PGrallocBuffer actor always
  * corresponds 1:1 to a pre-shared gralloc buffer (sharing is done by
  * the PGrallocBuffer constructor).
  */
 async protocol PGrallocBuffer {
-  manager PImageContainer or PLayers;
+  /* FIXME: Bug 783451: shouldn't be managed by PImageContainer */
+  manager PImageBridge or PImageContainer or PLayers;
 
   /** Gralloc buffers can be "owned" by either parent or child. */
 both:
   async __delete__();
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -1,30 +1,41 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 LayersSurfaces;
+include protocol PGrallocBuffer;
 include protocol PImageContainer;
 
 using ImageHandle;
 
 namespace mozilla {
 namespace layers {
 
 /**
  * The PImageBridge protocol is used to allow isolated threads or processes to push
  * frames directly to the compositor thread/process without relying on the main thread
  * which might be too busy dealing with content script.
  */
 /*FIXME: sync*/rpc protocol PImageBridge
 {
   manages PImageContainer;
+  manages PGrallocBuffer;
 
 parent:
+
+  // Allocates a gralloc buffer that may not suitable to use with
+  // gfxImageSurface but allows hardware decoder to write to the
+  // buffer directly. The format is a enum defined in
+  // system/graphics.h and the usage is the GraphicBuffer usage
+  // flag. See GraphicBuffer.h and gralloc.h.
+  sync PGrallocBuffer(gfxIntSize size, uint32_t format, uint32_t usage)
+    returns (MaybeMagicGrallocBufferHandle handle);
   
   // First step of the destruction sequence. This puts all the ImageContainerParents
   // in a state in which they can't send asynchronous messages to their child 
   // counterpart so as to not race with the upcomming __delete__ message. 
   // In the child side, the __delete__ messages are not sent right after Stop,
   // they are scheduled in the ImageBridgeChild's message queue in order to ensure 
   // that all the messages from the parent side have been received and processed
   // before sending __delete__.
--- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
@@ -187,16 +187,34 @@ ShadowLayerManager::OpenDescriptorForDir
 }
 
 /*static*/ void
 ShadowLayerManager::PlatformSyncBeforeReplyUpdate()
 {
   // Nothing to be done for gralloc.
 }
 
+/*static*/ PGrallocBufferParent*
+GrallocBufferActor::Create(const gfxIntSize& aSize,
+                           const uint32_t& aFormat,
+                           const uint32_t& aUsage,
+                           MaybeMagicGrallocBufferHandle* aOutHandle)
+{
+  GrallocBufferActor* actor = new GrallocBufferActor();
+  *aOutHandle = null_t();
+  sp<GraphicBuffer> buffer(
+    new GraphicBuffer(aSize.width, aSize.height, aFormat, aUsage));
+  if (buffer->initCheck() != OK)
+    return actor;
+
+  actor->mGraphicBuffer = buffer;
+  *aOutHandle = MagicGrallocBufferHandle(buffer);
+  return actor;
+}
+
 bool
 ShadowLayerManager::PlatformDestroySharedSurface(SurfaceDescriptor* aSurface)
 {
   if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aSurface->type()) {
     return false;
   }
 
   PGrallocBufferParent* gbp =
@@ -239,17 +257,17 @@ ShadowLayerForwarder::PlatformAllocBuffe
   if (handle.Tnull_t == handle.type()) {
     PGrallocBufferChild::Send__delete__(gc);
     return false;
   }
 
   GrallocBufferActor* gba = static_cast<GrallocBufferActor*>(gc);
   gba->InitFromHandle(handle.get_MagicGrallocBufferHandle());
 
-  *aBuffer = SurfaceDescriptorGralloc(nullptr, gc);
+  *aBuffer = SurfaceDescriptorGralloc(nullptr, gc, /* external */ false);
   return true;
 }
 
 //-----------------------------------------------------------------------------
 // Both processes
 
 /*static*/ sp<GraphicBuffer>
 GrallocBufferActor::GetFrom(const SurfaceDescriptorGralloc& aDescriptor)
--- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.h
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.h
@@ -56,36 +56,41 @@ struct MagicGrallocBufferHandle {
  * GraphicBuffer (pmem region).  It allows us to cheaply and
  * conveniently share gralloc handles between processes.
  */
 class GrallocBufferActor : public PGrallocBufferChild
                          , public PGrallocBufferParent
 {
   friend class ShadowLayerForwarder;
   friend class ShadowLayerManager;
+  friend class ImageBridgeChild;
   typedef android::GraphicBuffer GraphicBuffer;
 
 public:
   virtual ~GrallocBufferActor() {}
 
   static PGrallocBufferParent*
   Create(const gfxIntSize& aSize, const gfxContentType& aContent,
          MaybeMagicGrallocBufferHandle* aOutHandle);
 
+  static PGrallocBufferParent*
+  Create(const gfxIntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage,
+         MaybeMagicGrallocBufferHandle* aOutHandle);
+
   static PGrallocBufferChild*
   Create();
 
+  static android::sp<GraphicBuffer>
+  GetFrom(const SurfaceDescriptorGralloc& aDescriptor);
+
 private:
   GrallocBufferActor() {}
 
   void InitFromHandle(const MagicGrallocBufferHandle& aHandle);
 
-  static android::sp<GraphicBuffer>
-  GetFrom(const SurfaceDescriptorGralloc& aDescriptor);
-
   android::sp<GraphicBuffer> mGraphicBuffer;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 namespace IPC {
 
--- a/gfx/layers/ipc/SharedImageUtils.h
+++ b/gfx/layers/ipc/SharedImageUtils.h
@@ -15,17 +15,18 @@ namespace layers {
 
 template<typename Deallocator>
 void DeallocSharedImageData(Deallocator* protocol, const SharedImage& aImage)
 {
   if (aImage.type() == SharedImage::TYUVImage) {
     protocol->DeallocShmem(aImage.get_YUVImage().Ydata());
     protocol->DeallocShmem(aImage.get_YUVImage().Udata());
     protocol->DeallocShmem(aImage.get_YUVImage().Vdata());
-  } else if (aImage.type() == SharedImage::TSurfaceDescriptor) {
+  } else if (aImage.type() == SharedImage::TSurfaceDescriptor &&
+             aImage.get_SurfaceDescriptor().type() == SurfaceDescriptor::TShmem) {
     protocol->DeallocShmem(aImage.get_SurfaceDescriptor().get_Shmem());
   }
 }
 
 template<typename Allocator>
 bool AllocateSharedBuffer(Allocator* protocol,
                           const gfxIntSize& aSize,
                           gfxASurface::gfxContentType aContent,
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -890,16 +890,31 @@ ShadowImageLayerOGL::RenderLayer(int aPr
                                                   mOGLManager->GetCompositorID());
     PRUint32 imgVersion = ImageContainerParent::GetSharedImageVersion(mImageContainerID);
     if (imgVersion != mImageVersion) {
       SharedImage* img = ImageContainerParent::GetSharedImage(mImageContainerID);
       if (img && (img->type() == SharedImage::TYUVImage)) {
         UploadSharedYUVToTexture(img->get_YUVImage());
   
         mImageVersion = imgVersion;
+#ifdef MOZ_WIDGET_GONK
+      } else if (img
+                 && (img->type() == SharedImage::TSurfaceDescriptor)
+                 && (img->get_SurfaceDescriptor().type() == SurfaceDescriptor::TSurfaceDescriptorGralloc)) {
+        const SurfaceDescriptorGralloc& desc = img->get_SurfaceDescriptor().get_SurfaceDescriptorGralloc();
+        sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(desc);
+        mSize = gfxIntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+        if (!mExternalBufferTexture.IsAllocated()) {
+          mExternalBufferTexture.Allocate(gl());
+        }
+        gl()->MakeCurrent();
+        gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
+        gl()->BindExternalBuffer(mExternalBufferTexture.GetTextureID(), graphicBuffer->getNativeBuffer());
+        mImageVersion = imgVersion;
+#endif
       }
     }
   }
 
 
   if (mTexImage) {
     NS_ASSERTION(mTexImage->GetContentType() != gfxASurface::CONTENT_ALPHA,
                  "Image layer has alpha image");
@@ -930,16 +945,35 @@ ShadowImageLayerOGL::RenderLayer(int aPr
         // We can't use BindAndDrawQuad because that always uploads the whole texture from 0.0f -> 1.0f
         // in x and y. We use BindAndDrawQuadWithTextureRect to actually draw a subrect of the texture
         mOGLManager->BindAndDrawQuadWithTextureRect(colorProgram,
                                                     nsIntRect(0, 0, mTexImage->GetTileRect().width,
                                                                     mTexImage->GetTileRect().height),
                                                     mTexImage->GetTileRect().Size());
       } while (mTexImage->NextTile());
     }
+#ifdef MOZ_WIDGET_GONK
+  } else if (mExternalBufferTexture.IsAllocated()) {
+    ShaderProgramOGL *program = mOGLManager->GetProgram(RGBAExternalLayerProgramType, GetMaskLayer());
+
+    gl()->ApplyFilterToBoundTexture(mFilter);
+
+    program->Activate();
+    program->SetLayerQuadRect(nsIntRect(0, 0,
+                                        mSize.width, mSize.height));
+    program->SetLayerTransform(GetEffectiveTransform());
+    program->SetLayerOpacity(GetEffectiveOpacity());
+    program->SetRenderOffset(aOffset);
+    program->SetTextureUnit(0);
+    program->LoadMask(GetMaskLayer());
+
+    mOGLManager->BindAndDrawQuadWithTextureRect(program,
+                                                GetVisibleRegion().GetBounds(),
+                                                nsIntSize(mSize.width, mSize.height));
+#endif
   } else if (mSharedHandle) {
     GLContext::SharedHandleDetails handleDetails;
     if (!gl()->GetSharedHandleDetails(mShareType, mSharedHandle, handleDetails)) {
       NS_ERROR("Failed to get shared handle details");
       return;
     }
 
     ShaderProgramOGL* program = mOGLManager->GetProgram(handleDetails.mProgramType, GetMaskLayer());
@@ -1019,16 +1053,17 @@ ShadowImageLayerOGL::LoadAsTexture(GLuin
 void
 ShadowImageLayerOGL::CleanupResources()
 {
   if (mSharedHandle) {
     gl()->ReleaseSharedHandle(mShareType, mSharedHandle);
     mSharedHandle = NULL;
   }
 
+  mExternalBufferTexture.Release();
   mYUVTexture[0].Release();
   mYUVTexture[1].Release();
   mYUVTexture[2].Release();
   mTexImage = nullptr;
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -181,17 +181,22 @@ private:
 
   nsRefPtr<TextureImage> mTexImage;
 
   // For SharedTextureHandle
   gl::SharedTextureHandle mSharedHandle;
   gl::TextureImage::TextureShareType mShareType;
   bool mInverted;
   GLuint mTexture;
-  
+
+  // For direct texturing with OES_EGL_image_external extension. This
+  // texture is allocated when the image supports binding with
+  // BindExternalBuffer.
+  GLTexture mExternalBufferTexture;
+
   GLTexture mYUVTexture[3];
   gfxIntSize mSize;
   gfxIntSize mCbCrSize;
   nsIntRect mPictureRect;
 };
 
 } /* layers */
 } /* mozilla */