Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
|
miu
2016/05/12 21:53:06
To be consistent with how this is done for other i
o1ka
2016/05/17 17:17:23
Done.
| |
| 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/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 | |
| 13 namespace content { | |
| 14 | |
| 15 // static | |
| 16 std::unique_ptr<AudioRendererSinkCache> | |
| 17 AudioRendererSinkCache::CreateDefault() { | |
|
chcunningham
2016/05/12 19:56:31
You named MixerManager's method ::Create() (no Def
o1ka
2016/05/17 17:17:23
Done.
| |
| 18 return base::WrapUnique(new AudioRendererSinkCacheImpl( | |
| 19 base::ThreadTaskRunnerHandle::Get(), | |
| 20 base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink))); | |
| 21 } | |
| 22 | |
| 23 AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl( | |
| 24 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
| 25 const CreateSinkCallback& create_sink_cb) | |
| 26 : task_runner_(task_runner), | |
| 27 create_sink_cb_(create_sink_cb), | |
| 28 weak_ptr_factory_(this) {} | |
| 29 | |
| 30 AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() { | |
| 31 for (auto& iter : sinks_) | |
|
miu
2016/05/12 21:53:06
Consider (for safety):
sinks_lock_.AssertNotHel
o1ka
2016/05/17 17:17:23
AFAIK there is no AssertNotHeld() in chromium, onl
| |
| 32 iter.second.sink->Stop(); | |
| 33 } | |
| 34 | |
| 35 media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo( | |
| 36 int source_render_frame_id, | |
| 37 int session_id, | |
| 38 const std::string& device_id, | |
| 39 const url::Origin& security_origin) { | |
| 40 if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id, | |
| 41 device_id)) { | |
| 42 // We are provided with session id instead of device id. Session id is | |
| 43 // unique, so we can't find any matching sink. Creating a new one. | |
| 44 scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run( | |
| 45 source_render_frame_id, session_id, device_id, security_origin); | |
| 46 | |
| 47 const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo(); | |
| 48 DVLOG(1) << "GetSinkInfo: address: " << sink.get() | |
| 49 << " - used session to create new sink. source_render_frame_id: " | |
| 50 << source_render_frame_id << " session_id: " << session_id | |
| 51 << " device_id: " << device_id | |
| 52 << " security_origin: " << security_origin << " Device info: [" | |
| 53 << device_info.AsHumanReadableString() << "] "; | |
| 54 | |
| 55 base::AutoLock auto_lock(sinks_lock_); | |
| 56 // Cache a newly-created sink, using device id obtained from the sink. | |
| 57 sinks_.insert( | |
| 58 std::make_pair(SinkKey(source_render_frame_id, device_info.device_id(), | |
|
miu
2016/05/12 21:53:06
Does session_id need to be in the SinkKey?
o1ka
2016/05/17 17:17:23
No. Session_id is a mapping between an open input
| |
| 59 security_origin), | |
| 60 AudioRendererSinkReference(sink, false /*not in use*/))); | |
| 61 // And schedule it for deletion. | |
| 62 DeleteLaterIfUnused(sink.get()); | |
| 63 return device_info; | |
| 64 } | |
| 65 | |
| 66 // We are provided with the actual device id. | |
| 67 const SinkKey key(source_render_frame_id, device_id, security_origin); | |
| 68 base::AutoLock auto_lock(sinks_lock_); | |
| 69 | |
| 70 { | |
| 71 auto sink_iter = sinks_.find(key); | |
| 72 if (sink_iter != sinks_.end()) { | |
| 73 // A matching cached sink is found. | |
| 74 DVLOG(1) << "GetSinkInfo: address: " << sink_iter->second.sink.get() | |
| 75 << " - reusing a cached sink. source_render_frame_id: " | |
| 76 << source_render_frame_id << " session_id: " << session_id | |
| 77 << " device_id: " << device_id | |
| 78 << " security_origin: " << security_origin << " Device info: [" | |
| 79 << sink_iter->second.sink->GetOutputDeviceInfo() | |
| 80 .AsHumanReadableString() | |
| 81 << "] "; | |
| 82 return sink_iter->second.sink->GetOutputDeviceInfo(); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 // No matching sink is found, create one and cache it - still under the lock, | |
| 87 // see the comment to InsertNewSink(). | |
| 88 media::AudioRendererSink* sink = InsertNewSink(key, false /*not in use*/); | |
| 89 DVLOG(1) << "GetSinkInfo: address: " << sink | |
| 90 << " - no matching cached sink found, created a new one." | |
| 91 << " source_render_frame_id: " << source_render_frame_id | |
| 92 << " session_id: " << session_id << " device_id: " << device_id | |
| 93 << " security_origin: " << security_origin << " Device info: [" | |
| 94 << sink->GetOutputDeviceInfo().AsHumanReadableString() << "] "; | |
| 95 // Schedule it for deletion. | |
| 96 DeleteLaterIfUnused(sink); | |
| 97 return sink->GetOutputDeviceInfo(); | |
| 98 } | |
| 99 | |
| 100 media::AudioRendererSink* AudioRendererSinkCacheImpl::GetSink( | |
| 101 int source_render_frame_id, | |
| 102 const std::string& device_id, | |
| 103 const url::Origin& security_origin) { | |
| 104 const SinkKey key(source_render_frame_id, device_id, security_origin); | |
| 105 base::AutoLock auto_lock(sinks_lock_); | |
| 106 | |
| 107 const auto range = sinks_.equal_range(key); | |
| 108 for (auto iter = range.first; iter != range.second; ++iter) { | |
| 109 if (!iter->second.used) { | |
| 110 // Found unused sink; mark it as used and return. | |
| 111 DVLOG(1) << "GetSink: address: " << iter->second.sink.get() | |
| 112 << " - found unused cached sink, reusing it." | |
| 113 << " source_render_frame_id: " << source_render_frame_id | |
| 114 << " device_id: " << device_id | |
| 115 << " security_origin: " << security_origin; | |
| 116 | |
| 117 iter->second.used = true; | |
| 118 return iter->second.sink.get(); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 // No unused sink is found, create one, mark it used, cache it and return. | |
| 123 media::AudioRendererSink* sink = InsertNewSink(key, true); | |
|
miu
2016/05/12 21:53:06
Should there be an upper-bound, where we decide to
o1ka
2016/05/17 17:17:23
No, not for now at least. Temporary sinks created
| |
| 124 DVLOG(1) << "GetSink: address: " << sink | |
| 125 << " - no unused cached sink found, created a new one." | |
| 126 << " source_render_frame_id: " << source_render_frame_id | |
| 127 << " device_id: " << device_id | |
| 128 << " security_origin: " << security_origin; | |
| 129 return sink; | |
| 130 } | |
| 131 | |
| 132 void AudioRendererSinkCacheImpl::ReleaseSink(media::AudioRendererSink* sink) { | |
| 133 // We don't know the sink state, so won't reused it. Delete it immediately. | |
| 134 DeleteSink(sink, true); | |
| 135 } | |
| 136 | |
| 137 void AudioRendererSinkCacheImpl::DeleteLaterIfUnused( | |
| 138 media::AudioRendererSink* sink) { | |
| 139 DVLOG(1) << "DeleteLaterIfUnused: address: " << sink; | |
| 140 | |
| 141 task_runner_->PostDelayedTask( | |
| 142 FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink, | |
| 143 weak_ptr_factory_.GetWeakPtr(), sink, | |
| 144 false /*do not delete if used*/), | |
| 145 base::TimeDelta::FromMilliseconds(kDeleteTimeoutMs)); | |
| 146 } | |
| 147 | |
| 148 void AudioRendererSinkCacheImpl::DeleteSink(media::AudioRendererSink* sink, | |
| 149 bool force_delete_used) { | |
| 150 DCHECK(sink); | |
| 151 base::AutoLock auto_lock(sinks_lock_); | |
| 152 | |
| 153 // The sink key is not used for look up, to keep the interface simple: | |
| 154 // otherwise both the key and the sink pointer needs to be specified, since | |
| 155 // the key is not unique. Looking up by the key won't give any significant | |
| 156 // performance improvement, since the number of sinks is small. | |
| 157 for (auto iter = sinks_.begin(); iter != sinks_.end(); ++iter) { | |
| 158 if (iter->second.sink.get() == sink) { // Sink is found. | |
|
miu
2016/05/12 21:53:06
nit: Invert the if-statement expression so the ~25
o1ka
2016/05/17 17:17:24
Done. (I hope I did not miss anything)
| |
| 159 AudioRendererSinkReference& sink_ref(iter->second); | |
| 160 | |
| 161 DVLOG(1) << "DeleteSink: address: " << sink | |
| 162 << " force_delete_used: " << force_delete_used | |
| 163 << " in use: " << sink_ref.used << " source_render_frame_id: " | |
| 164 << iter->first.source_render_frame_id | |
| 165 << " device_id: " << iter->first.device_id | |
| 166 << " security_origin: " << iter->first.security_origin; | |
| 167 | |
| 168 // When |force_delete_used| is set, it's expected that we are deleting a | |
| 169 // used sink. | |
| 170 DCHECK((!force_delete_used) || (force_delete_used && sink_ref.used)) | |
| 171 << "Attempt to delete a non-aquired sink."; | |
| 172 | |
| 173 if (force_delete_used || !sink_ref.used) { // We can delete it. | |
| 174 sink_ref.sink->Stop(); // Sink must be stopped before deletion. | |
| 175 sinks_.erase(iter); | |
| 176 DVLOG(1) << "DeleteSink: address: " << sink << " deleted. "; | |
| 177 } else { | |
| 178 DVLOG(1) << "DeleteSink: address: " << sink | |
| 179 << " sink in use, skipping deletion."; | |
| 180 } | |
| 181 | |
| 182 return; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 // If we get here and |force_delete_used| is not set it means the sink | |
| 187 // scheduled for deletion get aquired and released before scheduled deletion - | |
|
chcunningham
2016/05/12 19:56:31
s/get/got/
o1ka
2016/05/17 17:17:24
Done.
| |
| 188 // this is ok. | |
| 189 DCHECK(!force_delete_used) << "Attempt to delete non-existing sink."; | |
| 190 } | |
| 191 | |
| 192 media::AudioRendererSink* AudioRendererSinkCacheImpl::InsertNewSink( | |
|
chcunningham
2016/05/12 19:56:31
I'm used to seeing such methods named *_Locked - n
miu
2016/05/12 21:53:06
I've also seen people name methods like this: Inse
o1ka
2016/05/17 17:17:24
Done.
| |
| 193 const SinkKey& key, | |
| 194 bool used) { | |
| 195 sinks_lock_.AssertAcquired(); | |
| 196 const AudioRendererSinkMap::iterator& iter = sinks_.insert(std::make_pair( | |
| 197 key, AudioRendererSinkReference( | |
| 198 create_sink_cb_.Run(key.source_render_frame_id, 0, key.device_id, | |
| 199 key.security_origin), | |
| 200 used))); | |
| 201 return iter->second.sink.get(); | |
| 202 } | |
| 203 | |
| 204 AudioRendererSinkCacheImpl::SinkKey::SinkKey(int source_render_frame_id, | |
| 205 const std::string& device_id, | |
| 206 const url::Origin& security_origin) | |
| 207 : source_render_frame_id(source_render_frame_id), | |
| 208 device_id(device_id), | |
| 209 security_origin(security_origin) {} | |
| 210 | |
| 211 AudioRendererSinkCacheImpl::SinkKey::SinkKey(const SinkKey& other) = default; | |
| 212 | |
| 213 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 214 AudioRendererSinkReference(scoped_refptr<media::AudioRendererSink> sink, | |
| 215 bool used) | |
| 216 : sink(sink), used(used) {} | |
| 217 | |
| 218 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 219 AudioRendererSinkReference(const AudioRendererSinkReference& other) = | |
| 220 default; | |
| 221 | |
| 222 AudioRendererSinkCacheImpl::AudioRendererSinkReference:: | |
| 223 ~AudioRendererSinkReference() {} | |
| 224 | |
| 225 } // namespace content | |
| OLD | NEW |