diff options
Diffstat (limited to 'chromium/media/filters/dav1d_video_decoder.cc')
-rw-r--r-- | chromium/media/filters/dav1d_video_decoder.cc | 107 |
1 files changed, 82 insertions, 25 deletions
diff --git a/chromium/media/filters/dav1d_video_decoder.cc b/chromium/media/filters/dav1d_video_decoder.cc index a93e8a38ebb..27a40ddeeea 100644 --- a/chromium/media/filters/dav1d_video_decoder.cc +++ b/chromium/media/filters/dav1d_video_decoder.cc @@ -16,6 +16,7 @@ #include "base/threading/sequenced_task_runner_handle.h" #include "media/base/bind_to_current_loop.h" #include "media/base/decoder_buffer.h" +#include "media/base/limits.h" #include "media/base/media_log.h" #include "media/base/video_util.h" @@ -25,13 +26,27 @@ extern "C" { namespace media { -// Returns the number of threads. -static int GetDecoderThreadCount(const VideoDecoderConfig& config) { - // For AV1 decode when using the default thread count, increase the number - // of decode threads to equal the maximum number of tiles possible for - // higher resolution streams. - return VideoDecoder::GetRecommendedThreadCount(config.coded_size().width() / - 256); +static void GetDecoderThreadCounts(const int coded_height, + int* tile_threads, + int* frame_threads) { + // Tile thread counts based on currently available content. Recommended by + // YouTube, while frame thread values fit within limits::kMaxVideoThreads. + if (coded_height >= 700) { + *tile_threads = + 4; // Current 720p content is encoded in 5 tiles and 1080p content with + // 8 tiles, but we'll exceed limits::kMaxVideoThreads with 5+ tile + // threads with 3 frame threads (5 * 3 + 3 = 18 threads vs 16 max). + // + // Since 720p playback isn't smooth without 3 frame threads, we've + // chosen a slightly lower tile thread count. + *frame_threads = 3; + } else if (coded_height >= 300) { + *tile_threads = 3; + *frame_threads = 2; + } else { + *tile_threads = 2; + *frame_threads = 2; + } } static VideoPixelFormat Dav1dImgFmtToVideoPixelFormat( @@ -132,15 +147,16 @@ std::string Dav1dVideoDecoder::GetDisplayName() const { void Dav1dVideoDecoder::Initialize(const VideoDecoderConfig& config, bool low_delay, CdmContext* /* cdm_context */, - const InitCB& init_cb, + InitCB init_cb, const OutputCB& output_cb, const WaitingCB& /* waiting_cb */) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(config.IsValidConfig()); - InitCB bound_init_cb = bind_callbacks_ ? BindToCurrentLoop(init_cb) : init_cb; + InitCB bound_init_cb = bind_callbacks_ ? BindToCurrentLoop(std::move(init_cb)) + : std::move(init_cb); if (config.is_encrypted() || config.codec() != kCodecAV1) { - bound_init_cb.Run(false); + std::move(bound_init_cb).Run(false); return; } @@ -149,61 +165,102 @@ void Dav1dVideoDecoder::Initialize(const VideoDecoderConfig& config, Dav1dSettings s; dav1d_default_settings(&s); - s.n_tile_threads = GetDecoderThreadCount(config); - // Use only 1 frame thread in low delay mode, otherwise we'll require at least - // two buffers before the first frame can be output. - s.n_frame_threads = low_delay ? 1 : 2; + // Compute the ideal thread count values. We'll then clamp these based on the + // maximum number of recommended threads (using number of processors, etc). + // + // dav1d will spawn |n_tile_threads| per frame thread. + GetDecoderThreadCounts(config.coded_size().height(), &s.n_tile_threads, + &s.n_frame_threads); + + const int max_threads = VideoDecoder::GetRecommendedThreadCount( + s.n_frame_threads * (s.n_tile_threads + 1)); + + // First clamp tile threads to the allowed maximum. We prefer tile threads + // over frame threads since dav1d folk indicate they are more efficient. In an + // ideal world this would be auto-detected by dav1d from the content. + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=1536783#c0 + s.n_tile_threads = std::min(max_threads, s.n_tile_threads); + + // Now clamp frame threads based on the number of total threads that would be + // created with the given |n_tile_threads| value. Note: A thread count of 1 + // generates no additional threads since the calling thread (this thread) is + // counted as a thread. + // + // We only want 1 frame thread in low delay mode, since otherwise we'll + // require at least two buffers before the first frame can be output. + // + // If a system has the cores for it, we'll end up using the following: + // <300p: 2 tile threads, 2 frame threads = 2 * 2 + 2 = 6 total threads. + // <700p: 3 tile threads, 2 frame threads = 3 * 2 + 2 = 8 total threads. + // + // For higher resolutions we hit limits::kMaxVideoThreads (16): + // >700p: 4 tile threads, 3 frame threads = 4 * 3 + 3 = 15 total threads. + // + // Due to the (surprising) performance issues which occurred when setting + // |n_frame_threads|=1 (https://crbug.com/957511) the minimum total number of + // threads is 6 (two tile and two frame) regardless of core count. The maximum + // is min(2 * base::SysInfo::NumberOfProcessors(), limits::kMaxVideoThreads). + if (low_delay) + s.n_frame_threads = 1; + else if (s.n_frame_threads * (s.n_tile_threads + 1) > max_threads) + s.n_frame_threads = std::max(2, max_threads / (s.n_tile_threads + 1)); // Route dav1d internal logs through Chrome's DLOG system. s.logger = {nullptr, &LogDav1dMessage}; + // Set a maximum frame size limit to avoid OOM'ing fuzzers. + s.frame_size_limit = limits::kMaxCanvas; + if (dav1d_open(&dav1d_decoder_, &s) < 0) { - bound_init_cb.Run(false); + std::move(bound_init_cb).Run(false); return; } config_ = config; state_ = DecoderState::kNormal; output_cb_ = output_cb; - bound_init_cb.Run(true); + std::move(bound_init_cb).Run(true); } void Dav1dVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer, - const DecodeCB& decode_cb) { + DecodeCB decode_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(buffer); DCHECK(decode_cb); DCHECK_NE(state_, DecoderState::kUninitialized) << "Called Decode() before successful Initialize()"; - DecodeCB bound_decode_cb = - bind_callbacks_ ? BindToCurrentLoop(decode_cb) : decode_cb; + DecodeCB bound_decode_cb = bind_callbacks_ + ? BindToCurrentLoop(std::move(decode_cb)) + : std::move(decode_cb); if (state_ == DecoderState::kError) { - bound_decode_cb.Run(DecodeStatus::DECODE_ERROR); + std::move(bound_decode_cb).Run(DecodeStatus::DECODE_ERROR); return; } if (!DecodeBuffer(std::move(buffer))) { state_ = DecoderState::kError; - bound_decode_cb.Run(DecodeStatus::DECODE_ERROR); + std::move(bound_decode_cb).Run(DecodeStatus::DECODE_ERROR); return; } // VideoDecoderShim expects |decode_cb| call after |output_cb_|. - bound_decode_cb.Run(DecodeStatus::OK); + std::move(bound_decode_cb).Run(DecodeStatus::OK); } -void Dav1dVideoDecoder::Reset(const base::RepeatingClosure& reset_cb) { +void Dav1dVideoDecoder::Reset(base::OnceClosure reset_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); state_ = DecoderState::kNormal; dav1d_flush(dav1d_decoder_); if (bind_callbacks_) - base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, reset_cb); + base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(reset_cb)); else - reset_cb.Run(); + std::move(reset_cb).Run(); } void Dav1dVideoDecoder::Detach() { |