1 // Copyright 2017 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/offloading_video_decoder.h"
9 #include "base/containers/contains.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/synchronization/atomic_flag.h"
13 #include "base/task/bind_post_task.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "base/task/thread_pool.h"
16 #include "media/base/cdm_context.h"
17 #include "media/base/decoder_buffer.h"
18 #include "media/base/video_frame.h"
22 // Helper class which manages cancellation of Decode() after Reset() and makes
23 // it easier to destruct on the proper thread.
24 class CancellationHelper {
26 CancellationHelper(std::unique_ptr<OffloadableVideoDecoder> decoder)
27 : cancellation_flag_(std::make_unique<base::AtomicFlag>()),
28 decoder_(std::move(decoder)) {}
30 CancellationHelper(const CancellationHelper&) = delete;
31 CancellationHelper& operator=(const CancellationHelper&) = delete;
33 // Safe to call from any thread.
34 void Cancel() { cancellation_flag_->Set(); }
36 void Decode(scoped_refptr<DecoderBuffer> buffer,
37 VideoDecoder::DecodeCB decode_cb) {
38 if (cancellation_flag_->IsSet()) {
39 std::move(decode_cb).Run(DecoderStatus::Codes::kAborted);
43 decoder_->Decode(std::move(buffer), std::move(decode_cb));
46 void Reset(base::OnceClosure reset_cb) {
47 // OffloadableVideoDecoders are required to have a synchronous Reset(), so
48 // we don't need to wait for the Reset to complete. Despite this, we don't
49 // want to run |reset_cb| before we've reset the cancellation flag or the
50 // client may end up issuing another Reset() before this code runs.
51 decoder_->Reset(base::DoNothing());
52 cancellation_flag_ = std::make_unique<base::AtomicFlag>();
53 std::move(reset_cb).Run();
56 OffloadableVideoDecoder* decoder() const { return decoder_.get(); }
59 std::unique_ptr<base::AtomicFlag> cancellation_flag_;
60 std::unique_ptr<OffloadableVideoDecoder> decoder_;
63 OffloadingVideoDecoder::OffloadingVideoDecoder(
64 int min_offloading_width,
65 std::vector<VideoCodec> supported_codecs,
66 std::unique_ptr<OffloadableVideoDecoder> decoder)
67 : min_offloading_width_(min_offloading_width),
68 supported_codecs_(std::move(supported_codecs)),
69 helper_(std::make_unique<CancellationHelper>(std::move(decoder))) {
70 DETACH_FROM_SEQUENCE(sequence_checker_);
73 OffloadingVideoDecoder::~OffloadingVideoDecoder() {
74 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
76 // The |helper_| must always be destroyed on the |offload_task_runner_| since
77 // we may still have tasks posted to it.
78 if (offload_task_runner_)
79 offload_task_runner_->DeleteSoon(FROM_HERE, std::move(helper_));
82 VideoDecoderType OffloadingVideoDecoder::GetDecoderType() const {
83 // This call is expected to be static and safe to call from any thread.
84 return helper_->decoder()->GetDecoderType();
87 void OffloadingVideoDecoder::Initialize(const VideoDecoderConfig& config,
89 CdmContext* cdm_context,
91 const OutputCB& output_cb,
92 const WaitingCB& waiting_cb) {
93 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
94 DCHECK(config.IsValidConfig());
96 const bool disable_offloading =
97 config.is_encrypted() ||
98 config.coded_size().width() < min_offloading_width_ ||
99 !base::Contains(supported_codecs_, config.codec());
102 initialized_ = false;
104 // We're transitioning from offloading to no offloading, so detach from the
105 // offloading thread so we can run on the media thread.
106 if (disable_offloading && offload_task_runner_) {
107 offload_task_runner_->PostTaskAndReply(
109 base::BindOnce(&OffloadableVideoDecoder::Detach,
110 base::Unretained(helper_->decoder())),
111 // We must trampoline back trough OffloadingVideoDecoder because it's
112 // possible for this class to be destroyed during Initialize().
113 base::BindOnce(&OffloadingVideoDecoder::Initialize,
114 weak_factory_.GetWeakPtr(), config, low_delay,
115 cdm_context, std::move(init_cb), output_cb,
120 // We're transitioning from no offloading to offloading, so detach from the
121 // media thread so we can run on the offloading thread.
122 if (!disable_offloading && !offload_task_runner_)
123 helper_->decoder()->Detach();
126 DCHECK(!initialized_);
129 // Offloaded decoders expect asynchronous execution of callbacks; even if we
130 // aren't currently using the offload thread.
131 InitCB bound_init_cb = base::BindPostTaskToCurrentDefault(std::move(init_cb));
132 OutputCB bound_output_cb = base::BindPostTaskToCurrentDefault(output_cb);
134 // If we're not offloading just pass through to the wrapped decoder.
135 if (disable_offloading) {
136 offload_task_runner_ = nullptr;
137 helper_->decoder()->Initialize(config, low_delay, cdm_context,
138 std::move(bound_init_cb), bound_output_cb,
143 if (!offload_task_runner_) {
144 offload_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
145 {base::TaskPriority::USER_BLOCKING});
148 offload_task_runner_->PostTask(
150 base::BindOnce(&OffloadableVideoDecoder::Initialize,
151 base::Unretained(helper_->decoder()), config, low_delay,
152 cdm_context, std::move(bound_init_cb), bound_output_cb,
156 void OffloadingVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
157 DecodeCB decode_cb) {
158 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
162 DecodeCB bound_decode_cb =
163 base::BindPostTaskToCurrentDefault(std::move(decode_cb));
164 if (!offload_task_runner_) {
165 helper_->decoder()->Decode(std::move(buffer), std::move(bound_decode_cb));
169 offload_task_runner_->PostTask(
170 FROM_HERE, base::BindOnce(&CancellationHelper::Decode,
171 base::Unretained(helper_.get()),
172 std::move(buffer), std::move(bound_decode_cb)));
175 void OffloadingVideoDecoder::Reset(base::OnceClosure reset_cb) {
176 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
178 base::OnceClosure bound_reset_cb =
179 base::BindPostTaskToCurrentDefault(std::move(reset_cb));
180 if (!offload_task_runner_) {
181 helper_->Reset(std::move(bound_reset_cb));
184 offload_task_runner_->PostTask(
185 FROM_HERE, base::BindOnce(&CancellationHelper::Reset,
186 base::Unretained(helper_.get()),
187 std::move(bound_reset_cb)));
191 int OffloadingVideoDecoder::GetMaxDecodeRequests() const {
192 // If we're offloading, try to parallelize decodes as well. Take care when
193 // adjusting this number as it may dramatically increase memory usage and
194 // reduce seek times. See http://crbug.com/731841.
196 // The current value of 2 was determined via experimental adjustment until a
197 // 4K60 VP9 playback dropped zero frames.
198 return offload_task_runner_ ? 2 : 1;