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

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: comments fixed 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),
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 DVLOG(1) << "DeleteLaterIfUnused: address: " << sink_ptr;
162
163 task_runner_->PostDelayedTask(
164 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink, weak_this_,
165 sink_ptr, false /*do not delete if used*/),
166 delete_timeout_);
167 }
168
169 void AudioRendererSinkCacheImpl::DeleteSink(
170 const media::AudioRendererSink* sink_ptr,
171 bool force_delete_used) {
172 DCHECK(sink_ptr);
173
174 scoped_refptr<media::AudioRendererSink> sink_to_stop;
175
176 {
177 base::AutoLock auto_lock(cache_lock_);
178
179 // Looking up the sink by its pointer.
180 auto cache_iter = std::find_if(cache_.begin(), cache_.end(),
181 [sink_ptr](const CacheEntry& val) {
182 return val.sink.get() == sink_ptr;
183 });
184
185 if (cache_iter == cache_.end()) {
186 // If |force_delete_used| is not set it means the sink scheduled for
187 // deletion got aquired and released before scheduled deletion - it's ok.
188 DCHECK(!force_delete_used)
189 << "DeleteSink: address: " << sink_ptr
190 << " could not find a sink which is supposed to be in use";
191
192 DVLOG(1) << "DeleteSink: address: " << sink_ptr
193 << " force_delete_used = true - already deleted.";
194 return;
195 }
196
197 // When |force_delete_used| is set, it's expected that we are deleting a
198 // used sink.
199 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used))
200 << "Attempt to delete a non-aquired sink.";
201
202 if (!force_delete_used && cache_iter->used) {
203 DVLOG(1) << "DeleteSink: address: " << sink_ptr
204 << " sink in use, skipping deletion.";
205 return;
206 }
207
208 // To stop the sink before deletion if it's not used, we need to hold
209 // a ref to it.
210 if (!cache_iter->used)
211 sink_to_stop = cache_iter->sink;
212
213 cache_.erase(cache_iter);
214 DVLOG(1) << "DeleteSink: address: " << sink_ptr;
215 } // Lock scope;
216
217 // Stop the sink out of the lock scope.
218 if (sink_to_stop.get()) {
219 DCHECK_EQ(sink_ptr, sink_to_stop.get());
220 sink_to_stop->Stop();
221 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
222 }
223 }
224
225 AudioRendererSinkCacheImpl::CacheContainer::iterator
226 AudioRendererSinkCacheImpl::FindCacheEntry_Locked(
227 int source_render_frame_id,
228 const std::string& device_id,
229 const url::Origin& security_origin,
230 bool unused_only) {
231 return std::find_if(
232 cache_.begin(), cache_.end(),
233 [source_render_frame_id, &device_id, &security_origin,
234 unused_only](const CacheEntry& val) {
235 if (val.used && unused_only)
236 return false;
237 if (val.source_render_frame_id != source_render_frame_id)
238 return false;
239 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) &&
240 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) {
241 // Both device IDs represent the same default device => do not compare
242 // them; the default device is always authorized => ignore security
243 // origin.
244 return true;
245 }
246 return val.device_id == device_id &&
247 val.security_origin == security_origin;
248 });
249 };
250
251 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
252 return cache_.size();
253 }
254
255 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698