Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 "base/bind.h" | |
| 6 #include "base/location.h" | |
| 7 #include "base/memory/ptr_util.h" | |
| 8 #include "base/synchronization/lock.h" | |
| 9 #include "base/threading/thread_task_runner_handle.h" | |
| 10 #include "content/renderer/media/audio_device_factory.h" | |
| 11 #include "content/renderer/media/audio_renderer_sink_cache_impl.h" | |
| 12 #include "url/origin.h" | |
| 13 | |
| 14 namespace { | |
| 15 enum { kDeleteTimeoutMs = 5000 }; | |
|
miu
2016/05/19 22:27:15
As of a few weeks ago, they've just started allowi
o1ka
2016/05/23 16:16:54
Thanks. Done.
| |
| 16 } // namespace | |
| 17 | |
| 18 namespace content { | |
| 19 | |
| 20 // static | |
| 21 std::unique_ptr<AudioRendererSinkCache> AudioRendererSinkCache::Create() { | |
| 22 return base::WrapUnique(new AudioRendererSinkCacheImpl( | |
| 23 base::ThreadTaskRunnerHandle::Get(), | |
| 24 base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink), | |
| 25 kDeleteTimeoutMs)); | |
| 26 } | |
| 27 | |
| 28 AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl( | |
| 29 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
| 30 const CreateSinkCallback& create_sink_cb, | |
| 31 int delete_timeout_ms) | |
| 32 : delete_timeout_ms_(delete_timeout_ms), | |
|
miu
2016/05/19 22:27:15
nit: For consistency/readability, try to maintain
o1ka
2016/05/23 16:16:54
Done.
| |
| 33 task_runner_(task_runner), | |
| 34 create_sink_cb_(create_sink_cb), | |
| 35 weak_ptr_factory_(this) {} | |
| 36 | |
| 37 AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() { | |
| 38 // We just release all the cached sinks here. | |
| 39 } | |
| 40 | |
| 41 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo( | |
| 42 int source_render_frame_id, | |
| 43 int session_id, | |
| 44 const std::string& device_id, | |
| 45 const url::Origin& security_origin) { | |
| 46 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, | |
| 47 device_id)) { | |
| 48 // We are provided with session id instead of device id. Session id is | |
| 49 // unique, so we can't find any matching sink. Creating a new one. | |
| 50 scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run( | |
| 51 source_render_frame_id, session_id, device_id, security_origin); | |
| 52 | |
| 53 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); | |
| 54 DVLOG(1) << "GetSinkInfo: address: " << sink.get() | |
| 55 << " - used session to create new sink. source_render_frame_id: " | |
| 56 << source_render_frame_id << " session_id: " << session_id | |
| 57 << " device_id: " << device_id | |
| 58 << " security_origin: " << security_origin << " Device info: [" | |
| 59 << device_info.AsHumanReadableString() << "] "; | |
| 60 | |
| 61 const SinkKey key(source_render_frame_id, device_info.device_id(), | |
| 62 security_origin); | |
| 63 | |
| 64 base::AutoLock auto_lock(sinks_lock_); | |
|
miu
2016/05/19 22:27:15
Try to minimize how long this lock is held (here a
o1ka
2016/05/23 16:16:54
Done.
DaleCurtis
2016/05/23 18:29:08
I don't think the original comment is good advice.
| |
| 65 // Cache a newly-created sink, using device id obtained from the sink. | |
| 66 sinks_.insert(std::make_pair( | |
| 67 key, AudioRendererSinkReference(sink, false /*not in use*/))); | |
| 68 // And schedule it for deletion. | |
| 69 DeleteLaterIfUnused(key, sink.get()); | |
| 70 return device_info; | |
| 71 } | |
| 72 | |
| 73 // We are provided with the actual device id. | |
| 74 const SinkKey key(source_render_frame_id, device_id, security_origin); | |
| 75 base::AutoLock auto_lock(sinks_lock_); | |
| 76 | |
| 77 { | |
| 78 auto sink_iter = sinks_.find(key); | |
| 79 if (sink_iter != sinks_.end()) { | |
| 80 // A matching cached sink is found. | |
| 81 DVLOG(1) << "GetSinkInfo: address: " << sink_iter->second.sink.get() | |
| 82 << " - reusing a cached sink. source_render_frame_id: " | |
| 83 << source_render_frame_id << " session_id: " << session_id | |
| 84 << " device_id: " << device_id | |
| 85 << " security_origin: " << security_origin << " Device info: [" | |
| 86 << sink_iter->second.sink->GetOutputDeviceInfo() | |
| 87 .AsHumanReadableString() | |
| 88 << "] "; | |
| 89 return sink_iter->second.sink->GetOutputDeviceInfo(); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 // No matching sink is found, create one and cache it - still under the lock, | |
| 94 // see the comment to InsertNewSinkWhileLockHeld(). | |
| 95 scoped_refptr<media::AudioRendererSink> sink = | |
| 96 InsertNewSinkWhileLockHeld(key, false /*not in use*/); | |
| 97 DVLOG(1) << "GetSinkInfo: address: " << sink | |
| 98 << " - no matching cached sink found, created a new one." | |
| 99 << " source_render_frame_id: " << source_render_frame_id | |
| 100 << " session_id: " << session_id << " device_id: " << device_id | |
| 101 << " security_origin: " << security_origin << " Device info: [" | |
| 102 << sink->GetOutputDeviceInfo().AsHumanReadableString() << "] "; | |
| 103 // Schedule it for deletion. | |
| 104 DeleteLaterIfUnused(key, sink.get()); | |
| 105 return sink->GetOutputDeviceInfo(); | |
| 106 } | |
| 107 | |
| 108 scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink( | |
| 109 int source_render_frame_id, | |
| 110 const std::string& device_id, | |
| 111 const url::Origin& security_origin) { | |
| 112 const SinkKey key(source_render_frame_id, device_id, security_origin); | |
| 113 base::AutoLock auto_lock(sinks_lock_); | |
| 114 | |
| 115 const auto range = sinks_.equal_range(key); | |
| 116 for (auto iter = range.first; iter != range.second; ++iter) { | |
| 117 if (iter->second.used) | |
| 118 continue; | |
| 119 | |
| 120 // Found unused sink; mark it as used and return. | |
| 121 DVLOG(1) << "GetSink: address: " << iter->second.sink.get() | |
| 122 << " - found unused cached sink, reusing it." | |
| 123 << " source_render_frame_id: " << source_render_frame_id | |
| 124 << " device_id: " << device_id | |
| 125 << " security_origin: " << security_origin; | |
| 126 | |
| 127 iter->second.used = true; | |
| 128 return iter->second.sink; | |
| 129 } | |
| 130 | |
| 131 // No unused sink is found, create one, mark it used, cache it and return. | |
| 132 scoped_refptr<media::AudioRendererSink> sink = | |
| 133 InsertNewSinkWhileLockHeld(key, true); | |
| 134 DVLOG(1) << "GetSink: address: " << sink.get() | |
| 135 << " - no unused cached sink found, created a new one." | |
| 136 << " source_render_frame_id: " << source_render_frame_id | |
| 137 << " device_id: " << device_id | |
| 138 << " security_origin: " << security_origin; | |
| 139 return sink; | |
| 140 } | |
| 141 | |
| 142 void AudioRendererSinkCacheImpl::ReleaseSink( | |
| 143 int source_render_frame_id, | |
| 144 const std::string& device_id, | |
| 145 const url::Origin& security_origin, | |
| 146 const media::AudioRendererSink* sink) { | |
| 147 // We don't know the sink state, so won't reused it. Delete it immediately. | |
|
miu
2016/05/19 22:27:15
typo: s/reused/reuse/
o1ka
2016/05/23 16:16:54
Done.
| |
| 148 DeleteSink(SinkKey(source_render_frame_id, device_id, security_origin), sink, | |
| 149 true); | |
| 150 } | |
| 151 | |
| 152 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused( | |
| 153 const SinkKey& key, | |
| 154 const media::AudioRendererSink* sink) { | |
| 155 DVLOG(1) << "DeleteLaterIfUnused: address: " << sink; | |
| 156 | |
| 157 task_runner_->PostDelayedTask( | |
| 158 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink, | |
| 159 weak_ptr_factory_.GetWeakPtr(), key, sink, | |
| 160 false /*do not delete if used*/), | |
| 161 base::TimeDelta::FromMilliseconds(delete_timeout_ms_)); | |
| 162 } | |
| 163 | |
| 164 void AudioRendererSinkCacheImpl::DeleteSink( | |
| 165 const SinkKey& key, | |
| 166 const media::AudioRendererSink* sink, | |
| 167 bool force_delete_used) { | |
| 168 DCHECK(sink); | |
| 169 base::AutoLock auto_lock(sinks_lock_); | |
| 170 | |
| 171 const auto range = sinks_.equal_range(key); | |
| 172 for (auto iter = range.first; iter != range.second; ++iter) { | |
| 173 if (iter->second.sink.get() != sink) | |
| 174 continue; | |
| 175 | |
| 176 const AudioRendererSinkReference& sink_ref(iter->second); | |
| 177 | |
| 178 DVLOG(1) << "DeleteSink: address: " << sink | |
| 179 << " force_delete_used: " << force_delete_used | |
| 180 << " in use: " << sink_ref.used | |
| 181 << " source_render_frame_id: " << key.source_render_frame_id | |
| 182 << " device_id: " << key.device_id | |
| 183 << " security_origin: " << key.security_origin; | |
| 184 | |
| 185 // When |force_delete_used| is set, it's expected that we are deleting a | |
| 186 // used sink. | |
| 187 DCHECK((!force_delete_used) || (force_delete_used && sink_ref.used)) | |
| 188 << "Attempt to delete a non-aquired sink."; | |
| 189 | |
| 190 if (force_delete_used || !sink_ref.used) { // We can delete it. | |
| 191 if (!sink_ref.used) | |
| 192 sink_ref.sink->Stop(); // Stop the sink before deletion if not used. | |
| 193 sinks_.erase(iter); | |
| 194 DVLOG(1) << "DeleteSink: address: " << sink << " deleted. "; | |
| 195 } else { | |
| 196 DVLOG(1) << "DeleteSink: address: " << sink | |
| 197 << " sink in use, skipping deletion."; | |
| 198 } | |
| 199 | |
| 200 return; | |
| 201 } // for | |
| 202 | |
| 203 // If we got here and |force_delete_used| is not set it means the sink | |
| 204 // scheduled for deletion get aquired and released before scheduled deletion - | |
| 205 // this is ok. | |
| 206 DCHECK(!force_delete_used) << "DeleteSink: address: " << sink | |
| 207 << " could not find a sink which is supposed to be" | |
| 208 << " in use. Wrong key? source_render_frame_id: " | |
| 209 << key.source_render_frame_id | |
| 210 << " device_id: " << key.device_id | |
| 211 << " security_origin: " << key.security_origin; | |
| 212 } | |
| 213 | |
| 214 scoped_refptr<media::AudioRendererSink> | |
| 215 AudioRendererSinkCacheImpl::InsertNewSinkWhileLockHeld(const SinkKey& key, | |
| 216 bool used) { | |
| 217 sinks_lock_.AssertAcquired(); | |
| 218 const AudioRendererSinkMap::iterator& iter = sinks_.insert(std::make_pair( | |
| 219 key, AudioRendererSinkReference( | |
| 220 create_sink_cb_.Run(key.source_render_frame_id, 0, key.device_id, | |
| 221 key.security_origin), | |
| 222 used))); | |
| 223 return iter->second.sink; | |
| 224 } | |
| 225 | |
| 226 AudioRendererSinkCacheImpl::SinkKey::SinkKey(int source_render_frame_id, | |
| 227 const std::string& device_id, | |
| 228 const url::Origin& security_origin) | |
| 229 : source_render_frame_id(source_render_frame_id), | |
| 230 device_id(device_id), | |
| 231 security_origin(security_origin) {} | |
| 232 | |
| 233 AudioRendererSinkCacheImpl::SinkKey::SinkKey(const SinkKey& other) = default; | |
| 234 | |
| 235 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 236 AudioRendererSinkReference(scoped_refptr<media::AudioRendererSink> sink, | |
| 237 bool used) | |
| 238 : sink(sink), used(used) {} | |
| 239 | |
| 240 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 241 AudioRendererSinkReference(const AudioRendererSinkReference& other) = | |
| 242 default; | |
| 243 | |
| 244 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 245 ~AudioRendererSinkReference() {} | |
| 246 | |
| 247 } // namespace content | |
| OLD | NEW |