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; |
| 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 { |
|
DaleCurtis
2016/11/30 22:00:05
Seems you could early return here to avoid the big
o1ka
2016/12/02 12:20:43
Done.
| |
| 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 = |
| 111 FindCacheEntry_Locked(source_render_frame_id, device_id, | 110 FindCacheEntry_Locked(source_render_frame_id, device_id, |
| 112 security_origin, false /* unused_only */); | 111 security_origin, 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 removed from cache before we get |
| 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 = |
| 161 FindCacheEntry_Locked(source_render_frame_id, device_id, security_origin, | 146 FindCacheEntry_Locked(source_render_frame_id, 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 | 163 |
| 182 cache_.push_back(cache_entry); | 164 bool cache_it = SinkIsHealthy(cache_entry.sink.get()); |
|
DaleCurtis
2016/11/30 22:00:05
_it naming seems incorrect for a bool.
o1ka
2016/12/02 12:20:43
Done.
| |
| 165 if (cache_it) | |
| 166 cache_.push_back(cache_entry); | |
| 183 | 167 |
| 184 DVLOG(1) << "GetSink: address: " << cache_entry.sink.get() | 168 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.HealthyUsedSinkCached", |
|
DaleCurtis
2016/11/30 22:00:05
Do we need these new metrics? With your fixes we'l
o1ka
2016/12/02 12:20:43
Done.
| |
| 185 << " - no unused cached sink found, created a new one." | 169 cache_it); |
| 186 << " source_render_frame_id: " << source_render_frame_id | 170 |
| 187 << " device_id: " << device_id | |
| 188 << " security_origin: " << security_origin; | |
| 189 return cache_entry.sink; | 171 return cache_entry.sink; |
| 190 } | 172 } |
| 191 | 173 |
| 192 void AudioRendererSinkCacheImpl::ReleaseSink( | 174 void AudioRendererSinkCacheImpl::ReleaseSink( |
| 193 const media::AudioRendererSink* sink_ptr) { | 175 const media::AudioRendererSink* sink_ptr) { |
| 194 // We don't know the sink state, so won't reuse it. Delete it immediately. | 176 // We don't know the sink state, so won't reuse it. Delete it immediately. |
| 195 DeleteSink(sink_ptr, true); | 177 DeleteSink(sink_ptr, true); |
| 196 } | 178 } |
| 197 | 179 |
| 198 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused( | 180 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused( |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 213 { | 195 { |
| 214 base::AutoLock auto_lock(cache_lock_); | 196 base::AutoLock auto_lock(cache_lock_); |
| 215 | 197 |
| 216 // Looking up the sink by its pointer. | 198 // Looking up the sink by its pointer. |
| 217 auto cache_iter = std::find_if(cache_.begin(), cache_.end(), | 199 auto cache_iter = std::find_if(cache_.begin(), cache_.end(), |
| 218 [sink_ptr](const CacheEntry& val) { | 200 [sink_ptr](const CacheEntry& val) { |
| 219 return val.sink.get() == sink_ptr; | 201 return val.sink.get() == sink_ptr; |
| 220 }); | 202 }); |
| 221 | 203 |
| 222 if (cache_iter == cache_.end()) { | 204 if (cache_iter == cache_.end()) { |
| 223 // 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. | |
| 225 DCHECK(!force_delete_used) | |
| 226 << "DeleteSink: address: " << sink_ptr | |
| 227 << " 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; | 205 return; |
| 232 } | 206 } |
| 233 | 207 |
| 234 // When |force_delete_used| is set, it's expected that we are deleting a | 208 // When |force_delete_used| is set, it's expected that we are deleting a |
| 235 // used sink. | 209 // used sink. |
| 236 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used)) | 210 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used)) |
| 237 << "Attempt to delete a non-aquired sink."; | 211 << "Attempt to delete a non-aquired sink."; |
| 238 | 212 |
| 239 if (!force_delete_used && cache_iter->used) { | 213 if (!force_delete_used && cache_iter->used) |
| 240 DVLOG(1) << "DeleteSink: address: " << sink_ptr | |
| 241 << " sink in use, skipping deletion."; | |
| 242 return; | 214 return; |
| 243 } | |
| 244 | 215 |
| 245 // To stop the sink before deletion if it's not used, we need to hold | 216 // To stop the sink before deletion if it's not used, we need to hold |
| 246 // a ref to it. | 217 // a ref to it. |
| 247 if (!cache_iter->used) { | 218 if (!cache_iter->used) { |
| 248 sink_to_stop = cache_iter->sink; | 219 sink_to_stop = cache_iter->sink; |
| 249 UMA_HISTOGRAM_BOOLEAN( | 220 UMA_HISTOGRAM_BOOLEAN( |
| 250 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false); | 221 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false); |
| 251 } | 222 } |
| 252 | 223 |
| 253 cache_.erase(cache_iter); | 224 cache_.erase(cache_iter); |
| 254 DVLOG(1) << "DeleteSink: address: " << sink_ptr; | |
| 255 } // Lock scope; | 225 } // Lock scope; |
| 256 | 226 |
| 257 // Stop the sink out of the lock scope. | 227 // Stop the sink out of the lock scope. |
| 258 if (sink_to_stop.get()) { | 228 if (sink_to_stop.get()) { |
| 259 DCHECK_EQ(sink_ptr, sink_to_stop.get()); | 229 DCHECK_EQ(sink_ptr, sink_to_stop.get()); |
| 260 sink_to_stop->Stop(); | 230 sink_to_stop->Stop(); |
| 261 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped."; | |
| 262 } | 231 } |
| 263 } | 232 } |
| 264 | 233 |
| 265 AudioRendererSinkCacheImpl::CacheContainer::iterator | 234 AudioRendererSinkCacheImpl::CacheContainer::iterator |
| 266 AudioRendererSinkCacheImpl::FindCacheEntry_Locked( | 235 AudioRendererSinkCacheImpl::FindCacheEntry_Locked( |
| 267 int source_render_frame_id, | 236 int source_render_frame_id, |
| 268 const std::string& device_id, | 237 const std::string& device_id, |
| 269 const url::Origin& security_origin, | 238 const url::Origin& security_origin, |
| 270 bool unused_only) { | 239 bool unused_only) { |
| 271 return std::find_if( | 240 return std::find_if( |
| 272 cache_.begin(), cache_.end(), | 241 cache_.begin(), cache_.end(), |
| 273 [source_render_frame_id, &device_id, &security_origin, | 242 [source_render_frame_id, &device_id, &security_origin, |
| 274 unused_only](const CacheEntry& val) { | 243 unused_only](const CacheEntry& val) { |
| 275 if (val.used && unused_only) | 244 if (val.used && unused_only) |
| 276 return false; | 245 return false; |
| 277 if (val.source_render_frame_id != source_render_frame_id) | 246 if (val.source_render_frame_id != source_render_frame_id) |
| 278 return false; | 247 return false; |
| 279 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) && | 248 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) && |
| 280 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) { | 249 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) { |
| 281 // Both device IDs represent the same default device => do not compare | 250 // Both device IDs represent the same default device => do not compare |
| 282 // them; the default device is always authorized => ignore security | 251 // them; the default device is always authorized => ignore security |
| 283 // origin. | 252 // origin. |
| 284 return true; | 253 return true; |
| 285 } | 254 } |
| 286 return val.device_id == device_id && | 255 return val.device_id == device_id && |
| 287 val.security_origin == security_origin; | 256 val.security_origin == security_origin; |
| 288 }); | 257 }); |
| 289 }; | 258 }; |
| 290 | 259 |
| 260 void AudioRendererSinkCacheImpl::CacheUnusedSinkIfHealthy( | |
| 261 int source_render_frame_id, | |
| 262 const std::string& device_id, | |
| 263 const url::Origin& security_origin, | |
| 264 scoped_refptr<media::AudioRendererSink> sink) { | |
| 265 if (!SinkIsHealthy(sink.get())) { | |
| 266 UMA_HISTOGRAM_BOOLEAN( | |
| 267 "Media.Audio.Render.SinkCache.HealthyUnusedSinkCached", false); | |
| 268 return; | |
| 269 } | |
| 270 | |
| 271 CacheEntry cache_entry = {source_render_frame_id, device_id, security_origin, | |
| 272 sink, false /* not used */}; | |
| 273 | |
| 274 { | |
| 275 base::AutoLock auto_lock(cache_lock_); | |
| 276 cache_.push_back(cache_entry); | |
| 277 } | |
| 278 | |
| 279 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.HealthyUnusedSinkCached", | |
| 280 true); | |
| 281 DeleteLaterIfUnused(cache_entry.sink.get()); | |
| 282 } | |
| 283 | |
| 291 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() { | 284 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() { |
| 292 return cache_.size(); | 285 return cache_.size(); |
| 293 } | 286 } |
| 294 | 287 |
| 295 } // namespace content | 288 } // namespace content |
| OLD | NEW |