Bug 767480 - Gralloc backed video buffer. r=roc
authorKan-Ru Chen (陳侃如) <kanru@kanru.info>
Tue, 21 Aug 2012 18:22:58 +0800
changeset 108392 94f6e5a00d8b387296defb979ee03955c2a244b7
parent 108391 e1ec49e3076f2f3d42bc0b6a4aaccc3ea369ba90
child 108393 79e9fb28b8e17a0efcffd3153c50a0b5870e52eb
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs767480
milestone17.0a1
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)