Bug 1321076 - Updated VPXDecoder to decode alpha frames. r=jya draft
authorkaro <kkoorts@mozilla.com>
Wed, 21 Dec 2016 14:49:50 +1300
changeset 452673 a287670aa62949e8811f678810eb3ac5e3acdbe3
parent 452404 10a5e2fc24e4e24fbe115fb190bcc28df4627ee2
child 452674 a2b4bad848c6350041c2cff805803fb5728342d2
push id39448
push userbmo:kkoorts@mozilla.com
push dateThu, 22 Dec 2016 00:02:10 +0000
reviewersjya
bugs1321076
milestone53.0a1
Bug 1321076 - Updated VPXDecoder to decode alpha frames. r=jya VPXDecoder initializes a second context when alpha is encountered. MozReview-Commit-ID: FMzHFvP8YK0
dom/media/platforms/agnostic/VPXDecoder.cpp
dom/media/platforms/agnostic/VPXDecoder.h
--- a/dom/media/platforms/agnostic/VPXDecoder.cpp
+++ b/dom/media/platforms/agnostic/VPXDecoder.cpp
@@ -29,65 +29,87 @@ static int MimeTypeToCodec(const nsACStr
   } else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
     return VPXDecoder::Codec::VP9;
   } else if (aMimeType.EqualsLiteral("video/vp9")) {
     return VPXDecoder::Codec::VP9;
   }
   return -1;
 }
 
+static nsresult
+InitContext(vpx_codec_ctx_t* aCtx,
+            const VideoInfo& aInfo,
+            const int aCodec)
+{
+  int decode_threads = 2;
+
+  vpx_codec_iface_t* dx = nullptr;
+  if (aCodec == VPXDecoder::Codec::VP8) {
+    dx = vpx_codec_vp8_dx();
+  }
+  else if (aCodec == VPXDecoder::Codec::VP9) {
+    dx = vpx_codec_vp9_dx();
+    if (aInfo.mDisplay.width >= 2048) {
+      decode_threads = 8;
+    }
+    else if (aInfo.mDisplay.width >= 1024) {
+      decode_threads = 4;
+    }
+  }
+  decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
+
+  vpx_codec_dec_cfg_t config;
+  config.threads = decode_threads;
+  config.w = config.h = 0; // set after decode
+
+  if (!dx || vpx_codec_dec_init(aCtx, dx, &config, 0)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
 VPXDecoder::VPXDecoder(const CreateDecoderParams& aParams)
   : mImageContainer(aParams.mImageContainer)
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(aParams.mCallback)
   , mIsFlushing(false)
   , mInfo(aParams.VideoConfig())
   , mCodec(MimeTypeToCodec(aParams.VideoConfig().mMimeType))
 {
   MOZ_COUNT_CTOR(VPXDecoder);
   PodZero(&mVPX);
+  PodZero(&mVPXAlpha);
 }
 
 VPXDecoder::~VPXDecoder()
 {
   MOZ_COUNT_DTOR(VPXDecoder);
 }
 
 void
 VPXDecoder::Shutdown()
 {
   vpx_codec_destroy(&mVPX);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 VPXDecoder::Init()
 {
-  int decode_threads = 2;
-
-  vpx_codec_iface_t* dx = nullptr;
-  if (mCodec == Codec::VP8) {
-    dx = vpx_codec_vp8_dx();
-  } else if (mCodec == Codec::VP9) {
-    dx = vpx_codec_vp9_dx();
-    if (mInfo.mDisplay.width >= 2048) {
-      decode_threads = 8;
-    } else if (mInfo.mDisplay.width >= 1024) {
-      decode_threads = 4;
+  if (NS_FAILED(InitContext(&mVPX, mInfo, mCodec))) {
+    return VPXDecoder::InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                                                    __func__);
+  }
+  if (mInfo.HasAlpha()) {
+    if (NS_FAILED(InitContext(&mVPXAlpha, mInfo, mCodec))) {
+      return VPXDecoder::InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                                                      __func__);
     }
   }
-  decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
-
-  vpx_codec_dec_cfg_t config;
-  config.threads = decode_threads;
-  config.w = config.h = 0; // set after decode
-
-  if (!dx || vpx_codec_dec_init(&mVPX, dx, &config, 0)) {
-    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
-  }
-  return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+  return VPXDecoder::InitPromise::CreateAndResolve(TrackInfo::kVideoTrack,
+                                                   __func__);
 }
 
 void
 VPXDecoder::Flush()
 {
   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
   mIsFlushing = true;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([this] () {
@@ -116,24 +138,37 @@ VPXDecoder::DoDecode(MediaRawData* aSamp
 
   if (vpx_codec_err_t r = vpx_codec_decode(&mVPX, aSample->Data(), aSample->Size(), nullptr, 0)) {
     LOG("VPX Decode error: %s", vpx_codec_err_to_string(r));
     return MediaResult(
       NS_ERROR_DOM_MEDIA_DECODE_ERR,
       RESULT_DETAIL("VPX error: %s", vpx_codec_err_to_string(r)));
   }
 
-  vpx_codec_iter_t  iter = nullptr;
-  vpx_image_t      *img;
+  vpx_codec_iter_t iter = nullptr;
+  vpx_image_t *img;
+  vpx_image_t *img_alpha = nullptr;
+  bool alpha_decoded = false;
 
   while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
     NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420 ||
                  img->fmt == VPX_IMG_FMT_I444,
                  "WebM image format not I420 or I444");
+    NS_ASSERTION(!alpha_decoded,
+                 "Multiple frames per packet that contains alpha");
 
+    if (aSample->AlphaSize() > 0) {
+      if(!alpha_decoded){
+        MediaResult rv = DecodeAlpha(&img_alpha, aSample);
+        if (NS_FAILED(rv)) {
+          return(rv);
+        }
+        alpha_decoded = true;
+      }
+    }
     // Chroma shifts are rounded down as per the decoding examples in the SDK
     VideoData::YCbCrBuffer b;
     b.mPlanes[0].mData = img->planes[0];
     b.mPlanes[0].mStride = img->stride[0];
     b.mPlanes[0].mHeight = img->d_h;
     b.mPlanes[0].mWidth = img->d_w;
     b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
 
@@ -218,16 +253,42 @@ VPXDecoder::ProcessDrain()
 
 void
 VPXDecoder::Drain()
 {
   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
   mTaskQueue->Dispatch(NewRunnableMethod(this, &VPXDecoder::ProcessDrain));
 }
 
+MediaResult
+VPXDecoder::DecodeAlpha(vpx_image_t** aImgAlpha,
+                        MediaRawData* aSample)
+{
+  vpx_codec_err_t r = vpx_codec_decode(&mVPXAlpha,
+                                       aSample->AlphaData(),
+                                       aSample->AlphaSize(),
+                                       nullptr,
+                                       0);
+  if (r) {
+    LOG("VPX decode alpha error: %s", vpx_codec_err_to_string(r));
+    return MediaResult(
+      NS_ERROR_DOM_MEDIA_DECODE_ERR,
+      RESULT_DETAIL("VPX decode alpha error: %s", vpx_codec_err_to_string(r)));
+  }
+
+  vpx_codec_iter_t iter = nullptr;
+
+  *aImgAlpha = vpx_codec_get_frame(&mVPXAlpha, &iter);
+  NS_ASSERTION((*aImgAlpha)->fmt == VPX_IMG_FMT_I420 ||
+               (*aImgAlpha)->fmt == VPX_IMG_FMT_I444,
+               "WebM image format not I420 or I444");
+
+  return NS_OK;
+}
+
 /* static */
 bool
 VPXDecoder::IsVPX(const nsACString& aMimeType, uint8_t aCodecMask)
 {
   return ((aCodecMask & VPXDecoder::VP8) &&
           aMimeType.EqualsLiteral("video/webm; codecs=vp8")) ||
          ((aCodecMask & VPXDecoder::VP9) &&
           aMimeType.EqualsLiteral("video/webm; codecs=vp9")) ||
--- a/dom/media/platforms/agnostic/VPXDecoder.h
+++ b/dom/media/platforms/agnostic/VPXDecoder.h
@@ -45,25 +45,30 @@ public:
   static bool IsVPX(const nsACString& aMimeType, uint8_t aCodecMask=VP8|VP9);
   static bool IsVP8(const nsACString& aMimeType);
   static bool IsVP9(const nsACString& aMimeType);
 
 private:
   void ProcessDecode(MediaRawData* aSample);
   MediaResult DoDecode(MediaRawData* aSample);
   void ProcessDrain();
+  MediaResult DecodeAlpha(vpx_image_t** aImgAlpha,
+                          MediaRawData* aSample);
 
   const RefPtr<ImageContainer> mImageContainer;
   const RefPtr<TaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   Atomic<bool> mIsFlushing;
 
   // VPx decoder state
   vpx_codec_ctx_t mVPX;
 
+  // VPx alpha decoder state
+  vpx_codec_ctx_t mVPXAlpha;
+
   const VideoInfo& mInfo;
 
   const int mCodec;
 };
 
 } // namespace mozilla
 
 #endif