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