1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/gpu/android/codec_allocator.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <memory>
11
12 #include "base/bind_helpers.h"
13 #include "base/logging.h"
14 #include "base/task/post_task.h"
15 #include "base/task/task_traits.h"
16 #include "base/task/thread_pool.h"
17 #include "base/task_runner_util.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "base/time/default_tick_clock.h"
20 #include "base/trace_event/trace_event.h"
21 #include "media/base/android/media_codec_bridge_impl.h"
22 #include "media/base/bind_to_current_loop.h"
23 #include "media/base/limits.h"
24 #include "media/base/timestamp_constants.h"
25
26 namespace media {
27
28 namespace {
29
30 // This must be safe to call on any thread. Returns nullptr on failure.
CreateMediaCodecInternal(CodecAllocator::CodecFactoryCB factory_cb,std::unique_ptr<VideoCodecConfig> codec_config)31 std::unique_ptr<MediaCodecBridge> CreateMediaCodecInternal(
32 CodecAllocator::CodecFactoryCB factory_cb,
33 std::unique_ptr<VideoCodecConfig> codec_config) {
34 TRACE_EVENT0("media", "CodecAllocator::CreateMediaCodec");
35 base::ScopedBlockingCall scoped_block(FROM_HERE,
36 base::BlockingType::MAY_BLOCK);
37 return factory_cb.Run(*codec_config);
38 }
39
40 // Delete |codec| and signal |done_event| if it's not null.
ReleaseMediaCodecInternal(std::unique_ptr<MediaCodecBridge> codec)41 void ReleaseMediaCodecInternal(std::unique_ptr<MediaCodecBridge> codec) {
42 TRACE_EVENT0("media", "CodecAllocator::ReleaseMediaCodec");
43 base::ScopedBlockingCall scoped_block(FROM_HERE,
44 base::BlockingType::MAY_BLOCK);
45 codec.reset();
46 }
47
CreateCodecTaskRunner()48 scoped_refptr<base::SequencedTaskRunner> CreateCodecTaskRunner() {
49 return base::ThreadPool::CreateSequencedTaskRunner(
50 {base::TaskPriority::USER_VISIBLE, base::MayBlock(),
51 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
52 }
53
54 } // namespace
55
56 // static
GetInstance(scoped_refptr<base::SequencedTaskRunner> task_runner)57 CodecAllocator* CodecAllocator::GetInstance(
58 scoped_refptr<base::SequencedTaskRunner> task_runner) {
59 static base::NoDestructor<CodecAllocator> allocator(
60 base::BindRepeating(&MediaCodecBridgeImpl::CreateVideoDecoder),
61 task_runner);
62
63 // Verify that this caller agrees on the task runner, if one was specified.
64 DCHECK(!task_runner || allocator->task_runner_ == task_runner);
65 return allocator.get();
66 }
67
CreateMediaCodecAsync(CodecCreatedCB codec_created_cb,std::unique_ptr<VideoCodecConfig> codec_config)68 void CodecAllocator::CreateMediaCodecAsync(
69 CodecCreatedCB codec_created_cb,
70 std::unique_ptr<VideoCodecConfig> codec_config) {
71 DCHECK(codec_created_cb);
72 DCHECK(codec_config);
73
74 if (!task_runner_->RunsTasksInCurrentSequence()) {
75 task_runner_->PostTask(
76 FROM_HERE,
77 base::BindOnce(&CodecAllocator::CreateMediaCodecAsync,
78 base::Unretained(this),
79 BindToCurrentLoop(std::move(codec_created_cb)),
80 std::move(codec_config)));
81 return;
82 }
83
84 // Select the task runner before adding the PendingOperation and before
85 // querying the |force_sw_codecs_| state.
86 auto* task_runner = SelectCodecTaskRunner();
87
88 // If we can't satisfy the request, fail the creation.
89 if (codec_config->codec_type == CodecType::kSecure && force_sw_codecs_) {
90 DLOG(ERROR) << "Secure software codec doesn't exist.";
91 std::move(codec_created_cb).Run(nullptr);
92 return;
93 }
94
95 if (force_sw_codecs_)
96 codec_config->codec_type = CodecType::kSoftware;
97
98 const auto start_time = tick_clock_->NowTicks();
99 pending_operations_.push_back(start_time);
100
101 // Post creation to the task runner. This may hang on broken platforms; if it
102 // hangs, we will detect it on the next creation request, and future creations
103 // will fallback to software.
104 base::PostTaskAndReplyWithResult(
105 std::move(task_runner), FROM_HERE,
106 base::BindOnce(&CreateMediaCodecInternal, factory_cb_,
107 std::move(codec_config)),
108 base::BindOnce(&CodecAllocator::OnCodecCreated, base::Unretained(this),
109 start_time, std::move(codec_created_cb)));
110 }
111
ReleaseMediaCodec(std::unique_ptr<MediaCodecBridge> codec,base::OnceClosure codec_released_cb)112 void CodecAllocator::ReleaseMediaCodec(std::unique_ptr<MediaCodecBridge> codec,
113 base::OnceClosure codec_released_cb) {
114 DCHECK(codec);
115 DCHECK(codec_released_cb);
116
117 if (!task_runner_->RunsTasksInCurrentSequence()) {
118 task_runner_->PostTask(
119 FROM_HERE,
120 base::BindOnce(&CodecAllocator::ReleaseMediaCodec,
121 base::Unretained(this), std::move(codec),
122 BindToCurrentLoop(std::move(codec_released_cb))));
123 return;
124 }
125
126 // Update |force_sw_codecs_| status.
127 auto* task_runner = SelectCodecTaskRunner();
128
129 // We always return non-software codecs to the primary task runner regardless
130 // of whether it's hung or not. We don't want any non-sw codecs to hang the
131 // the secondary task runner upon release.
132 //
133 // It's okay to release a software codec back to the primary task runner, we
134 // just don't want to release non-sw codecs to the secondary task runner.
135 if (codec->GetCodecType() != CodecType::kSoftware)
136 task_runner = primary_task_runner_.get();
137
138 const auto start_time = tick_clock_->NowTicks();
139 pending_operations_.push_back(start_time);
140
141 task_runner->PostTaskAndReply(
142 FROM_HERE, base::BindOnce(&ReleaseMediaCodecInternal, std::move(codec)),
143 base::BindOnce(&CodecAllocator::OnCodecReleased, base::Unretained(this),
144 start_time, std::move(codec_released_cb)));
145 }
146
CodecAllocator(CodecFactoryCB factory_cb,scoped_refptr<base::SequencedTaskRunner> task_runner)147 CodecAllocator::CodecAllocator(
148 CodecFactoryCB factory_cb,
149 scoped_refptr<base::SequencedTaskRunner> task_runner)
150 : task_runner_(std::move(task_runner)),
151 factory_cb_(std::move(factory_cb)),
152 tick_clock_(base::DefaultTickClock::GetInstance()) {}
153
154 CodecAllocator::~CodecAllocator() = default;
155
OnCodecCreated(base::TimeTicks start_time,CodecCreatedCB codec_created_cb,std::unique_ptr<MediaCodecBridge> codec)156 void CodecAllocator::OnCodecCreated(base::TimeTicks start_time,
157 CodecCreatedCB codec_created_cb,
158 std::unique_ptr<MediaCodecBridge> codec) {
159 DCHECK(task_runner_->RunsTasksInCurrentSequence());
160 CompletePendingOperation(start_time);
161 std::move(codec_created_cb).Run(std::move(codec));
162 }
163
OnCodecReleased(base::TimeTicks start_time,base::OnceClosure codec_released_cb)164 void CodecAllocator::OnCodecReleased(base::TimeTicks start_time,
165 base::OnceClosure codec_released_cb) {
166 DCHECK(task_runner_->RunsTasksInCurrentSequence());
167 CompletePendingOperation(start_time);
168 std::move(codec_released_cb).Run();
169 }
170
IsPrimaryTaskRunnerLikelyHung() const171 bool CodecAllocator::IsPrimaryTaskRunnerLikelyHung() const {
172 DCHECK(task_runner_->RunsTasksInCurrentSequence());
173
174 // Give tasks 800ms before considering them hung. MediaCodec.configure() calls
175 // typically take 100-200ms on a N5, so 800ms is expected to very rarely
176 // result in false positives. Also, false positives have low impact because we
177 // resume using the thread when the task completes.
178 constexpr base::TimeDelta kHungTaskDetectionTimeout =
179 base::TimeDelta::FromMilliseconds(800);
180
181 return !pending_operations_.empty() &&
182 tick_clock_->NowTicks() - *pending_operations_.begin() >
183 kHungTaskDetectionTimeout;
184 }
185
SelectCodecTaskRunner()186 base::SequencedTaskRunner* CodecAllocator::SelectCodecTaskRunner() {
187 DCHECK(task_runner_->RunsTasksInCurrentSequence());
188
189 if (IsPrimaryTaskRunnerLikelyHung()) {
190 force_sw_codecs_ = true;
191 if (!secondary_task_runner_)
192 secondary_task_runner_ = CreateCodecTaskRunner();
193 return secondary_task_runner_.get();
194 }
195
196 if (!primary_task_runner_)
197 primary_task_runner_ = CreateCodecTaskRunner();
198
199 force_sw_codecs_ = false;
200 return primary_task_runner_.get();
201 }
202
CompletePendingOperation(base::TimeTicks start_time)203 void CodecAllocator::CompletePendingOperation(base::TimeTicks start_time) {
204 // Note: This intentionally only erases the first instance, since there may be
205 // multiple instances of the same value.
206 pending_operations_.erase(std::find(pending_operations_.begin(),
207 pending_operations_.end(), start_time));
208 }
209
210 } // namespace media
211