Bug 1210286 - Fall back to converting SourceSurfaces (RGB) to NV12 in OMXCodecWrapper. r=jolin
authorAndreas Pehrson <pehrsons@gmail.com>
Mon, 12 Oct 2015 10:33:58 +0800
changeset 267801 da0ad09260848f0e3fe2f2f483710c5bf9a30d68
parent 267800 dcee00c2b89dbb1c26720228d28672aa10264126
child 267802 c95bfb27dec9f5514db7bacea2ed35fd0ee816e7
push id29530
push usercbook@mozilla.com
push dateThu, 15 Oct 2015 09:53:07 +0000
treeherdermozilla-central@e193b4da0a8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjolin
bugs1210286
milestone44.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 1210286 - Fall back to converting SourceSurfaces (RGB) to NV12 in OMXCodecWrapper. r=jolin
dom/media/omx/OMXCodecWrapper.cpp
dom/media/omx/moz.build
--- a/dom/media/omx/OMXCodecWrapper.cpp
+++ b/dom/media/omx/OMXCodecWrapper.cpp
@@ -12,17 +12,20 @@
 #include <media/ICrypto.h>
 #include <media/IOMX.h>
 #include <OMX_Component.h>
 #include <stagefright/MediaDefs.h>
 #include <stagefright/MediaErrors.h>
 
 #include "AudioChannelFormat.h"
 #include "GrallocImages.h"
+#include "LayersLogging.h"
+#include "libyuv.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/layers/GrallocTextureClient.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 #define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
 // AMR NB kbps
@@ -375,16 +378,77 @@ ConvertGrallocImageToNV12(GrallocImage* 
       break;
     default:
       NS_ERROR("Unsupported input gralloc image type. Should never be here.");
   }
 
   graphicBuffer->unlock();
 }
 
+static nsresult
+ConvertSourceSurfaceToNV12(const nsRefPtr<SourceSurface>& aSurface, uint8_t* aDestination)
+{
+  uint32_t width = aSurface->GetSize().width;
+  uint32_t height = aSurface->GetSize().height;
+
+  uint8_t* y = aDestination;
+  int yStride = width;
+
+  uint8_t* uv = y + (yStride * height);
+  int uvStride = width / 2;
+
+  SurfaceFormat format = aSurface->GetFormat();
+
+  if (!aSurface) {
+    CODEC_ERROR("Getting surface %s from image failed", Stringify(format).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
+  if (!data) {
+    CODEC_ERROR("Getting data surface from %s image with %s (%s) surface failed",
+                Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(),
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
+  if (!map.IsMapped()) {
+    CODEC_ERROR("Reading DataSourceSurface from %s image with %s (%s) surface failed",
+                Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(),
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  int rv;
+  switch (aSurface->GetFormat()) {
+    case SurfaceFormat::B8G8R8A8:
+    case SurfaceFormat::B8G8R8X8:
+      rv = libyuv::ARGBToNV12(static_cast<uint8*>(map.GetData()),
+                              map.GetStride(),
+                              y, yStride,
+                              uv, uvStride,
+                              width, height);
+      break;
+    default:
+      CODEC_ERROR("Unsupported SourceSurface format %s",
+                  Stringify(aSurface->GetFormat()).c_str());
+      NS_ASSERTION(false, "Unsupported SourceSurface format");
+      return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  if (rv != 0) {
+    CODEC_ERROR("%s to I420 conversion failed",
+                Stringify(aSurface->GetFormat()).c_str());
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 nsresult
 OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight,
                         int64_t aTimestamp, int aInputFlags, bool* aSendEOS)
 {
   MOZ_ASSERT(mStarted, "Configure() should be called before Encode().");
 
   NS_ENSURE_TRUE(aWidth == mWidth && aHeight == mHeight && aTimestamp >= 0,
                  NS_ERROR_INVALID_ARG);
@@ -405,19 +469,20 @@ OMXVideoEncoder::Encode(const Image* aIm
     } else if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
       // Reject unsupported gralloc-ed buffers.
       int halFormat = static_cast<GrallocImage*>(img)->GetGraphicBuffer()->getPixelFormat();
       NS_ENSURE_TRUE(halFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
                      halFormat == HAL_PIXEL_FORMAT_YV12 ||
                      halFormat == GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
                      NS_ERROR_INVALID_ARG);
     } else {
-      // TODO: support RGB to YUV color conversion.
-      NS_ERROR("Unsupported input image type.");
-      return NS_ERROR_INVALID_ARG;
+      nsRefPtr<SourceSurface> surface = img->GetAsSourceSurface();
+      NS_ENSURE_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+                     surface->GetFormat() == SurfaceFormat::B8G8R8X8,
+                     NS_ERROR_INVALID_ARG);
     }
   }
 
   status_t result;
 
   // Dequeue an input buffer.
   uint32_t index;
   result = mCodec->dequeueInputBuffer(&index, INPUT_BUFFER_TIMEOUT_US);
@@ -445,16 +510,23 @@ OMXVideoEncoder::Encode(const Image* aIm
     // Fill UV plane.
     memset(dst + yLen, 0x80, uvLen);
   } else {
     if (format == ImageFormat::GRALLOC_PLANAR_YCBCR) {
       ConvertGrallocImageToNV12(static_cast<GrallocImage*>(img), dst);
     } else if (format == ImageFormat::PLANAR_YCBCR) {
       ConvertPlanarYCbCrToNV12(static_cast<PlanarYCbCrImage*>(img)->GetData(),
                                dst);
+    } else {
+      nsRefPtr<SourceSurface> surface = img->GetAsSourceSurface();
+      nsresult rv = ConvertSourceSurfaceToNV12(surface, dst);
+
+      if (rv != NS_OK) {
+        return NS_ERROR_FAILURE;
+      }
     }
   }
 
   // Queue this input buffer.
   result = mCodec->queueInputBuffer(index, 0, dstSize, aTimestamp, aInputFlags);
 
   if (aSendEOS && (aInputFlags & BUFFER_EOS) && result == OK) {
     *aSendEOS = true;
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -36,16 +36,17 @@ if CONFIG['MOZ_AUDIO_OFFLOAD']:
 if CONFIG['MOZ_OMX_ENCODER']:
     EXPORTS += [
         'OMXCodecWrapper.h',
     ]
     SOURCES += [
         'OMXCodecDescriptorUtil.cpp',
         'OMXCodecWrapper.cpp',
     ]
+    LOCAL_INCLUDES += ['/media/libyuv/include']
 
 if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
     EXPORTS += [
         'RtspExtractor.h',
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [