Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(299)

Side by Side Diff: content/renderer/media/audio_renderer_sink_cache_impl.cc

Issue 1942803002: Caching AudioOutputDevice instances in mixer manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: more review comments addressed Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 <algorithm>
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/synchronization/lock.h"
11 #include "base/threading/thread_task_runner_handle.h"
12 #include "content/renderer/media/audio_device_factory.h"
13 #include "content/renderer/media/audio_renderer_sink_cache_impl.h"
14 #include "media/audio/audio_device_description.h"
15 #include "media/base/audio_renderer_sink.h"
16 #include "url/origin.h"
17
18 namespace content {
19
20 constexpr int kDeleteTimeoutMs = 5000;
21
22 // Cached sink data.
23 struct AudioRendererSinkCacheImpl::CacheEntry {
24 int source_render_frame_id;
25 std::string device_id;
26 url::Origin security_origin;
27 scoped_refptr<media::AudioRendererSink> sink; // Sink instance
28 bool used; // True if in use by a client.
29 };
30
31 // Helper class for CacheEntry lookup.
32 class CacheEntryFinder {
33 public:
34 CacheEntryFinder(int source_render_frame_id,
35 const std::string& device_id,
36 const url::Origin& security_origin,
37 bool unused_only)
38 : source_render_frame_id_(source_render_frame_id),
39 device_id_(device_id),
40 security_origin_(security_origin),
41 unused_only_(unused_only) {}
42
43 bool operator()(const AudioRendererSinkCacheImpl::CacheEntry& s) const {
44 if (s.used && unused_only_)
45 return false;
46
47 if (s.source_render_frame_id != source_render_frame_id_)
48 return false;
49
50 if (media::AudioDeviceDescription::IsDefaultDevice(device_id_) &&
51 media::AudioDeviceDescription::IsDefaultDevice(s.device_id)) {
52 // Both device IDs represent the same default device => do not compare
53 // them; the default device is always authorized => ignoring security
54 // origin.
55 return true;
56 }
57
58 return (s.device_id == device_id_) &&
DaleCurtis 2016/05/24 20:41:42 Avoid unnecessary parens.
o1ka 2016/05/25 12:20:52 Done. This is something interesting though - I hav
59 (s.security_origin == security_origin_);
60 }
61
62 private:
63 int source_render_frame_id_;
DaleCurtis 2016/05/24 20:41:42 const all these
o1ka 2016/05/25 12:20:52 Done. (And then I removed the code)
64 std::string device_id_;
65 url::Origin security_origin_;
66 bool unused_only_;
67 };
DaleCurtis 2016/05/24 20:41:42 DISALLOW_COPY_AND_ASSIGN
o1ka 2016/05/25 12:20:52 It's copyable (and needs to be so for find_if), so
68
69 // static
70 std::unique_ptr<AudioRendererSinkCache> AudioRendererSinkCache::Create() {
71 return base::WrapUnique(new AudioRendererSinkCacheImpl(
72 base::ThreadTaskRunnerHandle::Get(),
73 base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink),
74 base::TimeDelta::FromMilliseconds(kDeleteTimeoutMs)));
75 }
76
77 AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl(
78 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
79 const CreateSinkCallback& create_sink_cb,
80 const base::TimeDelta& delete_timeout)
DaleCurtis 2016/05/24 20:41:42 Pass base::TimeDelta by value (see time.h)
o1ka 2016/05/25 12:20:52 Done.
81 : task_runner_(std::move(task_runner)),
82 create_sink_cb_(create_sink_cb),
83 delete_timeout_(delete_timeout),
84 weak_ptr_factory_(this) {}
85
86 AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() {
87 // We just release all the cached sinks here.
88 }
89
90 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo(
91 int source_render_frame_id,
92 int session_id,
93 const std::string& device_id,
94 const url::Origin& security_origin) {
95 CacheEntry cache_entry;
DaleCurtis 2016/05/24 20:41:42 Worth adding a constructor to CacheEntry with some
miu 2016/05/25 01:23:20 IIRC, the uniform initializer syntax is now approv
o1ka 2016/05/25 12:20:52 True, but assignment syntax is recommended for thi
96 cache_entry.source_render_frame_id = source_render_frame_id;
97 cache_entry.security_origin = security_origin;
98 cache_entry.used = false;
99
100 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
101 device_id)) {
102 // We are provided with session id instead of device id. Session id is
103 // unique, so we can't find any matching sink. Creating a new one.
104 cache_entry.sink = create_sink_cb_.Run(source_render_frame_id, session_id,
105 device_id, security_origin);
106 cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id();
107
108 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
109 << " - used session to create new sink.";
110
111 // Cache a newly-created sink.
112 base::AutoLock auto_lock(cache_lock_);
113 cache_.push_back(cache_entry);
114
115 } else {
116 // Ignore session id.
117 base::AutoLock auto_lock(cache_lock_);
118
119 auto cache_iter = std::find_if(
120 cache_.begin(), cache_.end(),
121 CacheEntryFinder(source_render_frame_id, device_id, security_origin,
122 false /* unused_only */));
123
124 if (cache_iter != cache_.end()) {
125 // A matching cached sink is found.
126 DVLOG(1)
127 << "GetSinkInfo: address: " << cache_iter->sink.get()
128 << " - reusing a cached sink. source_render_frame_id: "
129 << source_render_frame_id << " session_id: " << session_id
130 << " device_id: " << device_id
131 << " security_origin: " << security_origin << " Device info: ["
132 << cache_iter->sink->GetOutputDeviceInfo().AsHumanReadableString()
133 << "] ";
134
135 return cache_iter->sink->GetOutputDeviceInfo();
136 } // if (cache_iter != cache_.end())
DaleCurtis 2016/05/24 20:41:42 It's not chrome style to annotate trailing } in co
o1ka 2016/05/25 12:20:52 Done.
137
138 // No matching sink found, create a new one.
139 cache_entry.device_id = device_id;
140 cache_entry.sink = create_sink_cb_.Run(
141 source_render_frame_id, 0 /* session_id */, device_id, security_origin);
142
143 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
144 << " - no matching cached sink found, created a new one.";
145
146 // Cache a newly-created sink.
147 cache_.push_back(cache_entry);
148 } // else
149
150 // Schedule it for deletion.
151 DeleteLaterIfUnused(cache_entry.sink.get());
152
153 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
154 << " inserted. source_render_frame_id: " << source_render_frame_id
155 << " session_id: " << session_id << " device_id: " << device_id
156 << " security_origin: " << security_origin << " Device info: ["
157 << cache_entry.sink->GetOutputDeviceInfo().AsHumanReadableString()
158 << "] ";
159
160 return cache_entry.sink->GetOutputDeviceInfo();
161 }
162
163 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink(
164 int source_render_frame_id,
165 const std::string& device_id,
166 const url::Origin& security_origin) {
167 base::AutoLock auto_lock(cache_lock_);
168
169 auto cache_iter =
170 std::find_if(cache_.begin(), cache_.end(),
171 CacheEntryFinder(source_render_frame_id, device_id,
172 security_origin, true /* unused_only */));
173
174 if (cache_iter != cache_.end()) {
175 // Found unused sink; mark it as used and return.
176 DVLOG(1) << "GetSink: address: " << cache_iter->sink.get()
177 << " - found unused cached sink, reusing it."
178 << " source_render_frame_id: " << source_render_frame_id
179 << " device_id: " << device_id
180 << " security_origin: " << security_origin;
181
182 cache_iter->used = true;
183 return cache_iter->sink;
184 } // if
185
186 // No unused sink is found, create one, mark it used, cache it and return.
187 CacheEntry cache_entry;
miu 2016/05/25 01:23:20 ditto here: You could just use the uniform initial
o1ka 2016/05/25 12:20:52 Done, same as above.
188 cache_entry.source_render_frame_id = source_render_frame_id;
189 cache_entry.device_id = device_id;
190 cache_entry.security_origin = security_origin;
191 cache_entry.used = true;
192
193 cache_entry.sink = create_sink_cb_.Run(
194 source_render_frame_id, 0 /* session_id */, device_id, security_origin);
195
196 cache_.push_back(cache_entry);
197
198 DVLOG(1) << "GetSink: address: " << cache_entry.sink.get()
DaleCurtis 2016/05/24 20:41:42 I'd drop some of these DVLOGs from the production
o1ka 2016/05/25 12:20:52 Yes, they hurt readability. Reduced a bit.
199 << " - no unused cached sink found, created a new one."
200 << " source_render_frame_id: " << source_render_frame_id
201 << " device_id: " << device_id
202 << " security_origin: " << security_origin;
203 return cache_entry.sink;
204 }
205
206 void AudioRendererSinkCacheImpl::ReleaseSink(
207 const media::AudioRendererSink* sink_ptr) {
208 // We don't know the sink state, so won't reuse it. Delete it immediately.
DaleCurtis 2016/05/24 20:41:42 Aren't we always taking this path when tearing dow
o1ka 2016/05/25 12:20:52 Do you mean reusing a sink after it was returned b
209 DeleteSink(sink_ptr, true);
210 }
211
212 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
213 const media::AudioRendererSink* sink_ptr) {
214 DVLOG(1) << "DeleteLaterIfUnused: address: " << sink_ptr;
215
216 task_runner_->PostDelayedTask(
217 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink,
218 weak_ptr_factory_.GetWeakPtr(), sink_ptr,
219 false /*do not delete if used*/),
220 delete_timeout_);
221 }
222
223 void AudioRendererSinkCacheImpl::DeleteSink(
224 const media::AudioRendererSink* sink_ptr,
225 bool force_delete_used) {
226 DCHECK(sink_ptr);
227
228 scoped_refptr<media::AudioRendererSink> sink_to_stop;
229
230 {
231 base::AutoLock auto_lock(cache_lock_);
232
233 // Looking up the sink by its pointer.
234 auto cache_iter = std::find_if(cache_.begin(), cache_.end(),
235 [&sink_ptr](const CacheEntry& val) {
236 return val.sink.get() == sink_ptr;
237 });
238
239 if (cache_iter == cache_.end()) {
240 // If we got here and |force_delete_used| is not set it means the sink
241 // scheduled for deletion get aquired and released before scheduled
242 // deletion - this is ok.
243 DCHECK(!force_delete_used)
244 << "DeleteSink: address: " << sink_ptr
245 << " could not find a sink which is supposed to be in use";
246
247 DVLOG(1) << "DeleteSink: address: " << sink_ptr
248 << " force_delete_used = true - already deleted.";
249 return;
250 }
251
252 DVLOG(1) << "DeleteSink: address: " << sink_ptr
253 << " force_delete_used: " << force_delete_used
254 << " in use: " << cache_iter->used << " source_render_frame_id: "
255 << cache_iter->source_render_frame_id
256 << " device_id: " << cache_iter->device_id
257 << " security_origin: " << cache_iter->security_origin;
258
259 // When |force_delete_used| is set, it's expected that we are deleting a
260 // used sink.
261 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used))
262 << "Attempt to delete a non-aquired sink.";
263
264 if (!force_delete_used && cache_iter->used) {
265 DVLOG(1) << "DeleteSink: address: " << sink_ptr
266 << " sink in use, skipping deletion.";
267 return;
268 }
269
270 // To stop the sink before deletion if it's not used, we need to hold
271 // a ref to it.
272 if (!cache_iter->used)
273 sink_to_stop = cache_iter->sink;
274
275 cache_.erase(cache_iter);
276 DVLOG(1) << "DeleteSink: address: " << sink_ptr;
277 } // Lock scope;
278
279 // Stop the sink out of the lock scope.
280 if (sink_to_stop.get()) {
281 DCHECK_EQ(sink_ptr, sink_to_stop.get());
282 sink_to_stop->Stop();
283 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
284 }
285 }
286
287 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
288 return cache_.size();
289 }
290
291 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698