Bug 767480 - Gralloc backed video buffer. r=roc
☠☠ backed out by 7bf66490dfad ☠ ☠
authorKan-Ru Chen (陳侃如) <kanru@kanru.info>
Tue, 21 Aug 2012 14:59:42 +0800
changeset 102923 c8f7bace9cf901c983648de962a21a46975f683e
parent 102922 17ec4e01c12695d80dd0af6d14d8ea9dee93bcaa
child 102924 7bf66490dfadc559934a7f73d8a528e9caf62668
push id23317
push userryanvm@gmail.com
push dateWed, 22 Aug 2012 02:05:02 +0000
treeherdermozilla-central@abc17059522b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs767480
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 767480 - Gralloc backed video buffer. r=roc
content/media/nsBuiltinDecoderReader.cpp
gfx/layers/GrallocImages.cpp
gfx/layers/GrallocImages.h
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/ImageTypes.h
gfx/layers/Makefile.in
gfx/layers/ipc/ImageContainerChild.cpp
--- a/content/media/nsBuiltinDecoderReader.cpp
+++ b/content/media/nsBuiltinDecoderReader.cpp
@@ -58,16 +58,30 @@ static bool
 ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
 {
   return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
          aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
          aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          aPlane.mStride > 0;
 }
 
+static bool
+IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
+             const VideoData::YCbCrBuffer::Plane& aCbPlane,
+             const VideoData::YCbCrBuffer::Plane& aCrPlane)
+{
+  return
+    aYPlane.mWidth % 2 == 0 &&
+    aYPlane.mHeight % 2 == 0 &&
+    aYPlane.mWidth / 2 == aCbPlane.mWidth &&
+    aYPlane.mHeight / 2 == aCbPlane.mHeight &&
+    aCbPlane.mWidth == aCrPlane.mWidth &&
+    aCbPlane.mHeight == aCrPlane.mHeight;
+}
+
 bool
 nsVideoInfo::ValidateVideoRegion(const nsIntSize& aFrame,
                                  const nsIntRect& aPicture,
                                  const nsIntSize& aDisplay)
 {
   return
     aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
     aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
@@ -178,32 +192,37 @@ VideoData* VideoData::Create(nsVideoInfo
   }
 
   nsAutoPtr<VideoData> v(new VideoData(aOffset,
                                        aTime,
                                        aEndTime,
                                        aKeyframe,
                                        aTimecode,
                                        aInfo.mDisplay));
+  const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
+  const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
+  const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
+
   // Currently our decoder only knows how to output to PLANAR_YCBCR
   // format.
-  ImageFormat format = PLANAR_YCBCR;
-  v->mImage = aContainer->CreateImage(&format, 1);
+  ImageFormat format[2] = {PLANAR_YCBCR, GRALLOC_PLANAR_YCBCR};
+  if (IsYV12Format(Y, Cb, Cr)) {
+    v->mImage = aContainer->CreateImage(format, 2);
+  } else {
+    v->mImage = aContainer->CreateImage(format, 1);
+  }
   if (!v->mImage) {
     return nullptr;
   }
-  NS_ASSERTION(v->mImage->GetFormat() == PLANAR_YCBCR,
+  NS_ASSERTION(v->mImage->GetFormat() == PLANAR_YCBCR ||
+               v->mImage->GetFormat() == GRALLOC_PLANAR_YCBCR,
                "Wrong format?");
   PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
 
   PlanarYCbCrImage::Data data;
-  const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
-  const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
-  const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
-
   data.mYChannel = Y.mData;
   data.mYSize = gfxIntSize(Y.mWidth, Y.mHeight);
   data.mYStride = Y.mStride;
   data.mYOffset = Y.mOffset;
   data.mYSkip = Y.mSkip;
   data.mCbChannel = Cb.mData;
   data.mCrChannel = Cr.mData;
   data.mCbCrSize = gfxIntSize(Cb.mWidth, Cb.mHeight);
new file mode 100644
--- /dev/null
+++ b/gfx/layers/GrallocImages.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/layers/ImageBridgeChild.h"
+
+#include "nsDebug.h"
+#include "ImageContainer.h"
+#include "GrallocImages.h"
+
+using namespace mozilla::ipc;
+using namespace android;
+
+namespace mozilla {
+namespace layers {
+
+GrallocPlanarYCbCrImage::GrallocPlanarYCbCrImage()
+  : PlanarYCbCrImage(nullptr)
+{
+  mFormat = GRALLOC_PLANAR_YCBCR;
+}
+
+GrallocPlanarYCbCrImage::~GrallocPlanarYCbCrImage()
+{
+  ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
+  ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor);
+}
+
+void
+GrallocPlanarYCbCrImage::SetData(const Data& aData)
+{
+  NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width");
+  NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height");
+  NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels");
+
+  mData = aData;
+  mSize = aData.mPicSize;
+
+  if (mSurfaceDescriptor.type() == SurfaceDescriptor::T__None) {
+    ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton();
+    ibc->AllocSurfaceDescriptorGralloc(aData.mYSize,
+                                       HAL_PIXEL_FORMAT_YV12,
+                                       GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                       GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                                       GraphicBuffer::USAGE_HW_TEXTURE,
+                                       &mSurfaceDescriptor);
+  }
+  sp<GraphicBuffer> graphicBuffer =
+    GrallocBufferActor::GetFrom(mSurfaceDescriptor.get_SurfaceDescriptorGralloc());
+  if (!graphicBuffer.get()) {
+    return;
+  }
+
+  if (graphicBuffer->initCheck() != NO_ERROR) {
+    return;
+  }
+
+  void* vaddr;
+  if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+                          &vaddr) != OK) {
+    return;
+  }
+
+  PRUint8* yChannel = static_cast<PRUint8*>(vaddr);
+  gfxIntSize ySize = gfxIntSize(aData.mYSize.width,
+                                aData.mYSize.height);
+  PRInt32 yStride = graphicBuffer->getStride();
+
+  PRUint8* vChannel = yChannel + (yStride * ySize.height);
+  gfxIntSize uvSize = gfxIntSize(ySize.width / 2,
+                                 ySize.height / 2);
+  // Align to 16 bytes boundary
+  PRInt32 uvStride = ((yStride / 2) + 15) & ~0x0F;
+  PRUint8* uChannel = vChannel + (uvStride * uvSize.height);
+
+  // Memory outside of the image width may not writable. If the stride
+  // equals to the image width then we can use only one copy.
+  if (yStride == mData.mYStride &&
+      yStride == ySize.width) {
+    memcpy(yChannel, mData.mYChannel, yStride * ySize.width);
+  } else {
+    for (int i = 0; i < ySize.height; i++) {
+      memcpy(yChannel + i * yStride,
+             mData.mYChannel + i * mData.mYStride,
+             ySize.width);
+    }
+  }
+  if (uvStride == mData.mCbCrStride &&
+      uvStride == uvSize.width) {
+    memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.width);
+    memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.width);
+  } else {
+    for (int i = 0; i < uvSize.height; i++) {
+      memcpy(uChannel + i * uvStride,
+             mData.mCbChannel + i * mData.mCbCrStride,
+             uvSize.width);
+      memcpy(vChannel + i * uvStride,
+             mData.mCrChannel + i * mData.mCbCrStride,
+             uvSize.width);
+    }
+  }
+  graphicBuffer->unlock();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/GrallocImages.h
@@ -0,0 +1,70 @@
+/* -*- 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/. */
+
+#ifndef GRALLOCIMAGES_H
+#define GRALLOCIMAGES_H
+
+#ifdef MOZ_WIDGET_GONK
+
+#include "mozilla/layers/LayersSurfaces.h"
+#include "ImageLayers.h"
+
+#include <ui/GraphicBuffer.h>
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * The YUV format supported by Android HAL
+ *
+ * 4:2:0 - CbCr width and height is half that of Y.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * y_size = stride * height
+ * c_size = ALIGN(stride/2, 16) * height/2
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ * The Image that is rendered is the picture region defined by
+ * mPicX, mPicY and mPicSize. The size of the rendered image is
+ * mPicSize, not mYSize or mCbCrSize.
+ */
+class THEBES_API GrallocPlanarYCbCrImage : public PlanarYCbCrImage {
+  typedef PlanarYCbCrImage::Data Data;
+
+public:
+  GrallocPlanarYCbCrImage();
+
+  virtual ~GrallocPlanarYCbCrImage();
+
+  /**
+   * This makes a copy of the data buffers, in order to support functioning
+   * in all different layer managers.
+   */
+  virtual void SetData(const Data& aData);
+
+  virtual PRUint32 GetDataSize() { return 0; }
+
+  virtual bool IsValid() { return mSurfaceDescriptor.type() != SurfaceDescriptor::T__None; }
+
+  SurfaceDescriptor GetSurfaceDescriptor() {
+    return mSurfaceDescriptor;
+  }
+
+private:
+  SurfaceDescriptor mSurfaceDescriptor;
+};
+
+} // namespace layers
+} // namespace mozilla
+#endif
+
+#endif /* GRALLOCIMAGES_H */
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ImageContainerChild.h"
 
 #include "ImageContainer.h"
 #include "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 #include "mozilla/ipc/Shmem.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
 #include "SharedTextureImage.h"
 #include "gfxImageSurface.h"
 #include "gfxSharedImageSurface.h"
 #include "yuv_convert.h"
 #include "gfxUtils.h"
 
@@ -25,16 +26,17 @@
 #include "gfxD2DSurface.h"
 #include "gfxWindowsPlatform.h"
 #include <d3d10_1.h>
 
 #include "d3d10/ImageLayerD3D10.h"
 #endif
 
 using namespace mozilla::ipc;
+using namespace android;
 using mozilla::gfx::DataSourceSurface;
 using mozilla::gfx::SourceSurface;
 
 
 namespace mozilla {
 namespace layers {
 
 already_AddRefed<Image>
@@ -42,32 +44,47 @@ ImageFactory::CreateImage(const ImageFor
                           PRUint32 aNumFormats,
                           const gfxIntSize &,
                           BufferRecycleBin *aRecycleBin)
 {
   if (!aNumFormats) {
     return nullptr;
   }
   nsRefPtr<Image> img;
+#ifdef MOZ_WIDGET_GONK
+  if (FormatInList(aFormats, aNumFormats, GRALLOC_PLANAR_YCBCR)) {
+    img = new GrallocPlanarYCbCrImage();
+    return img.forget();
+  }
+#endif
   if (FormatInList(aFormats, aNumFormats, PLANAR_YCBCR)) {
     img = new PlanarYCbCrImage(aRecycleBin);
-  } else if (FormatInList(aFormats, aNumFormats, CAIRO_SURFACE)) {
+    return img.forget();
+  }
+  if (FormatInList(aFormats, aNumFormats, CAIRO_SURFACE)) {
     img = new CairoImage();
-  } else if (FormatInList(aFormats, aNumFormats, SHARED_TEXTURE)) {
+    return img.forget();
+  }
+  if (FormatInList(aFormats, aNumFormats, SHARED_TEXTURE)) {
     img = new SharedTextureImage();
+    return img.forget();
+  }
 #ifdef XP_MACOSX
-  } else if (FormatInList(aFormats, aNumFormats, MAC_IO_SURFACE)) {
+  if (FormatInList(aFormats, aNumFormats, MAC_IO_SURFACE)) {
     img = new MacIOSurfaceImage();
+    return img.forget();
+  }
 #endif
 #ifdef MOZ_WIDGET_GONK
-  } else if (FormatInList(aFormats, aNumFormats, GONK_IO_SURFACE)) {
+  if (FormatInList(aFormats, aNumFormats, GONK_IO_SURFACE)) {
     img = new GonkIOSurfaceImage();
+    return img.forget();
+  }
 #endif
-  }
-  return img.forget();
+  return nullptr;
 }
 
 BufferRecycleBin::BufferRecycleBin()
   : mLock("mozilla.layers.BufferRecycleBin.mLock")
 {
 }
 
 void
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -639,17 +639,17 @@ public:
                        mPicSize.height);
     }
   };
 
   enum {
     MAX_DIMENSION = 16384
   };
 
-  ~PlanarYCbCrImage();
+  virtual ~PlanarYCbCrImage();
 
   /**
    * This makes a copy of the data buffers, in order to support functioning
    * in all different layer managers.
    */
   virtual void SetData(const Data& aData);
 
   /**
--- a/gfx/layers/ImageTypes.h
+++ b/gfx/layers/ImageTypes.h
@@ -12,16 +12,23 @@ enum ImageFormat {
   /**
    * The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
    * support this format, because the Ogg video decoder depends on it.
    * The maximum image width and height is 16384.
    */
   PLANAR_YCBCR,
 
   /**
+   * The GRALLOC_PLANAR_YCBCR format creates a GrallocPlanarYCbCrImage, a
+   * subtype of PlanarYCbCrImage. It takes a PlanarYCbCrImage data and can be
+   * used as a texture by Gonk backend directly.
+   */
+  GRALLOC_PLANAR_YCBCR,
+
+  /**
    * The CAIRO_SURFACE format creates a CairoImage. All backends should
    * support this format, because video rendering sometimes requires it.
    * 
    * This format is useful even though a ThebesLayer could be used.
    * It makes it easy to render a cairo surface when another Image format
    * could be used. It can also avoid copying the surface data in some
    * cases.
    * 
@@ -68,9 +75,9 @@ enum StereoMode {
   STEREO_MODE_RIGHT_LEFT,
   STEREO_MODE_BOTTOM_TOP,
   STEREO_MODE_TOP_BOTTOM
 };
 
 
 } // namespace
 
-#endif
\ No newline at end of file
+#endif
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -167,17 +167,20 @@ EXPORTS_mozilla/layers += ShadowLayerUti
 DEFINES	+= -DMOZ_ENABLE_D3D10_LAYER
 endif
 
 # NB: Gralloc is available on other platforms that use the android GL
 # libraries, but only Gonk is able to use it reliably because Gecko
 # has full system permissions there.
 ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
 EXPORTS_mozilla/layers += ShadowLayerUtilsGralloc.h
-CPPSRCS += ShadowLayerUtilsGralloc.cpp
+CPPSRCS += \
+        ShadowLayerUtilsGralloc.cpp \
+        GrallocImages.cpp \
+        $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 LOCAL_INCLUDES += \
         -I$(topsrcdir)/content/events/src \
--- a/gfx/layers/ipc/ImageContainerChild.cpp
+++ b/gfx/layers/ipc/ImageContainerChild.cpp
@@ -6,16 +6,17 @@
 
 #include "ImageContainerChild.h"
 #include "gfxSharedImageSurface.h"
 #include "ShadowLayers.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/SharedImageUtils.h"
 #include "ImageContainer.h"
 #include "GonkIOSurfaceImage.h"
+#include "GrallocImages.h"
 
 namespace mozilla {
 namespace layers {
 
 /*
  * - POOL_MAX_SHARED_IMAGES is the maximum number number of shared images to
  * store in the ImageContainerChild's pool.
  *
@@ -201,16 +202,20 @@ SharedImage* ImageContainerChild::Create
     NS_ABORT_IF_FALSE(result->type() == SharedImage::TYUVImage,
                       "SharedImage type not set correctly");
     return result;
 #ifdef MOZ_WIDGET_GONK
   } else if (image->GetFormat() == GONK_IO_SURFACE) {
     GonkIOSurfaceImage* gonkImage = static_cast<GonkIOSurfaceImage*>(image);
     SharedImage* result = new SharedImage(gonkImage->GetSurfaceDescriptor());
     return result;
+  } else if (image->GetFormat() == GRALLOC_PLANAR_YCBCR) {
+    GrallocPlanarYCbCrImage* GrallocImage = static_cast<GrallocPlanarYCbCrImage*>(image);
+    SharedImage* result = new SharedImage(GrallocImage->GetSurfaceDescriptor());
+    return result;
 #endif
   } else {
     NS_RUNTIMEABORT("TODO: Only YUVImage is supported here right now.");
   }
   return nullptr;
 }
 
 bool ImageContainerChild::AddSharedImageToPool(SharedImage* img)