1 // Copyright 2019 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/dav1d_video_decoder.h"
11 #include "base/bind.h"
12 #include "base/bits.h"
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/sequenced_task_runner_handle.h"
17 #include "media/base/bind_to_current_loop.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/limits.h"
20 #include "media/base/media_log.h"
21 #include "media/base/video_aspect_ratio.h"
22 #include "media/base/video_util.h"
25 #include "third_party/dav1d/libdav1d/include/dav1d/dav1d.h"
30 static void GetDecoderThreadCounts(const int coded_height,
33 // Tile thread counts based on currently available content. Recommended by
34 // YouTube, while frame thread values fit within
35 // limits::kMaxVideoDecodeThreads.
36 if (coded_height >= 700) {
38 4; // Current 720p content is encoded in 5 tiles and 1080p content with
39 // 8 tiles, but we'll exceed limits::kMaxVideoDecodeThreads with 5+
40 // tile threads with 3 frame threads (5 * 3 + 3 = 18 threads vs 16
43 // Since 720p playback isn't smooth without 3 frame threads, we've
44 // chosen a slightly lower tile thread count.
46 } else if (coded_height >= 300) {
55 static VideoPixelFormat Dav1dImgFmtToVideoPixelFormat(
56 const Dav1dPictureParameters* pic) {
57 switch (pic->layout) {
58 // Single plane monochrome images will be converted to standard 3 plane ones
59 // since Chromium doesn't support single Y plane images.
60 case DAV1D_PIXEL_LAYOUT_I400:
61 case DAV1D_PIXEL_LAYOUT_I420:
64 return PIXEL_FORMAT_I420;
66 return PIXEL_FORMAT_YUV420P10;
68 return PIXEL_FORMAT_YUV420P12;
70 DLOG(ERROR) << "Unsupported bit depth: " << pic->bpc;
71 return PIXEL_FORMAT_UNKNOWN;
73 case DAV1D_PIXEL_LAYOUT_I422:
76 return PIXEL_FORMAT_I422;
78 return PIXEL_FORMAT_YUV422P10;
80 return PIXEL_FORMAT_YUV422P12;
82 DLOG(ERROR) << "Unsupported bit depth: " << pic->bpc;
83 return PIXEL_FORMAT_UNKNOWN;
85 case DAV1D_PIXEL_LAYOUT_I444:
88 return PIXEL_FORMAT_I444;
90 return PIXEL_FORMAT_YUV444P10;
92 return PIXEL_FORMAT_YUV444P12;
94 DLOG(ERROR) << "Unsupported bit depth: " << pic->bpc;
95 return PIXEL_FORMAT_UNKNOWN;
100 static void ReleaseDecoderBuffer(const uint8_t* buffer, void* opaque) {
102 static_cast<DecoderBuffer*>(opaque)->Release();
105 static void LogDav1dMessage(void* cookie, const char* format, va_list ap) {
106 auto log = base::StringPrintV(format, ap);
110 if (log.back() == '\n')
116 // std::unique_ptr release helpers. We need to release both the containing
117 // structs as well as refs held within the structures.
118 struct ScopedDav1dDataFree {
119 void operator()(void* x) const {
120 auto* data = static_cast<Dav1dData*>(x);
121 dav1d_data_unref(data);
126 struct ScopedDav1dPictureFree {
127 void operator()(void* x) const {
128 auto* pic = static_cast<Dav1dPicture*>(x);
129 dav1d_picture_unref(pic);
135 SupportedVideoDecoderConfigs Dav1dVideoDecoder::SupportedConfigs() {
136 return {{/*profile_min=*/AV1PROFILE_PROFILE_MAIN,
137 /*profile_max=*/AV1PROFILE_PROFILE_HIGH,
138 /*coded_size_min=*/kDefaultSwDecodeSizeMin,
139 /*coded_size_max=*/kDefaultSwDecodeSizeMax,
140 /*allow_encrypted=*/false,
141 /*require_encrypted=*/false}};
144 Dav1dVideoDecoder::Dav1dVideoDecoder(MediaLog* media_log,
145 OffloadState offload_state)
146 : media_log_(media_log),
147 bind_callbacks_(offload_state == OffloadState::kNormal) {
148 DETACH_FROM_SEQUENCE(sequence_checker_);
151 Dav1dVideoDecoder::~Dav1dVideoDecoder() {
152 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
156 VideoDecoderType Dav1dVideoDecoder::GetDecoderType() const {
157 return VideoDecoderType::kDav1d;
160 void Dav1dVideoDecoder::Initialize(const VideoDecoderConfig& config,
162 CdmContext* /* cdm_context */,
164 const OutputCB& output_cb,
165 const WaitingCB& /* waiting_cb */) {
166 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
167 DCHECK(config.IsValidConfig());
169 InitCB bound_init_cb = bind_callbacks_ ? BindToCurrentLoop(std::move(init_cb))
170 : std::move(init_cb);
171 if (config.is_encrypted()) {
172 std::move(bound_init_cb)
173 .Run(DecoderStatus::Codes::kUnsupportedEncryptionMode);
177 if (config.codec() != VideoCodec::kAV1) {
178 std::move(bound_init_cb)
179 .Run(DecoderStatus(DecoderStatus::Codes::kUnsupportedCodec)
180 .WithData("codec", config.codec()));
184 // Clear any previously initialized decoder.
188 dav1d_default_settings(&s);
190 // Compute the ideal thread count values. We'll then clamp these based on the
191 // maximum number of recommended threads (using number of processors, etc).
192 int tile_threads, frame_threads;
193 GetDecoderThreadCounts(config.coded_size().height(), &tile_threads,
196 // While dav1d has switched to a thread pool, preserve the same thread counts
197 // we used when tile and frame threads were configured distinctly. It may be
198 // possible to lower this after some performance analysis of the new system.
199 s.n_threads = VideoDecoder::GetRecommendedThreadCount(frame_threads *
202 // We only want 1 frame thread in low delay mode, since otherwise we'll
203 // require at least two buffers before the first frame can be output.
204 if (low_delay || config.is_rtc())
205 s.max_frame_delay = 1;
207 // Route dav1d internal logs through Chrome's DLOG system.
208 s.logger = {nullptr, &LogDav1dMessage};
210 // Set a maximum frame size limit to avoid OOM'ing fuzzers.
211 s.frame_size_limit = limits::kMaxCanvas;
213 // TODO(tmathmeyer) write the dav1d error into the data for the media error.
214 if (dav1d_open(&dav1d_decoder_, &s) < 0) {
215 std::move(bound_init_cb).Run(DecoderStatus::Codes::kFailedToCreateDecoder);
220 state_ = DecoderState::kNormal;
221 output_cb_ = output_cb;
222 std::move(bound_init_cb).Run(DecoderStatus::Codes::kOk);
225 void Dav1dVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
226 DecodeCB decode_cb) {
227 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
230 DCHECK_NE(state_, DecoderState::kUninitialized)
231 << "Called Decode() before successful Initialize()";
233 DecodeCB bound_decode_cb = bind_callbacks_
234 ? BindToCurrentLoop(std::move(decode_cb))
235 : std::move(decode_cb);
237 if (state_ == DecoderState::kError) {
238 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kFailed);
242 if (!DecodeBuffer(std::move(buffer))) {
243 state_ = DecoderState::kError;
244 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kFailed);
248 // VideoDecoderShim expects |decode_cb| call after |output_cb_|.
249 std::move(bound_decode_cb).Run(DecoderStatus::Codes::kOk);
252 void Dav1dVideoDecoder::Reset(base::OnceClosure reset_cb) {
253 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
254 state_ = DecoderState::kNormal;
255 dav1d_flush(dav1d_decoder_);
258 base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
259 std::move(reset_cb));
261 std::move(reset_cb).Run();
264 void Dav1dVideoDecoder::Detach() {
265 // Even though we offload all resolutions of AV1, this may be called in a
266 // transition from clear to encrypted content. Which will subsequently fail
267 // Initialize() since encrypted content isn't supported by this decoder.
269 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
270 DCHECK(!bind_callbacks_);
273 DETACH_FROM_SEQUENCE(sequence_checker_);
276 void Dav1dVideoDecoder::CloseDecoder() {
277 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
280 dav1d_close(&dav1d_decoder_);
281 DCHECK(!dav1d_decoder_);
284 bool Dav1dVideoDecoder::DecodeBuffer(scoped_refptr<DecoderBuffer> buffer) {
285 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
287 using ScopedPtrDav1dData = std::unique_ptr<Dav1dData, ScopedDav1dDataFree>;
288 ScopedPtrDav1dData input_buffer;
290 if (!buffer->end_of_stream()) {
291 input_buffer.reset(new Dav1dData{0});
292 if (dav1d_data_wrap(input_buffer.get(), buffer->data(), buffer->data_size(),
293 &ReleaseDecoderBuffer, buffer.get()) < 0) {
296 input_buffer->m.timestamp = buffer->timestamp().InMicroseconds();
300 // Used to DCHECK that dav1d_send_data() actually takes the packet. If we exit
301 // this function without sending |input_buffer| that packet will be lost. We
302 // have no packet to send at end of stream.
303 bool send_data_completed = buffer->end_of_stream();
305 while (!input_buffer || input_buffer->sz) {
307 const int res = dav1d_send_data(dav1d_decoder_, input_buffer.get());
308 if (res < 0 && res != -EAGAIN) {
309 MEDIA_LOG(ERROR, media_log_) << "dav1d_send_data() failed on "
310 << buffer->AsHumanReadableString();
315 send_data_completed = true;
317 // Even if dav1d_send_data() returned EAGAIN, try dav1d_get_picture().
320 using ScopedPtrDav1dPicture =
321 std::unique_ptr<Dav1dPicture, ScopedDav1dPictureFree>;
322 ScopedPtrDav1dPicture p(new Dav1dPicture{0});
324 const int res = dav1d_get_picture(dav1d_decoder_, p.get());
326 if (res != -EAGAIN) {
327 MEDIA_LOG(ERROR, media_log_) << "dav1d_get_picture() failed on "
328 << buffer->AsHumanReadableString();
332 // We've reached end of stream and no frames remain to drain.
334 DCHECK(send_data_completed);
341 auto frame = BindImageToVideoFrame(p.get());
343 MEDIA_LOG(DEBUG, media_log_)
344 << "Failed to produce video frame from Dav1dPicture.";
348 // AV1 color space defines match ISO 23001-8:2016 via ISO/IEC 23091-4/ITU-T
349 // H.273. https://aomediacodec.github.io/av1-spec/#color-config-semantics
350 VideoColorSpace color_space(
351 p->seq_hdr->pri, p->seq_hdr->trc, p->seq_hdr->mtrx,
352 p->seq_hdr->color_range ? gfx::ColorSpace::RangeID::FULL
353 : gfx::ColorSpace::RangeID::LIMITED);
355 // If the frame doesn't specify a color space, use the container's.
356 if (!color_space.IsSpecified())
357 color_space = config_.color_space_info();
359 frame->set_color_space(color_space.ToGfxColorSpace());
360 frame->metadata().power_efficient = false;
361 frame->set_hdr_metadata(config_.hdr_metadata());
363 // When we use bind mode, our image data is dependent on the Dav1dPicture,
364 // so we must ensure it stays alive along enough.
365 frame->AddDestructionObserver(
366 base::BindOnce([](ScopedPtrDav1dPicture) {}, std::move(p)));
367 output_cb_.Run(std::move(frame));
370 DCHECK(send_data_completed);
374 scoped_refptr<VideoFrame> Dav1dVideoDecoder::BindImageToVideoFrame(
375 const Dav1dPicture* pic) {
376 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
377 const gfx::Size visible_size(pic->p.w, pic->p.h);
379 VideoPixelFormat pixel_format = Dav1dImgFmtToVideoPixelFormat(&pic->p);
380 if (pixel_format == PIXEL_FORMAT_UNKNOWN)
383 auto uv_plane_stride = pic->stride[1];
384 auto* u_plane = static_cast<uint8_t*>(pic->data[1]);
385 auto* v_plane = static_cast<uint8_t*>(pic->data[2]);
387 const bool needs_fake_uv_planes = pic->p.layout == DAV1D_PIXEL_LAYOUT_I400;
388 if (needs_fake_uv_planes) {
389 // UV planes are half the size of the Y plane.
390 uv_plane_stride = base::bits::AlignUp(pic->stride[0] / 2, ptrdiff_t{2});
391 const auto uv_plane_height = (pic->p.h + 1) / 2;
392 const size_t size_needed = uv_plane_stride * uv_plane_height;
394 if (!fake_uv_data_ || fake_uv_data_->size() != size_needed) {
395 if (pic->p.bpc == 8) {
396 // Avoid having base::RefCountedBytes zero initialize the memory just to
397 // fill it with a different value.
398 constexpr uint8_t kBlankUV = 256 / 2;
399 std::vector<unsigned char> empty_data(size_needed, kBlankUV);
401 // When we resize, existing frames will keep their refs on the old data.
402 fake_uv_data_ = base::RefCountedBytes::TakeVector(&empty_data);
404 DCHECK(pic->p.bpc == 10 || pic->p.bpc == 12);
405 const uint16_t kBlankUV = (1 << pic->p.bpc) / 2;
407 base::MakeRefCounted<base::RefCountedBytes>(size_needed);
409 uint16_t* data = fake_uv_data_->front_as<uint16_t>();
410 std::fill(data, data + size_needed / 2, kBlankUV);
414 u_plane = v_plane = fake_uv_data_->front_as<uint8_t>();
417 auto frame = VideoFrame::WrapExternalYuvData(
418 pixel_format, visible_size, gfx::Rect(visible_size),
419 config_.aspect_ratio().GetNaturalSize(gfx::Rect(visible_size)),
420 pic->stride[0], uv_plane_stride, uv_plane_stride,
421 static_cast<uint8_t*>(pic->data[0]), u_plane, v_plane,
422 base::Microseconds(pic->m.timestamp));
426 // Each frame needs a ref on the fake UV data to keep it alive until done.
427 if (needs_fake_uv_planes) {
428 frame->AddDestructionObserver(base::BindOnce(
429 [](scoped_refptr<base::RefCountedBytes>) {}, fake_uv_data_));