Coverage Report

Created: 2018-09-25 14:53

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