Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |