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

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: rebase cache smoke test added minor unit test cleanup 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 // static
32 std::unique_ptr<AudioRendererSinkCache> AudioRendererSinkCache::Create() {
33 return base::WrapUnique(new AudioRendererSinkCacheImpl(
34 base::ThreadTaskRunnerHandle::Get(),
35 base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink),
DaleCurtis 2016/05/26 19:18:09 Hmm, should this have a & ? I'm surprised it compi
o1ka 2016/05/27 13:48:52 It's a static method, so & is optional (just like
36 base::TimeDelta::FromMilliseconds(kDeleteTimeoutMs)));
37 }
38
39 AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl(
40 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
41 const CreateSinkCallback& create_sink_cb,
42 base::TimeDelta delete_timeout)
43 : task_runner_(std::move(task_runner)),
44 create_sink_cb_(create_sink_cb),
45 delete_timeout_(delete_timeout),
46 weak_ptr_factory_(this) {
47 weak_this_ = weak_ptr_factory_.GetWeakPtr();
48 }
49
50 AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() {
51 // We just release all the cached sinks here.
52 }
53
54 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo(
55 int source_render_frame_id,
56 int session_id,
57 const std::string& device_id,
58 const url::Origin& security_origin) {
59 CacheEntry cache_entry = {source_render_frame_id,
60 std::string() /* device_id */, security_origin,
61 nullptr /* sink */, false /* not used */};
62
63 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
64 device_id)) {
65 // We are provided with session id instead of device id. Session id is
66 // unique, so we can't find any matching sink. Creating a new one.
67 cache_entry.sink = create_sink_cb_.Run(source_render_frame_id, session_id,
68 device_id, security_origin);
69 cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id();
70
71 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
72 << " - used session to create new sink.";
73
74 // Cache a newly-created sink.
75 base::AutoLock auto_lock(cache_lock_);
76 cache_.push_back(cache_entry);
77
78 } else {
79 // Ignore session id.
80 base::AutoLock auto_lock(cache_lock_);
81
82 auto cache_iter =
83 FindCacheEntry_Locked(source_render_frame_id, device_id,
84 security_origin, false /* unused_only */);
85
86 if (cache_iter != cache_.end()) {
87 // A matching cached sink is found.
88 DVLOG(1) << "GetSinkInfo: address: " << cache_iter->sink.get()
89 << " - reused a cached sink.";
90
91 return cache_iter->sink->GetOutputDeviceInfo();
92 }
93
94 // No matching sink found, create a new one.
95 cache_entry.device_id = device_id;
96 cache_entry.sink = create_sink_cb_.Run(
97 source_render_frame_id, 0 /* session_id */, device_id, security_origin);
98
99 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
100 << " - no matching cached sink found, created a new one.";
101
102 // Cache a newly-created sink.
103 cache_.push_back(cache_entry);
104 }
105
106 // Schedule it for deletion.
107 DeleteLaterIfUnused(cache_entry.sink.get());
108
109 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
110 << " created. source_render_frame_id: " << source_render_frame_id
111 << " session_id: " << session_id << " device_id: " << device_id
112 << " security_origin: " << security_origin;
113
114 return cache_entry.sink->GetOutputDeviceInfo();
115 }
116
117 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink(
118 int source_render_frame_id,
119 const std::string& device_id,
120 const url::Origin& security_origin) {
121 base::AutoLock auto_lock(cache_lock_);
122
123 auto cache_iter =
124 FindCacheEntry_Locked(source_render_frame_id, device_id, security_origin,
125 true /* unused_only */);
126
127 if (cache_iter != cache_.end()) {
128 // Found unused sink; mark it as used and return.
129 DVLOG(1) << "GetSink: address: " << cache_iter->sink.get()
130 << " - found unused cached sink, reusing it.";
131
132 cache_iter->used = true;
133 return cache_iter->sink;
134 }
135
136 // No unused sink is found, create one, mark it used, cache it and return.
137 CacheEntry cache_entry = {
138 source_render_frame_id, device_id, security_origin,
139 create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */, device_id,
140 security_origin),
141 true /* used */};
142
143 cache_.push_back(cache_entry);
144
145 DVLOG(1) << "GetSink: address: " << cache_entry.sink.get()
146 << " - no unused cached sink found, created a new one."
147 << " source_render_frame_id: " << source_render_frame_id
148 << " device_id: " << device_id
149 << " security_origin: " << security_origin;
150 return cache_entry.sink;
151 }
152
153 void AudioRendererSinkCacheImpl::ReleaseSink(
154 const media::AudioRendererSink* sink_ptr) {
155 // We don't know the sink state, so won't reuse it. Delete it immediately.
156 DeleteSink(sink_ptr, true);
157 }
158
159 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
160 const media::AudioRendererSink* sink_ptr) {
161 task_runner_->PostDelayedTask(
162 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink, weak_this_,
163 sink_ptr, false /*do not delete if used*/),
164 delete_timeout_);
165 }
166
167 void AudioRendererSinkCacheImpl::DeleteSink(
168 const media::AudioRendererSink* sink_ptr,
169 bool force_delete_used) {
170 DCHECK(sink_ptr);
171
172 scoped_refptr<media::AudioRendererSink> sink_to_stop;
173
174 {
175 base::AutoLock auto_lock(cache_lock_);
176
177 // Looking up the sink by its pointer.
178 auto cache_iter = std::find_if(cache_.begin(), cache_.end(),
179 [sink_ptr](const CacheEntry& val) {
180 return val.sink.get() == sink_ptr;
181 });
182
183 if (cache_iter == cache_.end()) {
184 // If |force_delete_used| is not set it means the sink scheduled for
185 // deletion got aquired and released before scheduled deletion - it's ok.
186 DCHECK(!force_delete_used)
187 << "DeleteSink: address: " << sink_ptr
188 << " could not find a sink which is supposed to be in use";
189
190 DVLOG(1) << "DeleteSink: address: " << sink_ptr
191 << " force_delete_used = false - already deleted.";
192 return;
193 }
194
195 // When |force_delete_used| is set, it's expected that we are deleting a
196 // used sink.
197 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used))
198 << "Attempt to delete a non-aquired sink.";
199
200 if (!force_delete_used && cache_iter->used) {
201 DVLOG(1) << "DeleteSink: address: " << sink_ptr
202 << " sink in use, skipping deletion.";
203 return;
204 }
205
206 // To stop the sink before deletion if it's not used, we need to hold
207 // a ref to it.
208 if (!cache_iter->used)
209 sink_to_stop = cache_iter->sink;
210
211 cache_.erase(cache_iter);
212 DVLOG(1) << "DeleteSink: address: " << sink_ptr;
213 } // Lock scope;
214
215 // Stop the sink out of the lock scope.
216 if (sink_to_stop.get()) {
217 DCHECK_EQ(sink_ptr, sink_to_stop.get());
218 sink_to_stop->Stop();
219 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
220 }
221 }
222
223 AudioRendererSinkCacheImpl::CacheContainer::iterator
224 AudioRendererSinkCacheImpl::FindCacheEntry_Locked(
225 int source_render_frame_id,
226 const std::string& device_id,
227 const url::Origin& security_origin,
228 bool unused_only) {
229 return std::find_if(
230 cache_.begin(), cache_.end(),
231 [source_render_frame_id, &device_id, &security_origin,
232 unused_only](const CacheEntry& val) {
233 if (val.used && unused_only)
234 return false;
235 if (val.source_render_frame_id != source_render_frame_id)
236 return false;
237 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) &&
238 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) {
239 // Both device IDs represent the same default device => do not compare
240 // them; the default device is always authorized => ignore security
241 // origin.
242 return true;
243 }
244 return val.device_id == device_id &&
245 val.security_origin == security_origin;
246 });
247 };
248
249 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
DaleCurtis 2016/05/26 19:18:09 This can be an inline get_cache_size_for_testing()
o1ka 2016/05/27 13:48:52 Unfortunately not, I need to publish CacheEntry st
250 return cache_.size();
251 }
252
253 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698