summaryrefslogtreecommitdiff
path: root/chromium/media/filters/dav1d_video_decoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/filters/dav1d_video_decoder.cc')
-rw-r--r--chromium/media/filters/dav1d_video_decoder.cc107
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() {