Bug 1306521 - Handle VP9 colorspace BT.709 on BasicCompositor r=nical,jwwang,jya,jrmuizel
authorSotaro Ikeda <sotaro.ikeda.g@gmail.com>
Tue, 11 Oct 2016 19:46:28 -0700
changeset 362686 2b520bbe1d527b1102233cb91b52dcc77f2f89eb
parent 362685 65c02bf0a79284e7074214484c1f0559c6906491
child 362687 fdf29e912685ecd840014334094ba5be7d9ef274
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical, jwwang, jya, jrmuizel
bugs1306521
milestone52.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 1306521 - Handle VP9 colorspace BT.709 on BasicCompositor r=nical,jwwang,jya,jrmuizel
dom/media/MediaData.cpp
dom/media/MediaData.h
dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
gfx/ipc/GfxMessageUtils.h
gfx/layers/BufferTexture.cpp
gfx/layers/BufferTexture.h
gfx/layers/ImageContainer.h
gfx/layers/ImageDataSerializer.cpp
gfx/layers/ImageDataSerializer.h
gfx/layers/ImageTypes.h
gfx/layers/client/ImageClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TextureClientRecycleAllocator.cpp
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
gfx/tests/gtest/TestTextures.cpp
gfx/ycbcr/YCbCrUtils.cpp
gfx/ycbcr/scale_yuv_argb.cpp
gfx/ycbcr/scale_yuv_argb.h
gfx/ycbcr/yuv_convert.cpp
gfx/ycbcr/yuv_convert.h
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -234,16 +234,17 @@ bool VideoData::SetVideoDataToImage(Plan
   data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
   data.mCbCrStride = Cb.mStride;
   data.mCbSkip = Cb.mSkip;
   data.mCrSkip = Cr.mSkip;
   data.mPicX = aPicture.x;
   data.mPicY = aPicture.y;
   data.mPicSize = aPicture.Size();
   data.mStereoMode = aInfo.mStereoMode;
+  data.mYUVColorSpace = aBuffer.mYUVColorSpace;
 
   aVideoImage->SetDelayedConversion(true);
   if (aCopyData) {
     return aVideoImage->CopyData(data);
   } else {
     return aVideoImage->AdoptData(data);
   }
 }
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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/. */
 #if !defined(MediaData_h)
 #define MediaData_h
 
+#include "AudioSampleFormat.h"
+#include "ImageTypes.h"
 #include "nsSize.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsRect.h"
-#include "AudioSampleFormat.h"
 #include "nsIMemoryReporter.h"
 #include "SharedBuffer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "nsTArray.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/PodOperations.h"
@@ -438,16 +439,17 @@ public:
       uint32_t mWidth;
       uint32_t mHeight;
       uint32_t mStride;
       uint32_t mOffset;
       uint32_t mSkip;
     };
 
     Plane mPlanes[3];
+    YUVColorSpace mYUVColorSpace = YUVColorSpace::BT601;
   };
 
   // Constructs a VideoData object. If aImage is nullptr, creates a new Image
   // holding a copy of the YCbCr data passed in aBuffer. If aImage is not
   // nullptr, it's stored as the underlying video image and aBuffer is assumed
   // to point to memory within aImage so no copy is made. aTimecode is a codec
   // specific number representing the timestamp of the frame of video data.
   // Returns nsnull if an error occurs. This may indicate that memory couldn't
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
@@ -81,26 +81,32 @@ FFmpegLibWrapper::Link()
       version = AV_FUNC_57;
       break;
     default:
       FFMPEG_LOG("Unknown avcodec version");
       Unlink();
       return false;
   }
 
-#define AV_FUNC(func, ver)                                                     \
-  if ((ver) & version) {                                                      \
+#define AV_FUNC_OPTION(func, ver)                                              \
+  if ((ver) & version) {                                                       \
     if (!(func = (decltype(func))PR_FindSymbol(((ver) & AV_FUNC_AVUTIL_MASK) ? mAVUtilLib : mAVCodecLib, #func))) { \
       FFMPEG_LOG("Couldn't load function " # func);                            \
-      Unlink();                                                                \
-      return false;                                                            \
     }                                                                          \
   } else {                                                                     \
     func = (decltype(func))nullptr;                                            \
   }
+
+#define AV_FUNC(func, ver)                                                     \
+  AV_FUNC_OPTION(func, ver)                                                    \
+  if ((ver) & version && !func) {                                              \
+    Unlink();                                                                  \
+    return false;                                                              \
+  }
+
   AV_FUNC(av_lockmgr_register, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_alloc_context3, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_close, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_decode_audio4, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_decode_video2, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
   AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
@@ -113,17 +119,19 @@ FFmpegLibWrapper::Link()
   AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
   AV_FUNC(avcodec_free_frame, AV_FUNC_54)
   AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
   AV_FUNC(av_frame_alloc, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
   AV_FUNC(av_frame_free, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
   AV_FUNC(av_frame_unref, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
+  AV_FUNC_OPTION(av_frame_get_colorspace, AV_FUNC_AVUTIL_ALL)
 #undef AV_FUNC
+#undef AV_FUNC_OPTION
 
   avcodec_register_all();
 #ifdef DEBUG
   av_log_set_level(AV_LOG_DEBUG);
 #endif
 
   return true;
 }
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
@@ -62,16 +62,19 @@ struct FFmpegLibWrapper
   void*	(*av_malloc)(size_t size);
   void (*av_freep)(void *ptr);
 
   // libavutil v55 and later only
   AVFrame* (*av_frame_alloc)();
   void (*av_frame_free)(AVFrame** frame);
   void (*av_frame_unref)(AVFrame* frame);
 
+  // libavutil optional
+  int (*av_frame_get_colorspace)(const AVFrame *frame);
+
   PRLibrary* mAVCodecLib;
   PRLibrary* mAVUtilLib;
 
 private:
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -296,17 +296,29 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecode(
   b.mPlanes[0].mHeight = mFrame->height;
   if (mCodecContext->pix_fmt == AV_PIX_FMT_YUV444P) {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = mFrame->width;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = mFrame->height;
   } else {
     b.mPlanes[1].mWidth = b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[1].mHeight = b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
   }
-
+  if (mLib->av_frame_get_colorspace) {
+    switch (mLib->av_frame_get_colorspace(mFrame)) {
+      case AVCOL_SPC_BT709:
+        b.mYUVColorSpace = YUVColorSpace::BT709;
+        break;
+      case AVCOL_SPC_SMPTE170M:
+      case AVCOL_SPC_BT470BG:
+        b.mYUVColorSpace = YUVColorSpace::BT601;
+        break;
+      default:
+        NS_WARNING("Unsupported yuv color space.");
+    }
+  }
   RefPtr<VideoData> v =
     VideoData::CreateAndCopyData(mInfo,
                                   mImageContainer,
                                   aSample->mOffset,
                                   pts,
                                   duration,
                                   b,
                                   !!mFrame->key_frame,
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -938,16 +938,24 @@ template <>
 struct ParamTraits<mozilla::StereoMode>
   : public ContiguousEnumSerializer<
              mozilla::StereoMode,
              mozilla::StereoMode::MONO,
              mozilla::StereoMode::MAX>
 {};
 
 template <>
+struct ParamTraits<mozilla::YUVColorSpace>
+  : public ContiguousEnumSerializer<
+             mozilla::YUVColorSpace,
+             mozilla::YUVColorSpace::BT601,
+             mozilla::YUVColorSpace::UNKNOWN>
+{};
+
+template <>
 struct ParamTraits<mozilla::layers::ScrollableLayerGuid>
 {
   typedef mozilla::layers::ScrollableLayerGuid paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mLayersId);
     WriteParam(aMsg, aParam.mPresShellId);
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -153,40 +153,43 @@ BufferTextureData::CreateInternal(Layers
 
     return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
   }
 }
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                                 int32_t aBufferSize,
+                                                YUVColorSpace aYUVColorSpace,
                                                 TextureFlags aTextureFlags)
 {
   if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
     return nullptr;
   }
 
   bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
                                                             aAllocator->GetCompositorBackendType());
 
   // Initialize the metadata with something, even if it will have to be rewritten
   // afterwards since we don't know the dimensions of the texture at this point.
   BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
                                           0, 0, 0, StereoMode::MONO,
+                                          aYUVColorSpace,
                                           hasIntermediateBuffer);
 
   return CreateInternal(aAllocator->GetTextureForwarder(), desc, gfx::BackendType::NONE, aBufferSize,
                         aTextureFlags);
 }
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCr(KnowsCompositor* aAllocator,
                                   gfx::IntSize aYSize,
                                   gfx::IntSize aCbCrSize,
                                   StereoMode aStereoMode,
+                                  YUVColorSpace aYUVColorSpace,
                                   TextureFlags aTextureFlags)
 {
   uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
   if (bufSize == 0) {
     return nullptr;
   }
 
   uint32_t yOffset;
@@ -196,17 +199,17 @@ BufferTextureData::CreateForYCbCr(KnowsC
                                           aCbCrSize.width, aCbCrSize.height,
                                           yOffset, cbOffset, crOffset);
 
   bool hasIntermediateBuffer = aAllocator ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
                                                                          aAllocator->GetCompositorBackendType())
                                           : true;
 
   YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
-                                               crOffset, aStereoMode,
+                                               crOffset, aStereoMode, aYUVColorSpace,
                                                hasIntermediateBuffer);
 
  return CreateInternal(aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor,
                        gfx::BackendType::NONE, bufSize, aTextureFlags);
 }
 
 void
 BufferTextureData::FillInfo(TextureData::Info& aInfo) const
@@ -239,16 +242,22 @@ BufferTextureData::GetSize() const
 }
 
 Maybe<gfx::IntSize>
 BufferTextureData::GetCbCrSize() const
 {
   return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor);
 }
 
+Maybe<YUVColorSpace>
+BufferTextureData::GetYUVColorSpace() const
+{
+  return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
+}
+
 Maybe<StereoMode>
 BufferTextureData::GetStereoMode() const
 {
   return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
 }
 
 gfx::SurfaceFormat
 BufferTextureData::GetFormat() const
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -27,23 +27,25 @@ public:
                                    TextureFlags aFlags,
                                    TextureAllocationFlags aAllocFlags,
                                    LayersIPCChannel* aAllocator);
 
   static BufferTextureData* CreateForYCbCr(KnowsCompositor* aAllocator,
                                            gfx::IntSize aYSize,
                                            gfx::IntSize aCbCrSize,
                                            StereoMode aStereoMode,
+                                           YUVColorSpace aYUVColorSpace,
                                            TextureFlags aTextureFlags);
 
   // It is generally better to use CreateForYCbCr instead.
   // This creates a half-initialized texture since we don't know the sizes and
   // offsets in the buffer.
   static BufferTextureData* CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                                          int32_t aSize,
+                                                         YUVColorSpace aYUVColorSpace,
                                                          TextureFlags aTextureFlags);
 
   virtual bool Lock(OpenMode aMode, FenceHandle*) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual void FillInfo(TextureData::Info& aInfo) const override;
 
@@ -58,16 +60,18 @@ public:
 
   virtual BufferTextureData* AsBufferTextureData() override { return this; }
 
   // Don't use this.
   void SetDesciptor(const BufferDescriptor& aDesc);
 
   Maybe<gfx::IntSize> GetCbCrSize() const;
 
+  Maybe<YUVColorSpace> GetYUVColorSpace() const;
+
   Maybe<StereoMode> GetStereoMode() const;
 
 protected:
   gfx::IntSize GetSize() const;
 
   gfx::SurfaceFormat GetFormat() const;
 
   static BufferTextureData* CreateInternal(LayersIPCChannel* aAllocator,
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -683,28 +683,30 @@ struct PlanarYCbCrData {
   gfx::IntSize mCbCrSize;
   int32_t mCbSkip;
   int32_t mCrSkip;
   // Picture region
   uint32_t mPicX;
   uint32_t mPicY;
   gfx::IntSize mPicSize;
   StereoMode mStereoMode;
+  YUVColorSpace mYUVColorSpace;
 
   gfx::IntRect GetPictureRect() const {
     return gfx::IntRect(mPicX, mPicY,
                      mPicSize.width,
                      mPicSize.height);
   }
 
   PlanarYCbCrData()
     : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0)
     , mCbChannel(nullptr), mCrChannel(nullptr)
     , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0)
     , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO)
+    , mYUVColorSpace(YUVColorSpace::BT601)
   {}
 };
 
 /****** Image subtypes for the different formats ******/
 
 /**
  * We assume that the image data is in the REC 470M color space (see
  * Theora specification, section 4.3.1).
--- a/gfx/layers/ImageDataSerializer.cpp
+++ b/gfx/layers/ImageDataSerializer.cpp
@@ -130,16 +130,30 @@ Maybe<gfx::IntSize> CbCrSizeFromBufferDe
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
       return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
     default:
       MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
   }
 }
 
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor)
+{
+{
+  switch (aDescriptor.type()) {
+    case BufferDescriptor::TRGBDescriptor:
+      return Nothing();
+    case BufferDescriptor::TYCbCrDescriptor:
+      return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
+    default:
+      MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
+  }
+}
+}
+
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor)
 {
   switch (aDescriptor.type()) {
     case BufferDescriptor::TRGBDescriptor:
       return Nothing();
     case BufferDescriptor::TYCbCrDescriptor:
       return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
     default:
@@ -197,16 +211,17 @@ DataSourceSurfaceFromYCbCrDescriptor(uin
   ycbcrData.mYChannel     = GetYChannel(aBuffer, aDescriptor);
   ycbcrData.mYStride      = yStride;
   ycbcrData.mYSize        = ySize;
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
   ycbcrData.mCbCrStride   = cbCrStride;
   ycbcrData.mCbCrSize     = cbCrSize;
   ycbcrData.mPicSize      = ySize;
+  ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
 
   gfx::ConvertYCbCrToRGB(ycbcrData,
                          gfx::SurfaceFormat::B8G8R8X8,
                          ySize,
                          map.mData,
                          map.mStride);
 
   result->Unmap();
@@ -231,15 +246,16 @@ ConvertAndScaleFromYCbCrDescriptor(uint8
   ycbcrData.mYChannel     = GetYChannel(aBuffer, aDescriptor);
   ycbcrData.mYStride      = yStride;
   ycbcrData.mYSize        = ySize;
   ycbcrData.mCbChannel    = GetCbChannel(aBuffer, aDescriptor);
   ycbcrData.mCrChannel    = GetCrChannel(aBuffer, aDescriptor);
   ycbcrData.mCbCrStride   = cbCrStride;
   ycbcrData.mCbCrSize     = cbCrSize;
   ycbcrData.mPicSize      = ySize;
+  ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
 
   gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride);
 }
 
 } // namespace ImageDataSerializer
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ImageDataSerializer.h
+++ b/gfx/layers/ImageDataSerializer.h
@@ -55,16 +55,18 @@ void ComputeYCbCrOffsets(int32_t yStride
                          uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset);
 
 gfx::SurfaceFormat FormatFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
+Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(const BufferDescriptor& aDescriptor);
+
 Maybe<StereoMode> StereoModeFromBufferDescriptor(const BufferDescriptor& aDescriptor);
 
 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
 uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor);
 
--- a/gfx/layers/ImageTypes.h
+++ b/gfx/layers/ImageTypes.h
@@ -110,11 +110,18 @@ enum class StereoMode {
   MONO,
   LEFT_RIGHT,
   RIGHT_LEFT,
   BOTTOM_TOP,
   TOP_BOTTOM,
   MAX,
 };
 
+enum class YUVColorSpace {
+  BT601,
+  BT709,
+  // This represents the unknown format.
+  UNKNOWN,
+};
+
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -118,16 +118,17 @@ ImageClient::CreateTextureClientForImage
   if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
     PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
     const PlanarYCbCrData* data = ycbcr->GetData();
     if (!data) {
       return nullptr;
     }
     texture = TextureClient::CreateForYCbCr(aForwarder,
                                             data->mYSize, data->mCbCrSize, data->mStereoMode,
+                                            data->mYUVColorSpace,
                                             TextureFlags::DEFAULT);
     if (!texture) {
       return nullptr;
     }
 
     TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
     if (!autoLock.Succeeded()) {
       return nullptr;
@@ -226,17 +227,16 @@ ImageClientSingle::UpdateImage(ImageCont
       OverlayImage* overlayImage = static_cast<OverlayImage*>(image);
       OverlaySource source;
       if (overlayImage->GetSidebandStream().IsValid()) {
         // Duplicate GonkNativeHandle::NhObj for ipc,
         // since ParamTraits<GonkNativeHandle>::Write() absorbs native_handle_t.
         RefPtr<GonkNativeHandle::NhObj> nhObj = overlayImage->GetSidebandStream().GetDupNhObj();
         GonkNativeHandle handle(nhObj);
         if (!handle.IsValid()) {
-          gfxWarning() << "ImageClientSingle::UpdateImage failed in GetDupNhObj";
           return false;
         }
         source.handle() = OverlayHandle(handle);
       } else {
         source.handle() = OverlayHandle(overlayImage->GetOverlayId());
       }
       source.size() = overlayImage->GetSize();
       GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1275,52 +1275,55 @@ TextureClient::CreateForRawBufferAccess(
 }
 
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
                               gfx::IntSize aYSize,
                               gfx::IntSize aCbCrSize,
                               StereoMode aStereoMode,
+                              YUVColorSpace aYUVColorSpace,
                               TextureFlags aTextureFlags)
 {
   // The only reason we allow aAllocator to be null is for gtests
   MOZ_ASSERT(!aAllocator || aAllocator->GetLayersIPCActor()->IPCOpen());
   if (aAllocator && !aAllocator->GetLayersIPCActor()->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
     return nullptr;
   }
 
   TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
-                                                        aStereoMode, aTextureFlags);
+                                                        aStereoMode, aYUVColorSpace,
+                                                        aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags,
                                       aAllocator ? aAllocator->GetTextureForwarder() : nullptr);
 }
 
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                             size_t aSize,
+                                            YUVColorSpace aYUVColorSpace,
                                             TextureFlags aTextureFlags)
 {
   // also test the validity of aAllocator
   MOZ_ASSERT(!aAllocator || aAllocator->GetLayersIPCActor()->IPCOpen());
   if (aAllocator && !aAllocator->GetLayersIPCActor()->IPCOpen()) {
     return nullptr;
   }
 
   TextureData* data =
-    BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize,
+    BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize, aYUVColorSpace,
                                                     aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags,
                                       aAllocator ? aAllocator->GetTextureForwarder() : 0);
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -362,16 +362,17 @@ public:
                     TextureAllocationFlags aAllocFlags);
 
   // Creates and allocates a TextureClient supporting the YCbCr format.
   static already_AddRefed<TextureClient>
   CreateForYCbCr(KnowsCompositor* aAllocator,
                  gfx::IntSize aYSize,
                  gfx::IntSize aCbCrSize,
                  StereoMode aStereoMode,
+                 YUVColorSpace aYUVColorSpace,
                  TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient (can be accessed through raw
   // pointers).
   static already_AddRefed<TextureClient>
   CreateForRawBufferAccess(KnowsCompositor* aAllocator,
                            gfx::SurfaceFormat aFormat,
                            gfx::IntSize aSize,
@@ -380,16 +381,17 @@ public:
                            TextureAllocationFlags flags = ALLOC_DEFAULT);
 
   // Creates and allocates a TextureClient (can beaccessed through raw
   // pointers) with a certain buffer size. It's unfortunate that we need this.
   // providing format and sizes could let us do more optimization.
   static already_AddRefed<TextureClient>
   CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
                                size_t aSize,
+                               YUVColorSpace aYUVColorSpace,
                                TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient of the same type.
   already_AddRefed<TextureClient>
   CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
 
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -101,29 +101,32 @@ YCbCrTextureClientAllocationHelper::IsCo
 {
   MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
 
   BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
   if (!bufferData ||
       aTextureClient->GetSize() != mData.mYSize ||
       bufferData->GetCbCrSize().isNothing() ||
       bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
+      bufferData->GetYUVColorSpace().isNothing() ||
+      bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
       bufferData->GetStereoMode().isNothing() ||
       bufferData->GetStereoMode().ref() != mData.mStereoMode) {
     return false;
   }
   return true;
 }
 
 already_AddRefed<TextureClient>
 YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
 {
   return TextureClient::CreateForYCbCr(aAllocator,
                                        mData.mYSize, mData.mCbCrSize,
                                        mData.mStereoMode,
+                                       mData.mYUVColorSpace,
                                        mTextureFlags);
 }
 
 TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
   : mSurfaceAllocator(aAllocator)
   , mMaxPooledSize(kMaxPooledSized)
   , mLock("TextureClientRecycleAllocatorImp.mLock")
   , mIsDestroyed(false)
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 using struct gfxPoint from "gfxPoint.h";
 using nsIntRegion from "nsRegion.h";
 using struct mozilla::layers::MagicGrallocBufferHandle from "gfxipc/ShadowLayerUtils.h";
 using struct mozilla::layers::GrallocBufferRef from "gfxipc/ShadowLayerUtils.h";
 using struct mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
 using mozilla::StereoMode from "ImageTypes.h";
+using mozilla::YUVColorSpace from "ImageTypes.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using gfxImageFormat from "gfxTypes.h";
 using struct mozilla::layers::GonkNativeHandle from "mozilla/layers/GonkNativeHandleUtils.h";
 
@@ -110,16 +111,17 @@ struct RGBDescriptor {
 
 struct YCbCrDescriptor {
   IntSize ySize;
   IntSize cbCrSize;
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;
   StereoMode stereoMode;
+  YUVColorSpace yUVColorSpace;
   bool hasIntermediateBuffer;
 };
 
 union BufferDescriptor {
   RGBDescriptor;
   YCbCrDescriptor;
 };
 
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -111,18 +111,20 @@ uint8_t*
 SharedPlanarYCbCrImage::AllocateAndGetNewBuffer(uint32_t aSize)
 {
   MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
   size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize);
   if (!size) {
     return nullptr;
   }
 
+  // XXX Add YUVColorSpace handling. Use YUVColorSpace::BT601 for now.
   mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(),
                                                                size,
+                                                               YUVColorSpace::BT601,
                                                                mCompositable->GetTextureFlags());
 
   // get new buffer _without_ setting mBuffer.
   if (!mTextureClient) {
     return nullptr;
   }
 
   // update buffer size
@@ -158,17 +160,17 @@ SharedPlanarYCbCrImage::AdoptData(const 
   uint32_t crOffset = aData.mCrChannel - base;
 
   auto fwd = mCompositable->GetForwarder();
   bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
                                                             fwd->GetCompositorBackendType());
 
   static_cast<BufferTextureData*>(mTextureClient->GetInternalData())->SetDesciptor(
     YCbCrDescriptor(aData.mYSize, aData.mCbCrSize, yOffset, cbOffset, crOffset,
-                    aData.mStereoMode, hasIntermediateBuffer)
+                    aData.mStereoMode, aData.mYUVColorSpace, hasIntermediateBuffer)
   );
 
   return true;
 }
 
 bool
 SharedPlanarYCbCrImage::IsValid() {
   return !!mTextureClient;
@@ -214,16 +216,17 @@ SharedPlanarYCbCrImage::Allocate(PlanarY
   mData.mCbChannel = aData.mCbChannel;
   mData.mCrChannel = aData.mCrChannel;
   mData.mYSize = aData.mYSize;
   mData.mCbCrSize = aData.mCbCrSize;
   mData.mPicX = aData.mPicX;
   mData.mPicY = aData.mPicY;
   mData.mPicSize = aData.mPicSize;
   mData.mStereoMode = aData.mStereoMode;
+  mData.mYUVColorSpace = aData.mYUVColorSpace;
   // those members are not always equal to aData's, due to potentially different
   // packing.
   mData.mYSkip = 0;
   mData.mCbSkip = 0;
   mData.mCrSkip = 0;
   mData.mYStride = mData.mYSize.width;
   mData.mCbCrStride = mData.mCbCrSize.width;
 
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -253,22 +253,24 @@ TEST(Layers, TextureYCbCrSerialization) 
   clientData.mCbChannel = cbSurface->Data();
   clientData.mCrChannel = crSurface->Data();
   clientData.mYSize = ySurface->GetSize();
   clientData.mPicSize = ySurface->GetSize();
   clientData.mCbCrSize = cbSurface->GetSize();
   clientData.mYStride = ySurface->Stride();
   clientData.mCbCrStride = cbSurface->Stride();
   clientData.mStereoMode = StereoMode::MONO;
+  clientData.mYUVColorSpace = YUVColorSpace::BT601;
   clientData.mYSkip = 0;
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
   RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(nullptr, clientData.mYSize, clientData.mCbCrSize,
-                                                               StereoMode::MONO, TextureFlags::DEALLOCATE_CLIENT);
+                                                               StereoMode::MONO, YUVColorSpace::BT601,
+                                                               TextureFlags::DEALLOCATE_CLIENT);
 
   TestTextureClientYCbCr(client, clientData);
 
   // XXX - Test more texture client types.
 }
--- a/gfx/ycbcr/YCbCrUtils.cpp
+++ b/gfx/ycbcr/YCbCrUtils.cpp
@@ -113,16 +113,17 @@ ConvertYCbCrToRGB(const layers::PlanarYC
                         aData.mPicSize.width,
                         aData.mPicSize.height,
                         aDestSize.width,
                         aDestSize.height,
                         aData.mYStride,
                         aData.mCbCrStride,
                         aStride,
                         yuvtype,
+                        aData.mYUVColorSpace,
                         FILTER_BILINEAR);
   } else { // no prescale
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
       ConvertYCbCrToRGB565(aData.mYChannel,
                            aData.mCbChannel,
                            aData.mCrChannel,
                            aDestBuffer,
@@ -142,14 +143,15 @@ ConvertYCbCrToRGB(const layers::PlanarYC
                           aDestBuffer,
                           aData.mPicX,
                           aData.mPicY,
                           aData.mPicSize.width,
                           aData.mPicSize.height,
                           aData.mYStride,
                           aData.mCbCrStride,
                           aStride,
-                          yuvtype);
+                          yuvtype,
+                          aData.mYUVColorSpace);
   }
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ycbcr/scale_yuv_argb.cpp
+++ b/gfx/ycbcr/scale_yuv_argb.cpp
@@ -66,16 +66,17 @@ struct YUVBuferIter {
   int src_stride_y;
   int src_stride_u;
   int src_stride_v;
   const uint8* src_y;
   const uint8* src_u;
   const uint8* src_v;
 
   uint32 src_fourcc;
+  const struct YuvConstants* yuvconstants;
   int y_index;
   const uint8* src_row_y;
   const uint8* src_row_u;
   const uint8* src_row_v;
 
   void (*YUVToARGBRow)(const uint8* y_buf,
                        const uint8* u_buf,
                        const uint8* v_buf,
@@ -195,25 +196,30 @@ static void YUVBuferIter_MoveToNextRowFo
   if (iter.y_index & 1) {
     iter.src_row_u += iter.src_stride_u;
     iter.src_row_v += iter.src_stride_v;
   }
   iter.y_index++;
 }
 
 static __inline void YUVBuferIter_ConvertToARGBRow(YUVBuferIter& iter, uint8* argb_row) {
-  iter.YUVToARGBRow(iter.src_row_y, iter.src_row_u, iter.src_row_v, argb_row, &kYuvI601Constants, iter.src_width);
+  iter.YUVToARGBRow(iter.src_row_y, iter.src_row_u, iter.src_row_v, argb_row, iter.yuvconstants, iter.src_width);
 }
 
-void YUVBuferIter_Init(YUVBuferIter& iter, uint32 src_fourcc) {
+void YUVBuferIter_Init(YUVBuferIter& iter, uint32 src_fourcc, mozilla::YUVColorSpace yuv_color_space) {
   iter.src_fourcc = src_fourcc;
   iter.y_index = 0;
   iter.src_row_y = iter.src_y;
   iter.src_row_u = iter.src_u;
   iter.src_row_v = iter.src_v;
+  if (yuv_color_space == mozilla::YUVColorSpace::BT709) {
+    iter.yuvconstants = &kYuvH709Constants;
+  } else {
+    iter.yuvconstants = &kYuvI601Constants;
+  }
 
   if (src_fourcc == FOURCC_I444) {
     YUVBuferIter_InitI444(iter);
     iter.MoveTo = YUVBuferIter_MoveToForI444;
     iter.MoveToNextRow = YUVBuferIter_MoveToNextRowForI444;
   } else if(src_fourcc == FOURCC_I422){
     YUVBuferIter_InitI422(iter);
     iter.MoveTo = YUVBuferIter_MoveToForI422;
@@ -236,17 +242,18 @@ static void ScaleYUVToARGBDown2(int src_
                                 int src_stride_v,
                                 int dst_stride_argb,
                                 const uint8* src_y,
                                 const uint8* src_u,
                                 const uint8* src_v,
                                 uint8* dst_argb,
                                 int x, int dx, int y, int dy,
                                 enum FilterMode filtering,
-                                uint32 src_fourcc) {
+                                uint32 src_fourcc,
+                                mozilla::YUVColorSpace yuv_color_space) {
   int j;
 
   // Allocate 2 rows of ARGB for source conversion.
   const int kRowSize = (src_width * 4 + 15) & ~15;
   align_buffer_64(argb_cnv_row, kRowSize * 2);
   uint8* argb_cnv_rowptr = argb_cnv_row;
   int argb_cnv_rowstride = kRowSize;
 
@@ -254,17 +261,17 @@ static void ScaleYUVToARGBDown2(int src_
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
 
   void (*ScaleARGBRowDown2)(const uint8* src_argb, ptrdiff_t src_stride,
                             uint8* dst_argb, int dst_width) =
     filtering == kFilterNone ? ScaleARGBRowDown2_C :
         (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_C :
         ScaleARGBRowDown2Box_C);
   assert(dx == 65536 * 2);  // Test scale factor of 2.
   assert((dy & 0x1ffff) == 0);  // Test vertical scale is multiple of 2.
@@ -372,17 +379,18 @@ static void ScaleYUVToARGBDownEven(int s
                                    int src_stride_v,
                                    int dst_stride_argb,
                                    const uint8* src_y,
                                    const uint8* src_u,
                                    const uint8* src_v,
                                    uint8* dst_argb,
                                    int x, int dx, int y, int dy,
                                    enum FilterMode filtering,
-                                   uint32 src_fourcc) {
+                                   uint32 src_fourcc,
+                                   mozilla::YUVColorSpace yuv_color_space) {
   int j;
   // Allocate 2 rows of ARGB for source conversion.
   const int kRowSize = (src_width * 4 + 15) & ~15;
   align_buffer_64(argb_cnv_row, kRowSize * 2);
   uint8* argb_cnv_rowptr = argb_cnv_row;
   int argb_cnv_rowstride = kRowSize;
 
   int col_step = dx >> 16;
@@ -419,17 +427,17 @@ static void ScaleYUVToARGBDownEven(int s
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
 
   const int dyi = dy >> 16;
   int lastyi = yi;
   YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
   // Prepare next row if necessary
   if (filtering != kFilterLinear) {
     if ((yi + dyi) < (src_height - 1)) {
       iter.MoveTo(iter, yi + dyi);
@@ -492,17 +500,18 @@ static void ScaleYUVToARGBBilinearDown(i
                                        int src_stride_v,
                                        int dst_stride_argb,
                                        const uint8* src_y,
                                        const uint8* src_u,
                                        const uint8* src_v,
                                        uint8* dst_argb,
                                        int x, int dx, int y, int dy,
                                        enum FilterMode filtering,
-                                       uint32 src_fourcc) {
+                                       uint32 src_fourcc,
+                                       mozilla::YUVColorSpace yuv_color_space) {
   int j;
   void (*InterpolateRow)(uint8* dst_argb, const uint8* src_argb,
       ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
       InterpolateRow_C;
   void (*ScaleARGBFilterCols)(uint8* dst_argb, const uint8* src_argb,
       int dst_width, int x, int dx) =
       (src_width >= 32768) ? ScaleARGBFilterCols64_C : ScaleARGBFilterCols_C;
   int64 xlast = x + (int64)(dst_width - 1) * dx;
@@ -578,17 +587,17 @@ static void ScaleYUVToARGBBilinearDown(i
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
   iter.MoveTo(iter, yi);
 
   // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
   // Allocate a row of ARGB.
   align_buffer_64(row, clip_src_width * 4);
 
   int lastyi = yi;
   YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_rowptr);
@@ -660,17 +669,18 @@ static void ScaleYUVToARGBBilinearUp(int
                                      int src_stride_v,
                                      int dst_stride_argb,
                                      const uint8* src_y,
                                      const uint8* src_u,
                                      const uint8* src_v,
                                      uint8* dst_argb,
                                      int x, int dx, int y, int dy,
                                      enum FilterMode filtering,
-                                     uint32 src_fourcc) {
+                                     uint32 src_fourcc,
+                                     mozilla::YUVColorSpace yuv_color_space) {
   int j;
   void (*InterpolateRow)(uint8* dst_argb, const uint8* src_argb,
       ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
       InterpolateRow_C;
   void (*ScaleARGBFilterCols)(uint8* dst_argb, const uint8* src_argb,
       int dst_width, int x, int dx) =
       filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
   const int max_y = (src_height - 1) << 16;
@@ -757,17 +767,17 @@ static void ScaleYUVToARGBBilinearUp(int
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
   iter.MoveTo(iter, yi);
 
   // Allocate 2 rows of ARGB.
   const int kRowSize = (dst_width * 4 + 15) & ~15;
   align_buffer_64(row, kRowSize * 2);
 
   uint8* rowptr = row;
   int rowstride = kRowSize;
@@ -843,17 +853,18 @@ static void ScaleYUVToARGBSimple(int src
                                  int src_stride_u,
                                  int src_stride_v,
                                  int dst_stride_argb,
                                  const uint8* src_y,
                                  const uint8* src_u,
                                  const uint8* src_v,
                                  uint8* dst_argb,
                                  int x, int dx, int y, int dy,
-                                 uint32 src_fourcc) {
+                                 uint32 src_fourcc,
+                                 mozilla::YUVColorSpace yuv_color_space) {
   int j;
   void (*ScaleARGBCols)(uint8* dst_argb, const uint8* src_argb,
       int dst_width, int x, int dx) =
       (src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
 
   // Allocate 1 row of ARGB for source conversion.
   align_buffer_64(argb_cnv_row, src_width * 4);
 
@@ -885,17 +896,17 @@ static void ScaleYUVToARGBSimple(int src
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
   iter.MoveTo(iter, yi);
 
   int lasty = yi;
   YUVBuferIter_ConvertToARGBRow(iter, argb_cnv_row);
 
   for (j = 0; j < dst_height; ++j) {
     yi = y >> 16;
     if (yi != lasty) {
@@ -911,44 +922,46 @@ static void ScaleYUVToARGBSimple(int src
 }
 
 static void YUVToARGBCopy(const uint8* src_y, int src_stride_y,
                           const uint8* src_u, int src_stride_u,
                           const uint8* src_v, int src_stride_v,
                           int src_width, int src_height,
                           uint8* dst_argb, int dst_stride_argb,
                           int dst_width, int dst_height,
-                          uint32 src_fourcc)
+                          uint32 src_fourcc,
+                          mozilla::YUVColorSpace yuv_color_space)
 {
   YUVBuferIter iter;
   iter.src_width = src_width;
   iter.src_height = src_height;
   iter.src_stride_y = src_stride_y;
   iter.src_stride_u = src_stride_u;
   iter.src_stride_v = src_stride_v;
   iter.src_y = src_y;
   iter.src_u = src_u;
   iter.src_v = src_v;
-  YUVBuferIter_Init(iter, src_fourcc);
+  YUVBuferIter_Init(iter, src_fourcc, yuv_color_space);
 
   for (int j = 0; j < dst_height; ++j) {
     YUVBuferIter_ConvertToARGBRow(iter, dst_argb);
     iter.MoveToNextRow(iter);
     dst_argb += dst_stride_argb;
   }
 }
 
 static void ScaleYUVToARGB(const uint8* src_y, int src_stride_y,
                            const uint8* src_u, int src_stride_u,
                            const uint8* src_v, int src_stride_v,
                            int src_width, int src_height,
                            uint8* dst_argb, int dst_stride_argb,
                            int dst_width, int dst_height,
                            enum FilterMode filtering,
-                           uint32 src_fourcc)
+                           uint32 src_fourcc,
+                           mozilla::YUVColorSpace yuv_color_space)
 {
   // Initial source x/y coordinate and step values as 16.16 fixed point.
   int x = 0;
   int y = 0;
   int dx = 0;
   int dy = 0;
   // ARGB does not support box filter yet, but allow the user to pass it.
   // Simplify filtering when possible.
@@ -974,46 +987,49 @@ static void ScaleYUVToARGB(const uint8* 
                               src_stride_v,
                               dst_stride_argb,
                               src_y,
                               src_u,
                               src_v,
                               dst_argb,
                               x, dx, y, dy,
                               filtering,
-                              src_fourcc);
+                              src_fourcc,
+                              yuv_color_space);
           return;
         }
         ScaleYUVToARGBDownEven(src_width, src_height,
                                dst_width, dst_height,
                                src_stride_y,
                                src_stride_u,
                                src_stride_v,
                                dst_stride_argb,
                                src_y,
                                src_u,
                                src_v,
                                dst_argb,
                                x, dx, y, dy,
                                filtering,
-                               src_fourcc);
+                               src_fourcc,
+                               yuv_color_space);
         return;
       }
       // Optimized odd scale down. ie 3, 5, 7, 9x.
       if ((dx & 0x10000) && (dy & 0x10000)) {
         filtering = kFilterNone;
         if (dx == 0x10000 && dy == 0x10000) {
           // Straight conversion and copy.
           YUVToARGBCopy(src_y, src_stride_y,
                         src_u, src_stride_u,
                         src_v, src_stride_v,
                         src_width, src_height,
                         dst_argb, dst_stride_argb,
                         dst_width, dst_height,
-                        src_fourcc);
+                        src_fourcc,
+                        yuv_color_space);
           return;
         }
       }
     }
   }
   if (filtering && dy < 65536) {
     ScaleYUVToARGBBilinearUp(src_width, src_height,
                              dst_width, dst_height,
@@ -1022,47 +1038,50 @@ static void ScaleYUVToARGB(const uint8* 
                              src_stride_v,
                              dst_stride_argb,
                              src_y,
                              src_u,
                              src_v,
                              dst_argb,
                              x, dx, y, dy,
                              filtering,
-                             src_fourcc);
+                             src_fourcc,
+                             yuv_color_space);
     return;
   }
   if (filtering) {
     ScaleYUVToARGBBilinearDown(src_width, src_height,
                                dst_width, dst_height,
                                src_stride_y,
                                src_stride_u,
                                src_stride_v,
                                dst_stride_argb,
                                src_y,
                                src_u,
                                src_v,
                                dst_argb,
                                x, dx, y, dy,
                                filtering,
-                               src_fourcc);
+                               src_fourcc,
+                               yuv_color_space);
     return;
   }
   ScaleYUVToARGBSimple(src_width, src_height,
                        dst_width, dst_height,
                        src_stride_y,
                        src_stride_u,
                        src_stride_v,
                        dst_stride_argb,
                        src_y,
                        src_u,
                        src_v,
                        dst_argb,
                        x, dx, y, dy,
-                       src_fourcc);
+                       src_fourcc,
+                       yuv_color_space);
 }
 
 bool IsConvertSupported(uint32 src_fourcc)
 {
   if (src_fourcc == FOURCC_I444 ||
       src_fourcc == FOURCC_I422 ||
       src_fourcc == FOURCC_I420) {
     return true;
@@ -1070,16 +1089,17 @@ bool IsConvertSupported(uint32 src_fourc
   return false;
 }
 
 LIBYUV_API
 int YUVToARGBScale(const uint8* src_y, int src_stride_y,
                    const uint8* src_u, int src_stride_u,
                    const uint8* src_v, int src_stride_v,
                    uint32 src_fourcc,
+                   mozilla::YUVColorSpace yuv_color_space,
                    int src_width, int src_height,
                    uint8* dst_argb, int dst_stride_argb,
                    int dst_width, int dst_height,
                    enum FilterMode filtering)
 {
   if (!src_y || !src_u || !src_v ||
       src_width == 0 || src_height == 0 ||
       !dst_argb || dst_width <= 0 || dst_height <= 0) {
@@ -1090,16 +1110,17 @@ int YUVToARGBScale(const uint8* src_y, i
   }
   ScaleYUVToARGB(src_y, src_stride_y,
                  src_u, src_stride_u,
                  src_v, src_stride_v,
                  src_width, src_height,
                  dst_argb, dst_stride_argb,
                  dst_width, dst_height,
                  filtering,
-                 src_fourcc);
+                 src_fourcc,
+                 yuv_color_space);
   return 0;
 }
 
 #ifdef __cplusplus
 }  // extern "C"
 }  // namespace libyuv
 #endif
--- a/gfx/ycbcr/scale_yuv_argb.h
+++ b/gfx/ycbcr/scale_yuv_argb.h
@@ -9,25 +9,28 @@
  */
 
 #ifndef INCLUDE_LIBYUV_SCALE_YUV_ARGB_H_  // NOLINT
 #define INCLUDE_LIBYUV_SCALE_YUV_ARGB_H_
 
 #include "libyuv/basic_types.h"
 #include "libyuv/scale.h"  // For FilterMode
 
+#include "ImageTypes.h" // For YUVColorSpace
+
 #ifdef __cplusplus
 namespace libyuv {
 extern "C" {
 #endif
 
 int YUVToARGBScale(const uint8* src_y, int src_stride_y,
                    const uint8* src_u, int src_stride_u,
                    const uint8* src_v, int src_stride_v,
                    uint32 src_fourcc,
+                   mozilla::YUVColorSpace yuv_color_space,
                    int src_width, int src_height,
                    uint8* dst_argb, int dst_stride_argb,
                    int dst_width, int dst_height,
                    enum FilterMode filtering);
 
 #ifdef __cplusplus
 }  // extern "C"
 }  // namespace libyuv
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -70,29 +70,36 @@ void ConvertYCbCrToRGB32(const uint8* y_
                          uint8* rgb_buf,
                          int pic_x,
                          int pic_y,
                          int pic_width,
                          int pic_height,
                          int y_pitch,
                          int uv_pitch,
                          int rgb_pitch,
-                         YUVType yuv_type) {
+                         YUVType yuv_type,
+                         YUVColorSpace yuv_color_space) {
 
 
   // Deprecated function's conversion is accurate.
   // libyuv converion is a bit inaccurate to get performance. It dynamically
   // calculates RGB from YUV to use simd. In it, signed byte is used for conversion's
   // coefficient, but it requests 129. libyuv cut 129 to 127. And only 6 bits are
   // used for a decimal part during the dynamic calculation.
   //
   // The function is still fast on some old intel chips.
   // See Bug 1256475.
   bool use_deprecated = gfxPrefs::YCbCrAccurateConversion() ||
-                        (supports_mmx() && supports_sse() && !supports_sse3());
+                        (supports_mmx() && supports_sse() && !supports_sse3() &&
+                         yuv_color_space == YUVColorSpace::BT601);
+  // The deprecated function only support BT601.
+  // See Bug 1210357.
+  if (yuv_color_space != YUVColorSpace::BT601) {
+    use_deprecated = false;
+  }
   if (use_deprecated) {
     ConvertYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf, rgb_buf,
                                    pic_x, pic_y, pic_width, pic_height,
                                    y_pitch, uv_pitch, rgb_pitch, yuv_type);
     return;
   }
                                     
   if (yuv_type == YV24) {
@@ -115,22 +122,32 @@ void ConvertYCbCrToRGB32(const uint8* y_
                                             rgb_buf, rgb_pitch,
                                             pic_width, pic_height);
     MOZ_ASSERT(!err);
   } else {
     MOZ_ASSERT(yuv_type == YV12);
     const uint8* src_y = y_buf + y_pitch * pic_y + pic_x;
     const uint8* src_u = u_buf + (uv_pitch * pic_y + pic_x) / 2;
     const uint8* src_v = v_buf + (uv_pitch * pic_y + pic_x) / 2;
-    DebugOnly<int> err = libyuv::I420ToARGB(src_y, y_pitch,
-                                            src_u, uv_pitch,
-                                            src_v, uv_pitch,
-                                            rgb_buf, rgb_pitch,
-                                            pic_width, pic_height);
-    MOZ_ASSERT(!err);
+    if (yuv_color_space == YUVColorSpace::BT709) {
+      DebugOnly<int> err = libyuv::H420ToARGB(src_y, y_pitch,
+                                              src_u, uv_pitch,
+                                              src_v, uv_pitch,
+                                              rgb_buf, rgb_pitch,
+                                              pic_width, pic_height);
+      MOZ_ASSERT(!err);
+    } else {
+      MOZ_ASSERT(yuv_color_space == YUVColorSpace::BT601);
+      DebugOnly<int> err = libyuv::I420ToARGB(src_y, y_pitch,
+                                              src_u, uv_pitch,
+                                              src_v, uv_pitch,
+                                              rgb_buf, rgb_pitch,
+                                              pic_width, pic_height);
+      MOZ_ASSERT(!err);
+    }
   }
 }
 
 // Convert a frame of YUV to 32 bit ARGB.
 void ConvertYCbCrToRGB32_deprecated(const uint8* y_buf,
                                     const uint8* u_buf,
                                     const uint8* v_buf,
                                     uint8* rgb_buf,
@@ -252,24 +269,30 @@ void ScaleYCbCrToRGB32(const uint8* y_bu
                        int source_width,
                        int source_height,
                        int width,
                        int height,
                        int y_pitch,
                        int uv_pitch,
                        int rgb_pitch,
                        YUVType yuv_type,
+                       YUVColorSpace yuv_color_space,
                        ScaleFilter filter) {
 
   bool use_deprecated = gfxPrefs::YCbCrAccurateConversion() ||
 #if defined(XP_WIN) && defined(_M_X64)
                         // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
                         supports_sse3() ||
 #endif
                         (supports_mmx() && supports_sse() && !supports_sse3());
+  // The deprecated function only support BT601.
+  // See Bug 1210357.
+  if (yuv_color_space != YUVColorSpace::BT601) {
+    use_deprecated = false;
+  }
   if (use_deprecated) {
     ScaleYCbCrToRGB32_deprecated(y_buf, u_buf, v_buf,
                                  rgb_buf,
                                  source_width, source_height,
                                  width, height,
                                  y_pitch, uv_pitch,
                                  rgb_pitch,
                                  yuv_type,
@@ -278,16 +301,17 @@ void ScaleYCbCrToRGB32(const uint8* y_bu
     return;
   }
 
   DebugOnly<int> err =
     libyuv::YUVToARGBScale(y_buf, y_pitch,
                            u_buf, uv_pitch,
                            v_buf, uv_pitch,
                            FourCCFromYUVType(yuv_type),
+                           yuv_color_space,
                            source_width, source_height,
                            rgb_buf, rgb_pitch,
                            width, height,
                            libyuv::kFilterBilinear);
   MOZ_ASSERT(!err);
   return;
 }
 
--- a/gfx/ycbcr/yuv_convert.h
+++ b/gfx/ycbcr/yuv_convert.h
@@ -1,16 +1,17 @@
 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef MEDIA_BASE_YUV_CONVERT_H_
 #define MEDIA_BASE_YUV_CONVERT_H_
 
 #include "chromium_types.h"
+#include "ImageTypes.h"
 
 namespace mozilla {
 
 namespace gfx {
  
 // Type of YUV surface.
 // The value of these enums matter as they are used to shift vertical indices.
 enum YUVType {
@@ -50,17 +51,18 @@ void ConvertYCbCrToRGB32(const uint8* yp
                          uint8* rgbframe,
                          int pic_x,
                          int pic_y,
                          int pic_width,
                          int pic_height,
                          int ystride,
                          int uvstride,
                          int rgbstride,
-                         YUVType yuv_type);
+                         YUVType yuv_type,
+                         YUVColorSpace yuv_color_space);
 
 void ConvertYCbCrToRGB32_deprecated(const uint8* yplane,
                                     const uint8* uplane,
                                     const uint8* vplane,
                                     uint8* rgbframe,
                                     int pic_x,
                                     int pic_y,
                                     int pic_width,
@@ -79,16 +81,17 @@ void ScaleYCbCrToRGB32(const uint8* ypla
                        int source_width,
                        int source_height,
                        int width,
                        int height,
                        int ystride,
                        int uvstride,
                        int rgbstride,
                        YUVType yuv_type,
+                       YUVColorSpace yuv_color_space,
                        ScaleFilter filter);
 
 void ScaleYCbCrToRGB32_deprecated(const uint8* yplane,
                                   const uint8* uplane,
                                   const uint8* vplane,
                                   uint8* rgbframe,
                                   int source_width,
                                   int source_height,