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

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

Issue 2533443003: AudioRendererSinkCache: Do not use unhealthy sinks (Closed)
Patch Set: Created 4 years 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <algorithm> 5 #include <algorithm>
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/location.h" 8 #include "base/location.h"
9 #include "base/memory/ptr_util.h" 9 #include "base/memory/ptr_util.h"
10 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
(...skipping 19 matching lines...) Expand all
30 // cache a new sink. 30 // cache a new sink.
31 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID = 1, 31 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID = 1,
32 32
33 // Output parmeters for an already-cached sink are requested. 33 // Output parmeters for an already-cached sink are requested.
34 SINK_CACHE_HIT = 2, 34 SINK_CACHE_HIT = 2,
35 35
36 // For UMA. 36 // For UMA.
37 SINK_CACHE_LAST_ENTRY 37 SINK_CACHE_LAST_ENTRY
38 }; 38 };
39 39
40 bool SinkIsHealthy(media::AudioRendererSink* sink) {
41 return sink->GetOutputDeviceInfo().device_status() ==
42 media::OUTPUT_DEVICE_STATUS_OK;
43 }
44
40 } // namespace 45 } // namespace
41 46
42 // Cached sink data. 47 // Cached sink data.
43 struct AudioRendererSinkCacheImpl::CacheEntry { 48 struct AudioRendererSinkCacheImpl::CacheEntry {
44 int source_render_frame_id; 49 int source_render_frame_id;
45 std::string device_id; 50 std::string device_id;
46 url::Origin security_origin; 51 url::Origin security_origin;
47 scoped_refptr<media::AudioRendererSink> sink; // Sink instance 52 scoped_refptr<media::AudioRendererSink> sink; // Sink instance
48 bool used; // True if in use by a client. 53 bool used; // True if in use by a client.
49 }; 54 };
(...skipping 24 matching lines...) Expand all
74 // is being destroyed anyways. 79 // is being destroyed anyways.
75 for (auto& entry : cache_) 80 for (auto& entry : cache_)
76 entry.sink->Stop(); 81 entry.sink->Stop();
77 } 82 }
78 83
79 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo( 84 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo(
80 int source_render_frame_id, 85 int source_render_frame_id,
81 int session_id, 86 int session_id,
82 const std::string& device_id, 87 const std::string& device_id,
83 const url::Origin& security_origin) { 88 const url::Origin& security_origin) {
84 CacheEntry cache_entry = {source_render_frame_id, 89 scoped_refptr<media::AudioRendererSink> sink(nullptr);
Guido Urdaneta 2016/11/25 22:13:17 nit: use default constructor? (up to you)
o1ka 2016/11/30 15:15:03 Done.
85 std::string() /* device_id */, security_origin,
86 nullptr /* sink */, false /* not used */};
87 90
88 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, 91 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
89 device_id)) { 92 device_id)) {
90 // We are provided with session id instead of device id. Session id is 93 // We are provided with session id instead of device id. Session id is
91 // unique, so we can't find any matching sink. Creating a new one. 94 // unique, so we can't find any matching sink. Creating a new one.
92 cache_entry.sink = create_sink_cb_.Run(source_render_frame_id, session_id, 95 sink = create_sink_cb_.Run(source_render_frame_id, session_id, device_id,
93 device_id, security_origin); 96 security_origin);
94 cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id();
95 97
96 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get() 98 CacheUnusedSinkIfHealthy(source_render_frame_id,
97 << " - used session to create new sink."; 99 sink->GetOutputDeviceInfo().device_id(),
98 100 security_origin, sink);
99 // Cache a newly-created sink.
100 base::AutoLock auto_lock(cache_lock_);
101 cache_.push_back(cache_entry);
102 UMA_HISTOGRAM_ENUMERATION( 101 UMA_HISTOGRAM_ENUMERATION(
103 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", 102 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization",
104 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID, SINK_CACHE_LAST_ENTRY); 103 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID, SINK_CACHE_LAST_ENTRY);
105 104
106 } else { 105 } else {
107 // Ignore session id. 106 // Ignore session id.
108 base::AutoLock auto_lock(cache_lock_); 107 {
109 108 base::AutoLock auto_lock(cache_lock_);
110 auto cache_iter = 109 auto cache_iter = FindHealthyCacheEntry_Locked(source_render_frame_id,
Guido Urdaneta 2016/11/25 22:13:17 nit: if only healthy entries are cached there is n
o1ka 2016/11/30 15:15:04 Changed so that the cached sinks are always health
111 FindCacheEntry_Locked(source_render_frame_id, device_id, 110 device_id, security_origin,
112 security_origin, false /* unused_only */); 111 false /* unused_only */);
113 112 if (cache_iter != cache_.end()) {
114 if (cache_iter != cache_.end()) { 113 // A matching cached sink is found.
115 // A matching cached sink is found. 114 UMA_HISTOGRAM_ENUMERATION(
116 DVLOG(1) << "GetSinkInfo: address: " << cache_iter->sink.get() 115 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization",
117 << " - reused a cached sink."; 116 SINK_CACHE_HIT, SINK_CACHE_LAST_ENTRY);
118 117 return cache_iter->sink->GetOutputDeviceInfo();
119 UMA_HISTOGRAM_ENUMERATION( 118 }
120 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization",
121 SINK_CACHE_HIT, SINK_CACHE_LAST_ENTRY);
122 return cache_iter->sink->GetOutputDeviceInfo();
123 } 119 }
124 120
125 // No matching sink found, create a new one. 121 // No matching sink found, create a new one.
126 cache_entry.device_id = device_id; 122 sink = create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */,
127 cache_entry.sink = create_sink_cb_.Run( 123 device_id, security_origin);
128 source_render_frame_id, 0 /* session_id */, device_id, security_origin); 124 CacheUnusedSinkIfHealthy(source_render_frame_id, device_id, security_origin,
129 125 sink);
130 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
131 << " - no matching cached sink found, created a new one.";
132
133 // Cache a newly-created sink.
134 cache_.push_back(cache_entry);
135 UMA_HISTOGRAM_ENUMERATION( 126 UMA_HISTOGRAM_ENUMERATION(
136 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", 127 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization",
137 SINK_CACHE_MISS_NO_SINK, SINK_CACHE_LAST_ENTRY); 128 SINK_CACHE_MISS_NO_SINK, SINK_CACHE_LAST_ENTRY);
138 } 129 }
139 130
140 // Schedule it for deletion. 131 //|sink| is ref-counted, so it's ok if it is grabage-collected before we get
Guido Urdaneta 2016/11/25 22:13:17 typo: grabage -> garbage. I think the comment is w
o1ka 2016/11/30 15:15:04 Done.
141 DeleteLaterIfUnused(cache_entry.sink.get()); 132 // here.
142 133 return sink->GetOutputDeviceInfo();
143 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
144 << " created. source_render_frame_id: " << source_render_frame_id
145 << " session_id: " << session_id << " device_id: " << device_id
146 << " security_origin: " << security_origin;
147
148 return cache_entry.sink->GetOutputDeviceInfo();
149 } 134 }
150 135
151 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink( 136 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink(
152 int source_render_frame_id, 137 int source_render_frame_id,
153 const std::string& device_id, 138 const std::string& device_id,
154 const url::Origin& security_origin) { 139 const url::Origin& security_origin) {
155 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.UsedForSinkCreation", 140 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.UsedForSinkCreation",
156 true); 141 true);
157 142
158 base::AutoLock auto_lock(cache_lock_); 143 base::AutoLock auto_lock(cache_lock_);
159 144
160 auto cache_iter = 145 auto cache_iter = FindHealthyCacheEntry_Locked(source_render_frame_id,
161 FindCacheEntry_Locked(source_render_frame_id, device_id, security_origin, 146 device_id, security_origin,
162 true /* unused sink only */); 147 true /* unused sink only */);
163 148
164 if (cache_iter != cache_.end()) { 149 if (cache_iter != cache_.end()) {
165 // Found unused sink; mark it as used and return. 150 // Found unused sink; mark it as used and return.
166 DVLOG(1) << "GetSink: address: " << cache_iter->sink.get()
167 << " - found unused cached sink, reusing it.";
168
169 cache_iter->used = true; 151 cache_iter->used = true;
170 UMA_HISTOGRAM_BOOLEAN( 152 UMA_HISTOGRAM_BOOLEAN(
171 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", true); 153 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", true);
172 return cache_iter->sink; 154 return cache_iter->sink;
173 } 155 }
174 156
175 // No unused sink is found, create one, mark it used, cache it and return. 157 // No unused sink is found, create one, mark it used, cache it and return.
176 CacheEntry cache_entry = { 158 CacheEntry cache_entry = {
177 source_render_frame_id, device_id, security_origin, 159 source_render_frame_id, device_id, security_origin,
178 create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */, device_id, 160 create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */, device_id,
179 security_origin), 161 security_origin),
180 true /* used */}; 162 true /* used */};
181
182 cache_.push_back(cache_entry); 163 cache_.push_back(cache_entry);
183
184 DVLOG(1) << "GetSink: address: " << cache_entry.sink.get()
185 << " - no unused cached sink found, created a new one."
186 << " source_render_frame_id: " << source_render_frame_id
187 << " device_id: " << device_id
188 << " security_origin: " << security_origin;
189 return cache_entry.sink; 164 return cache_entry.sink;
190 } 165 }
191 166
192 void AudioRendererSinkCacheImpl::ReleaseSink( 167 void AudioRendererSinkCacheImpl::ReleaseSink(
193 const media::AudioRendererSink* sink_ptr) { 168 const media::AudioRendererSink* sink_ptr) {
194 // We don't know the sink state, so won't reuse it. Delete it immediately. 169 // We don't know the sink state, so won't reuse it. Delete it immediately.
195 DeleteSink(sink_ptr, true); 170 DeleteSink(sink_ptr, true);
196 } 171 }
197 172
198 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused( 173 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
(...skipping 19 matching lines...) Expand all
218 [sink_ptr](const CacheEntry& val) { 193 [sink_ptr](const CacheEntry& val) {
219 return val.sink.get() == sink_ptr; 194 return val.sink.get() == sink_ptr;
220 }); 195 });
221 196
222 if (cache_iter == cache_.end()) { 197 if (cache_iter == cache_.end()) {
223 // If |force_delete_used| is not set it means the sink scheduled for 198 // If |force_delete_used| is not set it means the sink scheduled for
224 // deletion got aquired and released before scheduled deletion - it's ok. 199 // deletion got aquired and released before scheduled deletion - it's ok.
225 DCHECK(!force_delete_used) 200 DCHECK(!force_delete_used)
226 << "DeleteSink: address: " << sink_ptr 201 << "DeleteSink: address: " << sink_ptr
227 << " could not find a sink which is supposed to be in use"; 202 << " could not find a sink which is supposed to be in use";
228
229 DVLOG(1) << "DeleteSink: address: " << sink_ptr
230 << " force_delete_used = false - already deleted.";
231 return; 203 return;
232 } 204 }
233 205
234 // When |force_delete_used| is set, it's expected that we are deleting a 206 // When |force_delete_used| is set, it's expected that we are deleting a
235 // used sink. 207 // used sink.
236 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used)) 208 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used))
237 << "Attempt to delete a non-aquired sink."; 209 << "Attempt to delete a non-aquired sink.";
238 210
239 if (!force_delete_used && cache_iter->used) { 211 if (!force_delete_used && cache_iter->used)
240 DVLOG(1) << "DeleteSink: address: " << sink_ptr
241 << " sink in use, skipping deletion.";
242 return; 212 return;
243 }
244 213
245 // To stop the sink before deletion if it's not used, we need to hold 214 // To stop the sink before deletion if it's not used, we need to hold
246 // a ref to it. 215 // a ref to it.
247 if (!cache_iter->used) { 216 if (!cache_iter->used) {
248 sink_to_stop = cache_iter->sink; 217 sink_to_stop = cache_iter->sink;
249 UMA_HISTOGRAM_BOOLEAN( 218 UMA_HISTOGRAM_BOOLEAN(
250 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false); 219 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false);
251 } 220 }
252 221
253 cache_.erase(cache_iter); 222 cache_.erase(cache_iter);
254 DVLOG(1) << "DeleteSink: address: " << sink_ptr;
255 } // Lock scope; 223 } // Lock scope;
256 224
257 // Stop the sink out of the lock scope. 225 // Stop the sink out of the lock scope.
258 if (sink_to_stop.get()) { 226 if (sink_to_stop.get()) {
259 DCHECK_EQ(sink_ptr, sink_to_stop.get()); 227 DCHECK_EQ(sink_ptr, sink_to_stop.get());
260 sink_to_stop->Stop(); 228 sink_to_stop->Stop();
261 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
262 } 229 }
263 } 230 }
264 231
265 AudioRendererSinkCacheImpl::CacheContainer::iterator 232 AudioRendererSinkCacheImpl::CacheContainer::iterator
266 AudioRendererSinkCacheImpl::FindCacheEntry_Locked( 233 AudioRendererSinkCacheImpl::FindHealthyCacheEntry_Locked(
267 int source_render_frame_id, 234 int source_render_frame_id,
268 const std::string& device_id, 235 const std::string& device_id,
269 const url::Origin& security_origin, 236 const url::Origin& security_origin,
270 bool unused_only) { 237 bool unused_only) {
271 return std::find_if( 238 return std::find_if(
272 cache_.begin(), cache_.end(), 239 cache_.begin(), cache_.end(),
273 [source_render_frame_id, &device_id, &security_origin, 240 [source_render_frame_id, &device_id, &security_origin,
274 unused_only](const CacheEntry& val) { 241 unused_only](const CacheEntry& val) {
275 if (val.used && unused_only) 242 if (val.used && unused_only)
276 return false; 243 return false;
277 if (val.source_render_frame_id != source_render_frame_id) 244 if (val.source_render_frame_id != source_render_frame_id)
278 return false; 245 return false;
279 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) && 246 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) &&
280 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) { 247 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) {
281 // Both device IDs represent the same default device => do not compare 248 // Both device IDs represent the same default device => do not compare
282 // them; the default device is always authorized => ignore security 249 // them; the default device is always authorized => ignore security
283 // origin. 250 // origin.
284 return true; 251 return SinkIsHealthy(val.sink.get());
Guido Urdaneta 2016/11/25 22:13:17 Can a healthy sink become unhealthy? If not, why c
DaleCurtis 2016/11/28 19:00:37 Yes, OnError() may be fired by the browser side.
o1ka 2016/11/30 15:15:03 It does not change device status though. We don't
285 } 252 }
286 return val.device_id == device_id && 253 return val.device_id == device_id &&
287 val.security_origin == security_origin; 254 val.security_origin == security_origin &&
255 SinkIsHealthy(val.sink.get());
Guido Urdaneta 2016/11/25 22:13:17 ditto
o1ka 2016/11/30 15:15:04 Done.
288 }); 256 });
289 }; 257 };
290 258
259 void AudioRendererSinkCacheImpl::CacheUnusedSinkIfHealthy(
260 int source_render_frame_id,
261 const std::string& device_id,
262 const url::Origin& security_origin,
263 scoped_refptr<media::AudioRendererSink> sink) {
264 if (!SinkIsHealthy(sink.get())) {
265 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.HealthySinkCached",
266 false);
267 return;
268 }
269
270 CacheEntry cache_entry = {source_render_frame_id, device_id, security_origin,
271 sink, false /* not used */};
272
273 {
274 base::AutoLock auto_lock(cache_lock_);
275 cache_.push_back(cache_entry);
276 }
277
278 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.HealthySinkCached", true);
279 DeleteLaterIfUnused(cache_entry.sink.get());
280 }
281
291 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() { 282 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
292 return cache_.size(); 283 return cache_.size();
293 } 284 }
294 285
295 } // namespace content 286 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/audio_renderer_sink_cache_impl.h ('k') | content/renderer/media/audio_renderer_sink_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698