LCOV - code coverage report
Current view: top level - dom/media/platforms/agnostic - VPXDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 168 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 22 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "VPXDecoder.h"
       8             : #include "TimeUnits.h"
       9             : #include "gfx2DGlue.h"
      10             : #include "mozilla/PodOperations.h"
      11             : #include "mozilla/SyncRunnable.h"
      12             : #include "nsError.h"
      13             : #include "prsystem.h"
      14             : 
      15             : #include <algorithm>
      16             : 
      17             : #undef LOG
      18             : #define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("VPXDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
      19             : 
      20             : namespace mozilla {
      21             : 
      22             : using namespace gfx;
      23             : using namespace layers;
      24             : 
      25           0 : static VPXDecoder::Codec MimeTypeToCodec(const nsACString& aMimeType)
      26             : {
      27           0 :   if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
      28           0 :     return VPXDecoder::Codec::VP8;
      29           0 :   } else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
      30           0 :     return VPXDecoder::Codec::VP9;
      31           0 :   } else if (aMimeType.EqualsLiteral("video/vp9")) {
      32           0 :     return VPXDecoder::Codec::VP9;
      33             :   }
      34           0 :   return VPXDecoder::Codec::Unknown;
      35             : }
      36             : 
      37             : static nsresult
      38           0 : InitContext(vpx_codec_ctx_t* aCtx,
      39             :             const VideoInfo& aInfo,
      40             :             const VPXDecoder::Codec aCodec)
      41             : {
      42           0 :   int decode_threads = 2;
      43             : 
      44           0 :   vpx_codec_iface_t* dx = nullptr;
      45           0 :   if (aCodec == VPXDecoder::Codec::VP8) {
      46           0 :     dx = vpx_codec_vp8_dx();
      47             :   }
      48           0 :   else if (aCodec == VPXDecoder::Codec::VP9) {
      49           0 :     dx = vpx_codec_vp9_dx();
      50           0 :     if (aInfo.mDisplay.width >= 2048) {
      51           0 :       decode_threads = 8;
      52             :     }
      53           0 :     else if (aInfo.mDisplay.width >= 1024) {
      54           0 :       decode_threads = 4;
      55             :     }
      56             :   }
      57           0 :   decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
      58             : 
      59             :   vpx_codec_dec_cfg_t config;
      60           0 :   config.threads = decode_threads;
      61           0 :   config.w = config.h = 0; // set after decode
      62             : 
      63           0 :   if (!dx || vpx_codec_dec_init(aCtx, dx, &config, 0)) {
      64           0 :     return NS_ERROR_FAILURE;
      65             :   }
      66           0 :   return NS_OK;
      67             : }
      68             : 
      69           0 : VPXDecoder::VPXDecoder(const CreateDecoderParams& aParams)
      70           0 :   : mImageContainer(aParams.mImageContainer)
      71             :   , mImageAllocator(aParams.mKnowsCompositor)
      72           0 :   , mTaskQueue(aParams.mTaskQueue)
      73           0 :   , mInfo(aParams.VideoConfig())
      74           0 :   , mCodec(MimeTypeToCodec(aParams.VideoConfig().mMimeType))
      75             : {
      76           0 :   MOZ_COUNT_CTOR(VPXDecoder);
      77           0 :   PodZero(&mVPX);
      78           0 :   PodZero(&mVPXAlpha);
      79           0 : }
      80             : 
      81           0 : VPXDecoder::~VPXDecoder()
      82             : {
      83           0 :   MOZ_COUNT_DTOR(VPXDecoder);
      84           0 : }
      85             : 
      86             : RefPtr<ShutdownPromise>
      87           0 : VPXDecoder::Shutdown()
      88             : {
      89           0 :   RefPtr<VPXDecoder> self = this;
      90           0 :   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
      91           0 :     vpx_codec_destroy(&mVPX);
      92           0 :     vpx_codec_destroy(&mVPXAlpha);
      93           0 :     return ShutdownPromise::CreateAndResolve(true, __func__);
      94           0 :   });
      95             : }
      96             : 
      97             : RefPtr<MediaDataDecoder::InitPromise>
      98           0 : VPXDecoder::Init()
      99             : {
     100           0 :   if (NS_FAILED(InitContext(&mVPX, mInfo, mCodec))) {
     101             :     return VPXDecoder::InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     102           0 :                                                     __func__);
     103             :   }
     104           0 :   if (mInfo.HasAlpha()) {
     105           0 :     if (NS_FAILED(InitContext(&mVPXAlpha, mInfo, mCodec))) {
     106             :       return VPXDecoder::InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     107           0 :                                                       __func__);
     108             :     }
     109             :   }
     110             :   return VPXDecoder::InitPromise::CreateAndResolve(TrackInfo::kVideoTrack,
     111           0 :                                                    __func__);
     112             : }
     113             : 
     114             : RefPtr<MediaDataDecoder::FlushPromise>
     115           0 : VPXDecoder::Flush()
     116             : {
     117           0 :   return InvokeAsync(mTaskQueue, __func__, []() {
     118             :     return FlushPromise::CreateAndResolve(true, __func__);
     119           0 :   });
     120             : }
     121             : 
     122             : RefPtr<MediaDataDecoder::DecodePromise>
     123           0 : VPXDecoder::ProcessDecode(MediaRawData* aSample)
     124             : {
     125           0 :   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     126             : 
     127             : #if defined(DEBUG)
     128           0 :   NS_ASSERTION(IsKeyframe(*aSample, mCodec) == aSample->mKeyframe,
     129             :                "VPX Decode Keyframe error sample->mKeyframe and sample data out of sync");
     130             : #endif
     131             : 
     132           0 :   if (vpx_codec_err_t r = vpx_codec_decode(&mVPX, aSample->Data(), aSample->Size(), nullptr, 0)) {
     133           0 :     LOG("VPX Decode error: %s", vpx_codec_err_to_string(r));
     134             :     return DecodePromise::CreateAndReject(
     135           0 :       MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
     136           0 :                   RESULT_DETAIL("VPX error: %s", vpx_codec_err_to_string(r))),
     137           0 :       __func__);
     138             :   }
     139             : 
     140           0 :   vpx_codec_iter_t iter = nullptr;
     141             :   vpx_image_t *img;
     142           0 :   vpx_image_t *img_alpha = nullptr;
     143           0 :   bool alpha_decoded = false;
     144           0 :   DecodedData results;
     145             : 
     146           0 :   while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
     147           0 :     NS_ASSERTION(img->fmt == VPX_IMG_FMT_I420 ||
     148             :                  img->fmt == VPX_IMG_FMT_I444,
     149             :                  "WebM image format not I420 or I444");
     150           0 :     NS_ASSERTION(!alpha_decoded,
     151             :                  "Multiple frames per packet that contains alpha");
     152             : 
     153           0 :     if (aSample->AlphaSize() > 0) {
     154           0 :       if (!alpha_decoded){
     155           0 :         MediaResult rv = DecodeAlpha(&img_alpha, aSample);
     156           0 :         if (NS_FAILED(rv)) {
     157           0 :           return DecodePromise::CreateAndReject(rv, __func__);
     158             :         }
     159           0 :         alpha_decoded = true;
     160             :       }
     161             :     }
     162             :     // Chroma shifts are rounded down as per the decoding examples in the SDK
     163           0 :     VideoData::YCbCrBuffer b;
     164           0 :     b.mPlanes[0].mData = img->planes[0];
     165           0 :     b.mPlanes[0].mStride = img->stride[0];
     166           0 :     b.mPlanes[0].mHeight = img->d_h;
     167           0 :     b.mPlanes[0].mWidth = img->d_w;
     168           0 :     b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
     169             : 
     170           0 :     b.mPlanes[1].mData = img->planes[1];
     171           0 :     b.mPlanes[1].mStride = img->stride[1];
     172           0 :     b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
     173             : 
     174           0 :     b.mPlanes[2].mData = img->planes[2];
     175           0 :     b.mPlanes[2].mStride = img->stride[2];
     176           0 :     b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
     177             : 
     178           0 :     if (img->fmt == VPX_IMG_FMT_I420) {
     179           0 :       b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
     180           0 :       b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
     181             : 
     182           0 :       b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
     183           0 :       b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
     184           0 :     } else if (img->fmt == VPX_IMG_FMT_I444) {
     185           0 :       b.mPlanes[1].mHeight = img->d_h;
     186           0 :       b.mPlanes[1].mWidth = img->d_w;
     187             : 
     188           0 :       b.mPlanes[2].mHeight = img->d_h;
     189           0 :       b.mPlanes[2].mWidth = img->d_w;
     190             :     } else {
     191           0 :       LOG("VPX Unknown image format");
     192             :       return DecodePromise::CreateAndReject(
     193           0 :         MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
     194           0 :                     RESULT_DETAIL("VPX Unknown image format")),
     195           0 :         __func__);
     196             :     }
     197             : 
     198           0 :     RefPtr<VideoData> v;
     199           0 :     if (!img_alpha) {
     200           0 :       v = VideoData::CreateAndCopyData(mInfo,
     201             :                                        mImageContainer,
     202             :                                        aSample->mOffset,
     203             :                                        aSample->mTime,
     204             :                                        aSample->mDuration,
     205             :                                        b,
     206           0 :                                        aSample->mKeyframe,
     207             :                                        aSample->mTimecode,
     208           0 :                                        mInfo.ScaledImageRect(img->d_w,
     209           0 :                                                              img->d_h),
     210           0 :                                        mImageAllocator);
     211             :     } else {
     212             :       VideoData::YCbCrBuffer::Plane alpha_plane;
     213           0 :       alpha_plane.mData = img_alpha->planes[0];
     214           0 :       alpha_plane.mStride = img_alpha->stride[0];
     215           0 :       alpha_plane.mHeight = img_alpha->d_h;
     216           0 :       alpha_plane.mWidth = img_alpha->d_w;
     217           0 :       alpha_plane.mOffset = alpha_plane.mSkip = 0;
     218           0 :       v = VideoData::CreateAndCopyData(mInfo,
     219             :                                        mImageContainer,
     220             :                                        aSample->mOffset,
     221             :                                        aSample->mTime,
     222             :                                        aSample->mDuration,
     223             :                                        b,
     224             :                                        alpha_plane,
     225           0 :                                        aSample->mKeyframe,
     226             :                                        aSample->mTimecode,
     227           0 :                                        mInfo.ScaledImageRect(img->d_w,
     228           0 :                                                              img->d_h));
     229             : 
     230             :     }
     231             : 
     232           0 :     if (!v) {
     233           0 :       LOG(
     234             :         "Image allocation error source %ux%u display %ux%u picture %ux%u",
     235             :         img->d_w, img->d_h, mInfo.mDisplay.width, mInfo.mDisplay.height,
     236             :         mInfo.mImage.width, mInfo.mImage.height);
     237             :       return DecodePromise::CreateAndReject(
     238           0 :         MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
     239             :     }
     240           0 :     results.AppendElement(Move(v));
     241             :   }
     242           0 :   return DecodePromise::CreateAndResolve(Move(results), __func__);
     243             : }
     244             : 
     245             : RefPtr<MediaDataDecoder::DecodePromise>
     246           0 : VPXDecoder::Decode(MediaRawData* aSample)
     247             : {
     248             :   return InvokeAsync<MediaRawData*>(mTaskQueue, this, __func__,
     249           0 :                                     &VPXDecoder::ProcessDecode, aSample);
     250             : }
     251             : 
     252             : RefPtr<MediaDataDecoder::DecodePromise>
     253           0 : VPXDecoder::Drain()
     254             : {
     255           0 :   return InvokeAsync(mTaskQueue, __func__, [] {
     256           0 :     return DecodePromise::CreateAndResolve(DecodedData(), __func__);
     257           0 :   });
     258             : }
     259             : 
     260             : MediaResult
     261           0 : VPXDecoder::DecodeAlpha(vpx_image_t** aImgAlpha, const MediaRawData* aSample)
     262             : {
     263           0 :   vpx_codec_err_t r = vpx_codec_decode(&mVPXAlpha,
     264             :                                        aSample->AlphaData(),
     265           0 :                                        aSample->AlphaSize(),
     266             :                                        nullptr,
     267           0 :                                        0);
     268           0 :   if (r) {
     269           0 :     LOG("VPX decode alpha error: %s", vpx_codec_err_to_string(r));
     270             :     return MediaResult(
     271             :       NS_ERROR_DOM_MEDIA_DECODE_ERR,
     272           0 :       RESULT_DETAIL("VPX decode alpha error: %s", vpx_codec_err_to_string(r)));
     273             :   }
     274             : 
     275           0 :   vpx_codec_iter_t iter = nullptr;
     276             : 
     277           0 :   *aImgAlpha = vpx_codec_get_frame(&mVPXAlpha, &iter);
     278           0 :   NS_ASSERTION((*aImgAlpha)->fmt == VPX_IMG_FMT_I420 ||
     279             :                (*aImgAlpha)->fmt == VPX_IMG_FMT_I444,
     280             :                "WebM image format not I420 or I444");
     281             : 
     282           0 :   return NS_OK;
     283             : }
     284             : 
     285             : /* static */
     286             : bool
     287           0 : VPXDecoder::IsVPX(const nsACString& aMimeType, uint8_t aCodecMask)
     288             : {
     289           0 :   return ((aCodecMask & VPXDecoder::VP8)
     290           0 :           && aMimeType.EqualsLiteral("video/webm; codecs=vp8"))
     291           0 :          || ((aCodecMask & VPXDecoder::VP9)
     292           0 :              && aMimeType.EqualsLiteral("video/webm; codecs=vp9"))
     293           0 :          || ((aCodecMask & VPXDecoder::VP9)
     294           0 :              && aMimeType.EqualsLiteral("video/vp9"));
     295             : }
     296             : 
     297             : /* static */
     298             : bool
     299           0 : VPXDecoder::IsVP8(const nsACString& aMimeType)
     300             : {
     301           0 :   return IsVPX(aMimeType, VPXDecoder::VP8);
     302             : }
     303             : 
     304             : /* static */
     305             : bool
     306           0 : VPXDecoder::IsVP9(const nsACString& aMimeType)
     307             : {
     308           0 :   return IsVPX(aMimeType, VPXDecoder::VP9);
     309             : }
     310             : 
     311             : /* static */
     312             : bool
     313           0 : VPXDecoder::IsKeyframe(Span<const uint8_t> aBuffer, Codec aCodec)
     314             : {
     315             :   vpx_codec_stream_info_t si;
     316           0 :   PodZero(&si);
     317           0 :   si.sz = sizeof(si);
     318             : 
     319           0 :   if (aCodec == Codec::VP8) {
     320           0 :     vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aBuffer.Elements(), aBuffer.Length(), &si);
     321           0 :     return bool(si.is_kf);
     322           0 :   } else if (aCodec == Codec::VP9) {
     323           0 :     vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aBuffer.Elements(), aBuffer.Length(), &si);
     324           0 :     return bool(si.is_kf);
     325             :   }
     326             : 
     327           0 :   return false;
     328             : }
     329             : 
     330             : /* static */
     331             : nsIntSize
     332           0 : VPXDecoder::GetFrameSize(Span<const uint8_t> aBuffer, Codec aCodec)
     333             : {
     334             :   vpx_codec_stream_info_t si;
     335           0 :   PodZero(&si);
     336           0 :   si.sz = sizeof(si);
     337             : 
     338           0 :   if (aCodec == Codec::VP8) {
     339           0 :     vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), aBuffer.Elements(), aBuffer.Length(), &si);
     340           0 :   } else if (aCodec == Codec::VP9) {
     341           0 :     vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), aBuffer.Elements(), aBuffer.Length(), &si);
     342             :   }
     343             : 
     344           0 :   return nsIntSize(si.w, si.h);
     345             : }
     346             : } // namespace mozilla
     347             : #undef LOG

Generated by: LCOV version 1.13