1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/filters/vpx_video_decoder.h"
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/feature_list.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/sys_byteorder.h"
21 #include "base/trace_event/trace_event.h"
22 #include "media/base/bind_to_current_loop.h"
23 #include "media/base/decoder_buffer.h"
24 #include "media/base/limits.h"
25 #include "media/base/media_switches.h"
26 #include "media/base/video_aspect_ratio.h"
27 #include "media/filters/frame_buffer_pool.h"
28 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
29 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
30 #include "third_party/libvpx/source/libvpx/vpx/vpx_frame_buffer.h"
32 #include "third_party/libyuv/include/libyuv/convert.h"
33 #include "third_party/libyuv/include/libyuv/planar_functions.h"
37 // Returns the number of threads.
38 static int GetVpxVideoDecoderThreadCount(const VideoDecoderConfig& config) {
39 // vp8a doesn't really need more threads.
40 int desired_threads = limits::kMinVideoDecodeThreads;
42 // For VP9 decoding increase the number of decode threads to equal the
43 // maximum number of tiles possible for higher resolution streams.
44 if (config.codec() == VideoCodec::kVP9) {
45 const int width = config.coded_size().width();
48 else if (width >= 2560)
50 else if (width >= 1280)
54 return VideoDecoder::GetRecommendedThreadCount(desired_threads);
57 static std::unique_ptr<vpx_codec_ctx> InitializeVpxContext(
58 const VideoDecoderConfig& config) {
59 auto context = std::make_unique<vpx_codec_ctx>();
60 vpx_codec_dec_cfg_t vpx_config = {0};
61 vpx_config.w = config.coded_size().width();
62 vpx_config.h = config.coded_size().height();
63 vpx_config.threads = GetVpxVideoDecoderThreadCount(config);
65 vpx_codec_err_t status = vpx_codec_dec_init(context.get(),
66 config.codec() == VideoCodec::kVP9
69 &vpx_config, 0 /* flags */);
70 if (status == VPX_CODEC_OK)
73 DLOG(ERROR) << "vpx_codec_dec_init() failed: "
74 << vpx_codec_error(context.get());
78 static int32_t GetVP9FrameBuffer(void* user_priv,
80 vpx_codec_frame_buffer* fb) {
83 FrameBufferPool* pool = static_cast<FrameBufferPool*>(user_priv);
84 fb->data = pool->GetFrameBuffer(min_size, &fb->priv);
86 return fb->data ? 0 : VPX_CODEC_MEM_ERROR;
89 static int32_t ReleaseVP9FrameBuffer(void* user_priv,
90 vpx_codec_frame_buffer* fb) {
96 FrameBufferPool* pool = static_cast<FrameBufferPool*>(user_priv);
97 pool->ReleaseFrameBuffer(fb->priv);
102 SupportedVideoDecoderConfigs VpxVideoDecoder::SupportedConfigs() {
103 SupportedVideoDecoderConfigs supported_configs;
104 supported_configs.emplace_back(/*profile_min=*/VP8PROFILE_ANY,
105 /*profile_max=*/VP8PROFILE_ANY,
106 /*coded_size_min=*/kDefaultSwDecodeSizeMin,
107 /*coded_size_max=*/kDefaultSwDecodeSizeMax,
108 /*allow_encrypted=*/false,
109 /*require_encrypted=*/false);
111 supported_configs.emplace_back(/*profile_min=*/VP9PROFILE_PROFILE0,
112 /*profile_max=*/VP9PROFILE_PROFILE2,
113 /*coded_size_min=*/kDefaultSwDecodeSizeMin,
114 /*coded_size_max=*/kDefaultSwDecodeSizeMax,
115 /*allow_encrypted=*/false,
116 /*require_encrypted=*/false);
117 return supported_configs;
120 VpxVideoDecoder::VpxVideoDecoder(OffloadState offload_state)
121 : bind_callbacks_(offload_state == OffloadState::kNormal) {
122 DETACH_FROM_SEQUENCE(sequence_checker_);
125 VpxVideoDecoder::~VpxVideoDecoder() {
126 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
130 VideoDecoderType VpxVideoDecoder::GetDecoderType() const {
131 return VideoDecoderType::kVpx;
134 void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config,
135 bool /* low_delay */,
136 CdmContext* /* cdm_context */,
138 const OutputCB& output_cb,
139 const WaitingCB& /* waiting_cb */) {
140 DVLOG(1) << __func__ << ": " << config.AsHumanReadableString();
141 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
142 DCHECK(config.IsValidConfig());
146 InitCB bound_init_cb = bind_callbacks_ ? BindToCurrentLoop(std::move(init_cb))
147 : std::move(init_cb);
148 if (config.is_encrypted()) {
149 std::move(bound_init_cb)
150 .Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
154 if (!ConfigureDecoder(config)) {
155 std::move(bound_init_cb).Run(DecoderStatus::Codes::kUnsupportedConfig);
161 state_ = DecoderState::kNormal;
162 output_cb_ = output_cb;
163 std::move(bound_init_cb).Run(DecoderStatus::Codes::kOk);
166 void VpxVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
167 DecodeCB decode_cb) {
168 DVLOG(3) << __func__ << ": " << buffer->AsHumanReadableString();
169 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
172 DCHECK_NE(state_, DecoderState::kUninitialized)
173 << "Called Decode() before successful Initialize()";
175 DecodeCB bound_decode_cb = bind_callbacks_
176 ? BindToCurrentLoop(std::move(decode_cb))
177 : std::move(decode_cb);
179 if (state_ == DecoderState::kError) {
180 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kFailed);
184 if (state_ == DecoderState::kDecodeFinished) {
185 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kOk);
189 if (state_ == DecoderState::kNormal && buffer->end_of_stream()) {
190 state_ = DecoderState::kDecodeFinished;
191 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kOk);
195 scoped_refptr<VideoFrame> video_frame;
196 if (!VpxDecode(buffer.get(), &video_frame)) {
197 state_ = DecoderState::kError;
198 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kFailed);
202 // We might get a successful VpxDecode but not a frame if only a partial
205 video_frame->metadata().power_efficient = false;
206 output_cb_.Run(video_frame);
209 // VideoDecoderShim expects |decode_cb| call after |output_cb_|.
210 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kOk);
213 void VpxVideoDecoder::Reset(base::OnceClosure reset_cb) {
214 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
215 state_ = DecoderState::kNormal;
218 BindToCurrentLoop(std::move(reset_cb)).Run();
220 std::move(reset_cb).Run();
222 // Allow Initialize() to be called on another thread now.
223 DETACH_FROM_SEQUENCE(sequence_checker_);
226 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
227 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
228 if (config.codec() != VideoCodec::kVP8 && config.codec() != VideoCodec::kVP9)
231 #if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
232 // When enabled, ffmpeg handles VP8 that doesn't have alpha, and
233 // VpxVideoDecoder will handle VP8 with alpha. FFvp8 is being deprecated.
234 // See http://crbug.com/992235.
235 if (base::FeatureList::IsEnabled(kFFmpegDecodeOpaqueVP8) &&
236 config.codec() == VideoCodec::kVP8 &&
237 config.alpha_mode() == VideoDecoderConfig::AlphaMode::kIsOpaque) {
243 vpx_codec_ = InitializeVpxContext(config);
247 // Configure VP9 to decode on our buffers to skip a data copy on
248 // decoding. For YV12A-VP9, we use our buffers for the Y, U and V planes and
250 if (config.codec() == VideoCodec::kVP9) {
251 DCHECK(vpx_codec_get_caps(vpx_codec_->iface) &
252 VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER);
254 DCHECK(!memory_pool_);
255 memory_pool_ = new FrameBufferPool();
257 if (vpx_codec_set_frame_buffer_functions(
258 vpx_codec_.get(), &GetVP9FrameBuffer, &ReleaseVP9FrameBuffer,
259 memory_pool_.get())) {
260 DLOG(ERROR) << "Failed to configure external buffers. "
261 << vpx_codec_error(vpx_codec_.get());
265 vpx_codec_err_t status =
266 vpx_codec_control(vpx_codec_.get(), VP9D_SET_LOOP_FILTER_OPT, 1);
267 if (status != VPX_CODEC_OK) {
268 DLOG(ERROR) << "Failed to enable VP9D_SET_LOOP_FILTER_OPT. "
269 << vpx_codec_error(vpx_codec_.get());
274 if (config.alpha_mode() == VideoDecoderConfig::AlphaMode::kIsOpaque)
277 DCHECK(!vpx_codec_alpha_);
278 vpx_codec_alpha_ = InitializeVpxContext(config);
279 return !!vpx_codec_alpha_;
282 void VpxVideoDecoder::CloseDecoder() {
283 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
285 // Note: The vpx_codec_destroy() calls below don't release the memory
286 // allocated for vpx_codec_ctx, they just release internal allocations, so we
287 // still need std::unique_ptr to release the structure memory.
289 vpx_codec_destroy(vpx_codec_.get());
291 if (vpx_codec_alpha_)
292 vpx_codec_destroy(vpx_codec_alpha_.get());
295 vpx_codec_alpha_.reset();
298 memory_pool_->Shutdown();
299 memory_pool_ = nullptr;
303 void VpxVideoDecoder::Detach() {
304 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
305 DCHECK(!bind_callbacks_);
308 DETACH_FROM_SEQUENCE(sequence_checker_);
311 bool VpxVideoDecoder::VpxDecode(const DecoderBuffer* buffer,
312 scoped_refptr<VideoFrame>* video_frame) {
313 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
315 DCHECK(!buffer->end_of_stream());
318 TRACE_EVENT1("media", "vpx_codec_decode", "buffer",
319 buffer->AsHumanReadableString());
320 vpx_codec_err_t status =
321 vpx_codec_decode(vpx_codec_.get(), buffer->data(), buffer->data_size(),
322 nullptr /* user_priv */, 0 /* deadline */);
323 if (status != VPX_CODEC_OK) {
324 DLOG(ERROR) << "vpx_codec_decode() error: "
325 << vpx_codec_err_to_string(status);
330 // Gets pointer to decoded data.
331 vpx_codec_iter_t iter = NULL;
332 const vpx_image_t* vpx_image = vpx_codec_get_frame(vpx_codec_.get(), &iter);
334 *video_frame = nullptr;
338 const vpx_image_t* vpx_image_alpha = nullptr;
339 const auto alpha_decode_status =
340 DecodeAlphaPlane(vpx_image, &vpx_image_alpha, buffer);
341 if (alpha_decode_status == kAlphaPlaneError) {
343 } else if (alpha_decode_status == kNoAlphaPlaneData) {
344 *video_frame = nullptr;
348 if (!CopyVpxImageToVideoFrame(vpx_image, vpx_image_alpha, video_frame))
351 if (vpx_image_alpha && config_.codec() == VideoCodec::kVP8) {
353 vpx_image_alpha->planes[VPX_PLANE_Y],
354 vpx_image_alpha->stride[VPX_PLANE_Y],
355 (*video_frame)->GetWritableVisibleData(VideoFrame::kAPlane),
356 (*video_frame)->stride(VideoFrame::kAPlane),
357 (*video_frame)->visible_rect().width(),
358 (*video_frame)->visible_rect().height());
361 (*video_frame)->set_timestamp(buffer->timestamp());
362 (*video_frame)->set_hdr_metadata(config_.hdr_metadata());
364 // Prefer the color space from the config if available. It generally comes
365 // from the color tag which is more expressive than the vp8 and vp9 bitstream.
366 if (config_.color_space_info().IsSpecified()) {
368 ->set_color_space(config_.color_space_info().ToGfxColorSpace());
372 auto primaries = gfx::ColorSpace::PrimaryID::INVALID;
373 auto transfer = gfx::ColorSpace::TransferID::INVALID;
374 auto matrix = gfx::ColorSpace::MatrixID::INVALID;
375 auto range = vpx_image->range == VPX_CR_FULL_RANGE
376 ? gfx::ColorSpace::RangeID::FULL
377 : gfx::ColorSpace::RangeID::LIMITED;
379 switch (vpx_image->cs) {
381 case VPX_CS_SMPTE_170:
382 primaries = gfx::ColorSpace::PrimaryID::SMPTE170M;
383 transfer = gfx::ColorSpace::TransferID::SMPTE170M;
384 matrix = gfx::ColorSpace::MatrixID::SMPTE170M;
386 case VPX_CS_SMPTE_240:
387 primaries = gfx::ColorSpace::PrimaryID::SMPTE240M;
388 transfer = gfx::ColorSpace::TransferID::SMPTE240M;
389 matrix = gfx::ColorSpace::MatrixID::SMPTE240M;
392 primaries = gfx::ColorSpace::PrimaryID::BT709;
393 transfer = gfx::ColorSpace::TransferID::BT709;
394 matrix = gfx::ColorSpace::MatrixID::BT709;
397 primaries = gfx::ColorSpace::PrimaryID::BT2020;
398 if (vpx_image->bit_depth >= 12)
399 transfer = gfx::ColorSpace::TransferID::BT2020_12;
400 else if (vpx_image->bit_depth >= 10)
401 transfer = gfx::ColorSpace::TransferID::BT2020_10;
403 transfer = gfx::ColorSpace::TransferID::BT709;
404 matrix = gfx::ColorSpace::MatrixID::BT2020_NCL; // is this right?
407 primaries = gfx::ColorSpace::PrimaryID::BT709;
408 transfer = gfx::ColorSpace::TransferID::SRGB;
409 matrix = gfx::ColorSpace::MatrixID::GBR;
415 // TODO(ccameron): Set a color space even for unspecified values.
416 if (primaries != gfx::ColorSpace::PrimaryID::INVALID) {
418 ->set_color_space(gfx::ColorSpace(primaries, transfer, matrix, range));
424 VpxVideoDecoder::AlphaDecodeStatus VpxVideoDecoder::DecodeAlphaPlane(
425 const struct vpx_image* vpx_image,
426 const struct vpx_image** vpx_image_alpha,
427 const DecoderBuffer* buffer) {
428 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
429 if (!vpx_codec_alpha_ || buffer->side_data_size() < 8) {
430 return kAlphaPlaneProcessed;
433 // First 8 bytes of side data is |side_data_id| in big endian.
434 const uint64_t side_data_id = base::NetToHost64(
435 *(reinterpret_cast<const uint64_t*>(buffer->side_data())));
436 if (side_data_id != 1) {
437 return kAlphaPlaneProcessed;
440 // Try and decode buffer->side_data() minus the first 8 bytes as a full
443 TRACE_EVENT1("media", "vpx_codec_decode_alpha", "buffer",
444 buffer->AsHumanReadableString());
445 vpx_codec_err_t status =
446 vpx_codec_decode(vpx_codec_alpha_.get(), buffer->side_data() + 8,
447 buffer->side_data_size() - 8, nullptr /* user_priv */,
449 if (status != VPX_CODEC_OK) {
450 DLOG(ERROR) << "vpx_codec_decode() failed for the alpha: "
451 << vpx_codec_error(vpx_codec_.get());
452 return kAlphaPlaneError;
456 vpx_codec_iter_t iter_alpha = NULL;
457 *vpx_image_alpha = vpx_codec_get_frame(vpx_codec_alpha_.get(), &iter_alpha);
458 if (!(*vpx_image_alpha)) {
459 return kNoAlphaPlaneData;
462 if ((*vpx_image_alpha)->d_h != vpx_image->d_h ||
463 (*vpx_image_alpha)->d_w != vpx_image->d_w) {
464 DLOG(ERROR) << "The alpha plane dimensions are not the same as the "
466 return kAlphaPlaneError;
469 return kAlphaPlaneProcessed;
472 bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
473 const struct vpx_image* vpx_image,
474 const struct vpx_image* vpx_image_alpha,
475 scoped_refptr<VideoFrame>* video_frame) {
476 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
479 VideoPixelFormat codec_format;
480 switch (vpx_image->fmt) {
481 case VPX_IMG_FMT_I420:
482 codec_format = vpx_image_alpha ? PIXEL_FORMAT_I420A : PIXEL_FORMAT_I420;
485 case VPX_IMG_FMT_I422:
486 codec_format = PIXEL_FORMAT_I422;
489 case VPX_IMG_FMT_I444:
490 codec_format = PIXEL_FORMAT_I444;
493 case VPX_IMG_FMT_I42016:
494 switch (vpx_image->bit_depth) {
496 codec_format = PIXEL_FORMAT_YUV420P10;
499 codec_format = PIXEL_FORMAT_YUV420P12;
502 DLOG(ERROR) << "Unsupported bit depth: " << vpx_image->bit_depth;
507 case VPX_IMG_FMT_I42216:
508 switch (vpx_image->bit_depth) {
510 codec_format = PIXEL_FORMAT_YUV422P10;
513 codec_format = PIXEL_FORMAT_YUV422P12;
516 DLOG(ERROR) << "Unsupported bit depth: " << vpx_image->bit_depth;
521 case VPX_IMG_FMT_I44416:
522 switch (vpx_image->bit_depth) {
524 codec_format = PIXEL_FORMAT_YUV444P10;
527 codec_format = PIXEL_FORMAT_YUV444P12;
530 DLOG(ERROR) << "Unsupported bit depth: " << vpx_image->bit_depth;
536 DLOG(ERROR) << "Unsupported pixel format: " << vpx_image->fmt;
540 // The mixed |w|/|d_h| in |coded_size| is intentional. Setting the correct
541 // coded width is necessary to allow coalesced memory access, which may avoid
542 // frame copies. Setting the correct coded height however does not have any
543 // benefit, and only risk copying too much data.
544 const gfx::Size coded_size(vpx_image->w, vpx_image->d_h);
545 const gfx::Size visible_size(vpx_image->d_w, vpx_image->d_h);
546 // Compute natural size by scaling visible size by *pixel* aspect ratio. Note
547 // that we could instead use vpx_image r_w and r_h, but doing so would allow
548 // pixel aspect ratio to change on a per-frame basis which would make
549 // vpx_video_decoder inconsistent with decoders where changes to
550 // pixel aspect ratio are not surfaced (e.g. Android MediaCodec).
551 const gfx::Size natural_size =
552 config_.aspect_ratio().GetNaturalSize(gfx::Rect(visible_size));
555 DCHECK_EQ(VideoCodec::kVP9, config_.codec());
556 if (vpx_image_alpha) {
557 size_t alpha_plane_size =
558 vpx_image_alpha->stride[VPX_PLANE_Y] * vpx_image_alpha->d_h;
559 uint8_t* alpha_plane = memory_pool_->AllocateAlphaPlaneForFrameBuffer(
560 alpha_plane_size, vpx_image->fb_priv);
561 if (!alpha_plane) // In case of OOM, abort copy.
563 libyuv::CopyPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
564 vpx_image_alpha->stride[VPX_PLANE_Y], alpha_plane,
565 vpx_image_alpha->stride[VPX_PLANE_Y],
566 vpx_image_alpha->d_w, vpx_image_alpha->d_h);
567 *video_frame = VideoFrame::WrapExternalYuvaData(
568 codec_format, coded_size, gfx::Rect(visible_size), natural_size,
569 vpx_image->stride[VPX_PLANE_Y], vpx_image->stride[VPX_PLANE_U],
570 vpx_image->stride[VPX_PLANE_V], vpx_image_alpha->stride[VPX_PLANE_Y],
571 vpx_image->planes[VPX_PLANE_Y], vpx_image->planes[VPX_PLANE_U],
572 vpx_image->planes[VPX_PLANE_V], alpha_plane, kNoTimestamp);
574 *video_frame = VideoFrame::WrapExternalYuvData(
575 codec_format, coded_size, gfx::Rect(visible_size), natural_size,
576 vpx_image->stride[VPX_PLANE_Y], vpx_image->stride[VPX_PLANE_U],
577 vpx_image->stride[VPX_PLANE_V], vpx_image->planes[VPX_PLANE_Y],
578 vpx_image->planes[VPX_PLANE_U], vpx_image->planes[VPX_PLANE_V],
584 video_frame->get()->AddDestructionObserver(
585 memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
589 *video_frame = frame_pool_.CreateFrame(codec_format, visible_size,
590 gfx::Rect(visible_size), natural_size,
595 for (int plane = 0; plane < 3; plane++) {
596 libyuv::CopyPlane(vpx_image->planes[plane], vpx_image->stride[plane],
597 (*video_frame)->GetWritableVisibleData(plane),
598 (*video_frame)->stride(plane),
599 (*video_frame)->row_bytes(plane),
600 (*video_frame)->rows(plane));