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

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: Review comments addressed, map->vector in AudioRendererCacheImpl Created 4 years, 7 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 {
19 constexpr int kDeleteTimeoutMs = 5000;
20 } // namespace
21
22 namespace content {
23
24 // Cached sink data.
25 struct AudioRendererSinkCacheImpl::CacheEntry {
26 int source_render_frame_id;
27 std::string device_id;
28 url::Origin security_origin;
29 scoped_refptr<media::AudioRendererSink> sink; // Sink instance
30 bool used; // True if in use by a client.
31 };
32
33 // Helper class for CacheEntry lookup.
DaleCurtis 2016/05/23 18:29:08 Why choose a helper class vs a lambda?
o1ka 2016/05/23 19:21:34 It's used in two places and requires a quite lot o
DaleCurtis 2016/05/23 20:20:26 Hmm, looks like my original comment got eaten. I c
o1ka 2016/05/24 15:00:41 Yes, this is another way to implement it. I don't
34 class CacheEntryFinder {
35 public:
36 CacheEntryFinder(int source_render_frame_id,
37 const std::string& device_id,
38 const url::Origin& security_origin,
39 bool unused_only)
40 : source_render_frame_id_(source_render_frame_id),
41 device_id_(device_id),
42 security_origin_(security_origin),
43 unused_only_(unused_only) {}
44
45 bool operator()(const AudioRendererSinkCacheImpl::CacheEntry& s) const {
46 if (s.used && unused_only_)
47 return false;
48
49 if (s.source_render_frame_id != source_render_frame_id_)
50 return false;
51
52 if (media::AudioDeviceDescription::IsDefaultDevice(device_id_) &&
53 media::AudioDeviceDescription::IsDefaultDevice(s.device_id)) {
54 // Both device IDs represent the same default device => do not compare
55 // them; the default device is always authorized => ignoring security
56 // origin.
57 return true;
58 }
59
60 return (s.device_id == device_id_) &&
61 (s.security_origin == security_origin_);
62 }
63
64 private:
65 int source_render_frame_id_;
66 std::string device_id_;
67 url::Origin security_origin_;
68 bool unused_only_;
69 };
70
71 // static
72 std::unique_ptr<AudioRendererSinkCache> AudioRendererSinkCache::Create() {
73 return base::WrapUnique(new AudioRendererSinkCacheImpl(
74 base::ThreadTaskRunnerHandle::Get(),
75 base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink),
76 kDeleteTimeoutMs));
77 }
78
79 AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl(
80 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
81 const CreateSinkCallback& create_sink_cb,
82 int delete_timeout_ms)
83 : task_runner_(task_runner),
DaleCurtis 2016/05/23 18:29:08 Isn't std::move necessary now whenever you pass-by
o1ka 2016/05/24 15:00:41 Done.
84 create_sink_cb_(create_sink_cb),
85 delete_timeout_ms_(delete_timeout_ms),
DaleCurtis 2016/05/23 18:29:08 store as timedelta, FromMilliseconds(...)
o1ka 2016/05/24 15:00:41 Done.
86 weak_ptr_factory_(this) {}
DaleCurtis 2016/05/23 18:29:08 If you take my suggestion below to use a Repeating
o1ka 2016/05/24 15:00:41 This is a nice suggestion. The advantage of the cu
87
88 AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() {
89 // We just release all the cached sinks here.
90 }
91
92 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo(
93 int source_render_frame_id,
94 int session_id,
95 const std::string& device_id,
96 const url::Origin& security_origin) {
97 CacheEntry cache_entry;
98 cache_entry.source_render_frame_id = source_render_frame_id;
99 cache_entry.security_origin = security_origin;
100 cache_entry.used = false;
101
102 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
103 device_id)) {
104 // We are provided with session id instead of device id. Session id is
105 // unique, so we can't find any matching sink. Creating a new one.
106 cache_entry.sink = create_sink_cb_.Run(source_render_frame_id, session_id,
107 device_id, security_origin);
108 cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id();
109
110 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
111 << " - used session to create new sink.";
112 } else {
113 // Ignore session id.
114 {
115 base::AutoLock auto_lock(cache_lock_);
DaleCurtis 2016/05/23 18:29:08 Instead of dropping in and out of lock state (some
o1ka 2016/05/23 19:21:34 Ok will do.
o1ka 2016/05/24 15:00:41 Done.
116 auto cache_iter = std::find_if(
117 cache_.begin(), cache_.end(),
118 CacheEntryFinder(source_render_frame_id, device_id, security_origin,
119 false /* unused_only */));
120 if (cache_iter != cache_.end()) {
121 // A matching cached sink is found.
122 DVLOG(1)
123 << "GetSinkInfo: address: " << cache_iter->sink.get()
124 << " - reusing a cached sink. source_render_frame_id: "
125 << source_render_frame_id << " session_id: " << session_id
126 << " device_id: " << device_id
127 << " security_origin: " << security_origin << " Device info: ["
128 << cache_iter->sink->GetOutputDeviceInfo().AsHumanReadableString()
129 << "] ";
130
131 return cache_iter->sink->GetOutputDeviceInfo();
132 } // if (cache_iter != cache_.end())
133 } // Lock scope.
134
135 // No matching sink found, create a new one.
136 cache_entry.device_id = device_id;
137 cache_entry.sink = create_sink_cb_.Run(
138 source_render_frame_id, 0 /* session_id */, device_id, security_origin);
139
140 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
141 << " - no matching cached sink found, created a new one.";
142 }
143
144 {
145 // Cache a newly-created sink.
146 base::AutoLock auto_lock(cache_lock_);
147 cache_.push_back(cache_entry);
148 }
149
150 // Schedule it for deletion.
DaleCurtis 2016/05/23 18:29:08 Why schedule them individually for deletion vs hav
o1ka 2016/05/23 19:21:34 Because it's quite a rare occasion when output par
o1ka 2016/05/23 19:26:27 (It was like that in the original draft version wh
DaleCurtis 2016/05/23 20:20:26 You wouldn't run the timer the whole time. You'd o
o1ka 2016/05/24 15:00:41 I looked into CancelableCallback before implementi
DaleCurtis 2016/05/24 20:41:42 This isn't true of WeakPtr either. You have a race
o1ka 2016/05/25 12:20:51 Oh, I've had a feeling that I might be missing som
DaleCurtis 2016/05/25 16:48:52 The multiple threads thing isn't a problem if you
o1ka 2016/05/25 17:00:56 Would't I need to use a weak pointer to post that
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 {
168 base::AutoLock auto_lock(cache_lock_);
DaleCurtis 2016/05/23 18:29:08 Ditto on lock once.
o1ka 2016/05/24 15:00:41 Done.
169
170 auto cache_iter =
171 std::find_if(cache_.begin(), cache_.end(),
172 CacheEntryFinder(source_render_frame_id, device_id,
173 security_origin, true /* unused_only */));
174
175 if (cache_iter != cache_.end()) {
176 // Found unused sink; mark it as used and return.
177 DVLOG(1) << "GetSink: address: " << cache_iter->sink.get()
178 << " - found unused cached sink, reusing it."
179 << " source_render_frame_id: " << source_render_frame_id
180 << " device_id: " << device_id
181 << " security_origin: " << security_origin;
182
183 cache_iter->used = true;
184 return cache_iter->sink;
185 } // for
186 } // Lock scope.
187
188 // No unused sink is found, create one, mark it used, cache it and return.
189 CacheEntry cache_entry;
190 cache_entry.source_render_frame_id = source_render_frame_id;
191 cache_entry.device_id = device_id;
192 cache_entry.security_origin = security_origin;
193 cache_entry.used = true;
194
195 cache_entry.sink = create_sink_cb_.Run(
196 source_render_frame_id, 0 /* session_id */, device_id, security_origin);
197
198 base::AutoLock auto_lock(cache_lock_);
199 cache_.push_back(cache_entry);
200
201 DVLOG(1) << "GetSink: address: " << cache_entry.sink.get()
202 << " - no unused cached sink found, created a new one."
203 << " source_render_frame_id: " << source_render_frame_id
204 << " device_id: " << device_id
205 << " security_origin: " << security_origin;
206 return cache_entry.sink;
207 }
208
209 void AudioRendererSinkCacheImpl::ReleaseSink(
210 const media::AudioRendererSink* sink_ptr) {
211 // We don't know the sink state, so won't reuse it. Delete it immediately.
212 DeleteSink(sink_ptr, true);
213 }
214
215 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
216 const media::AudioRendererSink* sink_ptr) {
217 DVLOG(1) << "DeleteLaterIfUnused: address: " << sink_ptr;
218
219 task_runner_->PostDelayedTask(
220 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink,
221 weak_ptr_factory_.GetWeakPtr(), sink_ptr,
222 false /*do not delete if used*/),
223 base::TimeDelta::FromMilliseconds(delete_timeout_ms_));
224 }
225
226 void AudioRendererSinkCacheImpl::DeleteSink(
227 const media::AudioRendererSink* sink_ptr,
228 bool force_delete_used) {
229 DCHECK(sink_ptr);
230
231 scoped_refptr<media::AudioRendererSink> sink_to_stop;
232
233 {
234 base::AutoLock auto_lock(cache_lock_);
235
236 // Looking up the sink by its pointer.
237 auto cache_iter = std::find_if(cache_.begin(), cache_.end(),
238 [&sink_ptr](const CacheEntry& val) {
239 return val.sink.get() == sink_ptr;
240 });
241
242 if (cache_iter == cache_.end()) {
243 // If we got here and |force_delete_used| is not set it means the sink
244 // scheduled for deletion get aquired and released before scheduled
245 // deletion - this is ok.
246 DCHECK(!force_delete_used)
247 << "DeleteSink: address: " << sink_ptr
248 << " could not find a sink which is supposed to be in use";
249
250 DVLOG(1) << "DeleteSink: address: " << sink_ptr
251 << " force_delete_used = true - already deleted.";
252 return;
253 }
254
255 DVLOG(1) << "DeleteSink: address: " << sink_ptr
256 << " force_delete_used: " << force_delete_used
257 << " in use: " << cache_iter->used << " source_render_frame_id: "
258 << cache_iter->source_render_frame_id
259 << " device_id: " << cache_iter->device_id
260 << " security_origin: " << cache_iter->security_origin;
261
262 // When |force_delete_used| is set, it's expected that we are deleting a
263 // used sink.
264 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used))
265 << "Attempt to delete a non-aquired sink.";
266
267 if (!force_delete_used && cache_iter->used) {
268 DVLOG(1) << "DeleteSink: address: " << sink_ptr
269 << " sink in use, skipping deletion.";
270 return;
271 }
272
273 // To stop the sink before deletion if it's not used, we need to hold
274 // a ref to it.
275 if (!cache_iter->used)
276 sink_to_stop = cache_iter->sink;
277
278 cache_.erase(cache_iter);
279 DVLOG(1) << "DeleteSink: address: " << sink_ptr;
280 } // Lock scope;
281
282 // Stop the sink out of the lock scope.
283 if (sink_to_stop.get()) {
284 DCHECK_EQ(sink_ptr, sink_to_stop.get());
285 sink_to_stop->Stop();
286 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
287 }
288 }
289
290 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
291 return cache_.size();
292 }
293
294 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698