| 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, | |
| 85 std::string() /* device_id */, security_origin, | |
| 86 nullptr /* sink */, false /* not used */}; | |
| 87 | |
| 88 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, | 89 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, |
| 89 device_id)) { | 90 device_id)) { |
| 90 // We are provided with session id instead of device id. Session id is | 91 // 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. | 92 // 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, | 93 scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run( |
| 93 device_id, security_origin); | 94 source_render_frame_id, session_id, device_id, security_origin); |
| 94 cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id(); | |
| 95 | 95 |
| 96 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get() | 96 CacheUnusedSinkIfHealthy(source_render_frame_id, |
| 97 << " - used session to create new sink."; | 97 sink->GetOutputDeviceInfo().device_id(), |
| 98 | 98 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( | 99 UMA_HISTOGRAM_ENUMERATION( |
| 103 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", | 100 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", |
| 104 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID, SINK_CACHE_LAST_ENTRY); | 101 SINK_CACHE_MISS_CANNOT_LOOKUP_BY_SESSION_ID, SINK_CACHE_LAST_ENTRY); |
| 105 | 102 return sink->GetOutputDeviceInfo(); |
| 106 } else { | 103 } |
| 107 // Ignore session id. | 104 // Ignore session id. |
| 105 { |
| 108 base::AutoLock auto_lock(cache_lock_); | 106 base::AutoLock auto_lock(cache_lock_); |
| 109 | |
| 110 auto cache_iter = | 107 auto cache_iter = |
| 111 FindCacheEntry_Locked(source_render_frame_id, device_id, | 108 FindCacheEntry_Locked(source_render_frame_id, device_id, |
| 112 security_origin, false /* unused_only */); | 109 security_origin, false /* unused_only */); |
| 113 | |
| 114 if (cache_iter != cache_.end()) { | 110 if (cache_iter != cache_.end()) { |
| 115 // A matching cached sink is found. | 111 // A matching cached sink is found. |
| 116 DVLOG(1) << "GetSinkInfo: address: " << cache_iter->sink.get() | |
| 117 << " - reused a cached sink."; | |
| 118 | |
| 119 UMA_HISTOGRAM_ENUMERATION( | 112 UMA_HISTOGRAM_ENUMERATION( |
| 120 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", | 113 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", |
| 121 SINK_CACHE_HIT, SINK_CACHE_LAST_ENTRY); | 114 SINK_CACHE_HIT, SINK_CACHE_LAST_ENTRY); |
| 122 return cache_iter->sink->GetOutputDeviceInfo(); | 115 return cache_iter->sink->GetOutputDeviceInfo(); |
| 123 } | 116 } |
| 124 | |
| 125 // No matching sink found, create a new one. | |
| 126 cache_entry.device_id = device_id; | |
| 127 cache_entry.sink = create_sink_cb_.Run( | |
| 128 source_render_frame_id, 0 /* session_id */, device_id, security_origin); | |
| 129 | |
| 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( | |
| 136 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", | |
| 137 SINK_CACHE_MISS_NO_SINK, SINK_CACHE_LAST_ENTRY); | |
| 138 } | 117 } |
| 139 | 118 |
| 140 // Schedule it for deletion. | 119 // No matching sink found, create a new one. |
| 141 DeleteLaterIfUnused(cache_entry.sink.get()); | 120 scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run( |
| 121 source_render_frame_id, 0 /* session_id */, device_id, security_origin); |
| 122 CacheUnusedSinkIfHealthy(source_render_frame_id, device_id, security_origin, |
| 123 sink); |
| 124 UMA_HISTOGRAM_ENUMERATION( |
| 125 "Media.Audio.Render.SinkCache.GetOutputDeviceInfoCacheUtilization", |
| 126 SINK_CACHE_MISS_NO_SINK, SINK_CACHE_LAST_ENTRY); |
| 142 | 127 |
| 143 DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get() | 128 //|sink| is ref-counted, so it's ok if it is removed from cache before we get |
| 144 << " created. source_render_frame_id: " << source_render_frame_id | 129 // here. |
| 145 << " session_id: " << session_id << " device_id: " << device_id | 130 return sink->GetOutputDeviceInfo(); |
| 146 << " security_origin: " << security_origin; | |
| 147 | |
| 148 return cache_entry.sink->GetOutputDeviceInfo(); | |
| 149 } | 131 } |
| 150 | 132 |
| 151 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink( | 133 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink( |
| 152 int source_render_frame_id, | 134 int source_render_frame_id, |
| 153 const std::string& device_id, | 135 const std::string& device_id, |
| 154 const url::Origin& security_origin) { | 136 const url::Origin& security_origin) { |
| 155 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.UsedForSinkCreation", | 137 UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.SinkCache.UsedForSinkCreation", |
| 156 true); | 138 true); |
| 157 | 139 |
| 158 base::AutoLock auto_lock(cache_lock_); | 140 base::AutoLock auto_lock(cache_lock_); |
| 159 | 141 |
| 160 auto cache_iter = | 142 auto cache_iter = |
| 161 FindCacheEntry_Locked(source_render_frame_id, device_id, security_origin, | 143 FindCacheEntry_Locked(source_render_frame_id, device_id, security_origin, |
| 162 true /* unused sink only */); | 144 true /* unused sink only */); |
| 163 | 145 |
| 164 if (cache_iter != cache_.end()) { | 146 if (cache_iter != cache_.end()) { |
| 165 // Found unused sink; mark it as used and return. | 147 // 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; | 148 cache_iter->used = true; |
| 170 UMA_HISTOGRAM_BOOLEAN( | 149 UMA_HISTOGRAM_BOOLEAN( |
| 171 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", true); | 150 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", true); |
| 172 return cache_iter->sink; | 151 return cache_iter->sink; |
| 173 } | 152 } |
| 174 | 153 |
| 175 // No unused sink is found, create one, mark it used, cache it and return. | 154 // No unused sink is found, create one, mark it used, cache it and return. |
| 176 CacheEntry cache_entry = { | 155 CacheEntry cache_entry = { |
| 177 source_render_frame_id, device_id, security_origin, | 156 source_render_frame_id, device_id, security_origin, |
| 178 create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */, device_id, | 157 create_sink_cb_.Run(source_render_frame_id, 0 /* session_id */, device_id, |
| 179 security_origin), | 158 security_origin), |
| 180 true /* used */}; | 159 true /* used */}; |
| 181 | 160 |
| 182 cache_.push_back(cache_entry); | 161 if (SinkIsHealthy(cache_entry.sink.get())) |
| 162 cache_.push_back(cache_entry); |
| 183 | 163 |
| 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 13 matching lines...) Expand all Loading... |
| 212 | 187 |
| 213 { | 188 { |
| 214 base::AutoLock auto_lock(cache_lock_); | 189 base::AutoLock auto_lock(cache_lock_); |
| 215 | 190 |
| 216 // Looking up the sink by its pointer. | 191 // Looking up the sink by its pointer. |
| 217 auto cache_iter = std::find_if(cache_.begin(), cache_.end(), | 192 auto cache_iter = std::find_if(cache_.begin(), cache_.end(), |
| 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 | |
| 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; | 198 return; |
| 232 } | |
| 233 | 199 |
| 234 // When |force_delete_used| is set, it's expected that we are deleting a | 200 // When |force_delete_used| is set, it's expected that we are deleting a |
| 235 // used sink. | 201 // used sink. |
| 236 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used)) | 202 DCHECK((!force_delete_used) || (force_delete_used && cache_iter->used)) |
| 237 << "Attempt to delete a non-aquired sink."; | 203 << "Attempt to delete a non-aquired sink."; |
| 238 | 204 |
| 239 if (!force_delete_used && cache_iter->used) { | 205 if (!force_delete_used && cache_iter->used) |
| 240 DVLOG(1) << "DeleteSink: address: " << sink_ptr | |
| 241 << " sink in use, skipping deletion."; | |
| 242 return; | 206 return; |
| 243 } | |
| 244 | 207 |
| 245 // To stop the sink before deletion if it's not used, we need to hold | 208 // To stop the sink before deletion if it's not used, we need to hold |
| 246 // a ref to it. | 209 // a ref to it. |
| 247 if (!cache_iter->used) { | 210 if (!cache_iter->used) { |
| 248 sink_to_stop = cache_iter->sink; | 211 sink_to_stop = cache_iter->sink; |
| 249 UMA_HISTOGRAM_BOOLEAN( | 212 UMA_HISTOGRAM_BOOLEAN( |
| 250 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false); | 213 "Media.Audio.Render.SinkCache.InfoSinkReusedForOutput", false); |
| 251 } | 214 } |
| 252 | 215 |
| 253 cache_.erase(cache_iter); | 216 cache_.erase(cache_iter); |
| 254 DVLOG(1) << "DeleteSink: address: " << sink_ptr; | |
| 255 } // Lock scope; | 217 } // Lock scope; |
| 256 | 218 |
| 257 // Stop the sink out of the lock scope. | 219 // Stop the sink out of the lock scope. |
| 258 if (sink_to_stop.get()) { | 220 if (sink_to_stop.get()) { |
| 259 DCHECK_EQ(sink_ptr, sink_to_stop.get()); | 221 DCHECK_EQ(sink_ptr, sink_to_stop.get()); |
| 260 sink_to_stop->Stop(); | 222 sink_to_stop->Stop(); |
| 261 DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped."; | |
| 262 } | 223 } |
| 263 } | 224 } |
| 264 | 225 |
| 265 AudioRendererSinkCacheImpl::CacheContainer::iterator | 226 AudioRendererSinkCacheImpl::CacheContainer::iterator |
| 266 AudioRendererSinkCacheImpl::FindCacheEntry_Locked( | 227 AudioRendererSinkCacheImpl::FindCacheEntry_Locked( |
| 267 int source_render_frame_id, | 228 int source_render_frame_id, |
| 268 const std::string& device_id, | 229 const std::string& device_id, |
| 269 const url::Origin& security_origin, | 230 const url::Origin& security_origin, |
| 270 bool unused_only) { | 231 bool unused_only) { |
| 271 return std::find_if( | 232 return std::find_if( |
| 272 cache_.begin(), cache_.end(), | 233 cache_.begin(), cache_.end(), |
| 273 [source_render_frame_id, &device_id, &security_origin, | 234 [source_render_frame_id, &device_id, &security_origin, |
| 274 unused_only](const CacheEntry& val) { | 235 unused_only](const CacheEntry& val) { |
| 275 if (val.used && unused_only) | 236 if (val.used && unused_only) |
| 276 return false; | 237 return false; |
| 277 if (val.source_render_frame_id != source_render_frame_id) | 238 if (val.source_render_frame_id != source_render_frame_id) |
| 278 return false; | 239 return false; |
| 279 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) && | 240 if (media::AudioDeviceDescription::IsDefaultDevice(device_id) && |
| 280 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) { | 241 media::AudioDeviceDescription::IsDefaultDevice(val.device_id)) { |
| 281 // Both device IDs represent the same default device => do not compare | 242 // Both device IDs represent the same default device => do not compare |
| 282 // them; the default device is always authorized => ignore security | 243 // them; the default device is always authorized => ignore security |
| 283 // origin. | 244 // origin. |
| 284 return true; | 245 return true; |
| 285 } | 246 } |
| 286 return val.device_id == device_id && | 247 return val.device_id == device_id && |
| 287 val.security_origin == security_origin; | 248 val.security_origin == security_origin; |
| 288 }); | 249 }); |
| 289 }; | 250 }; |
| 290 | 251 |
| 252 void AudioRendererSinkCacheImpl::CacheUnusedSinkIfHealthy( |
| 253 int source_render_frame_id, |
| 254 const std::string& device_id, |
| 255 const url::Origin& security_origin, |
| 256 scoped_refptr<media::AudioRendererSink> sink) { |
| 257 if (!SinkIsHealthy(sink.get())) |
| 258 return; |
| 259 |
| 260 CacheEntry cache_entry = {source_render_frame_id, device_id, security_origin, |
| 261 sink, false /* not used */}; |
| 262 |
| 263 { |
| 264 base::AutoLock auto_lock(cache_lock_); |
| 265 cache_.push_back(cache_entry); |
| 266 } |
| 267 |
| 268 DeleteLaterIfUnused(cache_entry.sink.get()); |
| 269 } |
| 270 |
| 291 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() { | 271 int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() { |
| 292 return cache_.size(); | 272 return cache_.size(); |
| 293 } | 273 } |
| 294 | 274 |
| 295 } // namespace content | 275 } // namespace content |
| OLD | NEW |