Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2501)

Unified Diff: content/renderer/media/audio_renderer_sink_cache_impl.cc

Issue 1942803002: Caching AudioOutputDevice instances in mixer manager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: First round of comments addressed Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/audio_renderer_sink_cache_impl.cc
diff --git a/content/renderer/media/audio_renderer_sink_cache_impl.cc b/content/renderer/media/audio_renderer_sink_cache_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..81a3a1e7dd3eb521264003f133c795129994d0d5
--- /dev/null
+++ b/content/renderer/media/audio_renderer_sink_cache_impl.cc
@@ -0,0 +1,246 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/renderer/media/audio_device_factory.h"
+#include "content/renderer/media/audio_renderer_sink_cache_impl.h"
+#include "url/origin.h"
+
+namespace {
+enum { kDeleteTimeoutMs = 5000 };
+} // namespace
+
+namespace content {
+
+// static
+std::unique_ptr<AudioRendererSinkCache> AudioRendererSinkCache::Create() {
+ return base::WrapUnique(new AudioRendererSinkCacheImpl(
+ base::ThreadTaskRunnerHandle::Get(),
+ base::Bind(AudioDeviceFactory::NewAudioRendererMixerSink),
+ kDeleteTimeoutMs));
+}
+
+AudioRendererSinkCacheImpl::AudioRendererSinkCacheImpl(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const CreateSinkCallback& create_sink_cb,
+ int delete_timeout_ms)
+ : delete_timeout_ms_(delete_timeout_ms),
+ task_runner_(task_runner),
+ create_sink_cb_(create_sink_cb),
+ weak_ptr_factory_(this) {}
+
+AudioRendererSinkCacheImpl::~AudioRendererSinkCacheImpl() {
+ // We just release all the cached sinks here.
+}
+
+media::OutputDeviceInfo AudioRendererSinkCacheImpl::GetSinkInfo(
+ int source_render_frame_id,
+ int session_id,
+ const std::string& device_id,
+ const url::Origin& security_origin) {
+ if (media::AudioDeviceDescription::UseSessionIdToSelectDevice(session_id,
+ device_id)) {
+ // We are provided with session id instead of device id. Session id is
+ // unique, so we can't find any matching sink. Creating a new one.
+ scoped_refptr<media::AudioRendererSink> sink = create_sink_cb_.Run(
+ source_render_frame_id, session_id, device_id, security_origin);
+
+ const media::OutputDeviceInfo& device_info = sink->GetOutputDeviceInfo();
+ DVLOG(1) << "GetSinkInfo: address: " << sink.get()
+ << " - used session to create new sink. source_render_frame_id: "
+ << source_render_frame_id << " session_id: " << session_id
+ << " device_id: " << device_id
+ << " security_origin: " << security_origin << " Device info: ["
+ << device_info.AsHumanReadableString() << "] ";
+
+ const SinkKey key(source_render_frame_id, device_info.device_id(),
+ security_origin);
+
+ base::AutoLock auto_lock(sinks_lock_);
+ // Cache a newly-created sink, using device id obtained from the sink.
+ sinks_.insert(std::make_pair(
+ key, AudioRendererSinkReference(sink, false /*not in use*/)));
+ // And schedule it for deletion.
+ DeleteLaterIfUnused(key, sink.get());
+ return device_info;
+ }
+
+ // We are provided with the actual device id.
+ const SinkKey key(source_render_frame_id, device_id, security_origin);
+ base::AutoLock auto_lock(sinks_lock_);
+
+ {
+ auto sink_iter = sinks_.find(key);
+ if (sink_iter != sinks_.end()) {
+ // A matching cached sink is found.
+ DVLOG(1) << "GetSinkInfo: address: " << sink_iter->second.sink.get()
+ << " - reusing a cached sink. source_render_frame_id: "
+ << source_render_frame_id << " session_id: " << session_id
+ << " device_id: " << device_id
+ << " security_origin: " << security_origin << " Device info: ["
+ << sink_iter->second.sink->GetOutputDeviceInfo()
+ .AsHumanReadableString()
+ << "] ";
+ return sink_iter->second.sink->GetOutputDeviceInfo();
+ }
+ }
+
+ // No matching sink is found, create one and cache it - still under the lock,
+ // see the comment to InsertNewSinkWhileLockHeld().
+ scoped_refptr<media::AudioRendererSink> sink =
+ InsertNewSinkWhileLockHeld(key, false /*not in use*/);
+ DVLOG(1) << "GetSinkInfo: address: " << sink
+ << " - no matching cached sink found, created a new one."
+ << " source_render_frame_id: " << source_render_frame_id
+ << " session_id: " << session_id << " device_id: " << device_id
+ << " security_origin: " << security_origin << " Device info: ["
+ << sink->GetOutputDeviceInfo().AsHumanReadableString() << "] ";
+ // Schedule it for deletion.
+ DeleteLaterIfUnused(key, sink.get());
+ return sink->GetOutputDeviceInfo();
+}
+
+scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink(
+ int source_render_frame_id,
+ const std::string& device_id,
+ const url::Origin& security_origin) {
+ const SinkKey key(source_render_frame_id, device_id, security_origin);
+ base::AutoLock auto_lock(sinks_lock_);
+
+ const auto range = sinks_.equal_range(key);
+ for (auto iter = range.first; iter != range.second; ++iter) {
+ if (iter->second.used)
+ continue;
+
+ // Found unused sink; mark it as used and return.
+ DVLOG(1) << "GetSink: address: " << iter->second.sink.get()
+ << " - found unused cached sink, reusing it."
+ << " source_render_frame_id: " << source_render_frame_id
+ << " device_id: " << device_id
+ << " security_origin: " << security_origin;
+
+ iter->second.used = true;
+ return iter->second.sink;
+ }
+
+ // No unused sink is found, create one, mark it used, cache it and return.
+ scoped_refptr<media::AudioRendererSink> sink =
+ InsertNewSinkWhileLockHeld(key, true);
+ DVLOG(1) << "GetSink: address: " << sink.get()
+ << " - no unused cached sink found, created a new one."
+ << " source_render_frame_id: " << source_render_frame_id
+ << " device_id: " << device_id
+ << " security_origin: " << security_origin;
+ return sink;
+}
+
+void AudioRendererSinkCacheImpl::ReleaseSink(
+ int source_render_frame_id,
+ const std::string& device_id,
+ const url::Origin& security_origin,
+ const media::AudioRendererSink* sink) {
+ // We don't know the sink state, so won't reused it. Delete it immediately.
+ DeleteSink(SinkKey(source_render_frame_id, device_id, security_origin), sink,
+ true);
+}
+
+void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
+ const SinkKey& key,
+ const media::AudioRendererSink* sink) {
+ DVLOG(1) << "DeleteLaterIfUnused: address: " << sink;
+
+ task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink,
+ weak_ptr_factory_.GetWeakPtr(), key, sink,
+ false /*do not delete if used*/),
+ base::TimeDelta::FromMilliseconds(delete_timeout_ms_));
+}
+
+void AudioRendererSinkCacheImpl::DeleteSink(
+ const SinkKey& key,
+ const media::AudioRendererSink* sink,
+ bool force_delete_used) {
+ DCHECK(sink);
+ base::AutoLock auto_lock(sinks_lock_);
+
+ const auto range = sinks_.equal_range(key);
+ for (auto iter = range.first; iter != range.second; ++iter) {
+ if (iter->second.sink.get() != sink)
+ continue;
+
+ const AudioRendererSinkReference& sink_ref(iter->second);
+
+ DVLOG(1) << "DeleteSink: address: " << sink
+ << " force_delete_used: " << force_delete_used
+ << " in use: " << sink_ref.used
+ << " source_render_frame_id: " << key.source_render_frame_id
+ << " device_id: " << key.device_id
+ << " security_origin: " << key.security_origin;
+
+ // When |force_delete_used| is set, it's expected that we are deleting a
+ // used sink.
+ DCHECK((!force_delete_used) || (force_delete_used && sink_ref.used))
+ << "Attempt to delete a non-aquired sink.";
+
+ if (force_delete_used || !sink_ref.used) { // We can delete it.
+ sink_ref.sink->Stop(); // Sink must be stopped before deletion.
+ sinks_.erase(iter);
+ DVLOG(1) << "DeleteSink: address: " << sink << " deleted. ";
+ } else {
+ DVLOG(1) << "DeleteSink: address: " << sink
+ << " sink in use, skipping deletion.";
+ }
+
+ return;
+ } // for
+
+ // If we got here and |force_delete_used| is not set it means the sink
+ // scheduled for deletion get aquired and released before scheduled deletion -
+ // this is ok.
+ DCHECK(!force_delete_used) << "DeleteSink: address: " << sink
+ << " could not find a sink which is supposed to be"
+ << " in use. Wrong key? source_render_frame_id: "
+ << key.source_render_frame_id
+ << " device_id: " << key.device_id
+ << " security_origin: " << key.security_origin;
+}
+
+scoped_refptr<media::AudioRendererSink>
+AudioRendererSinkCacheImpl::InsertNewSinkWhileLockHeld(const SinkKey& key,
+ bool used) {
+ sinks_lock_.AssertAcquired();
+ const AudioRendererSinkMap::iterator& iter = sinks_.insert(std::make_pair(
+ key, AudioRendererSinkReference(
+ create_sink_cb_.Run(key.source_render_frame_id, 0, key.device_id,
+ key.security_origin),
+ used)));
+ return iter->second.sink;
+}
+
+AudioRendererSinkCacheImpl::SinkKey::SinkKey(int source_render_frame_id,
+ const std::string& device_id,
+ const url::Origin& security_origin)
+ : source_render_frame_id(source_render_frame_id),
+ device_id(device_id),
+ security_origin(security_origin) {}
+
+AudioRendererSinkCacheImpl::SinkKey::SinkKey(const SinkKey& other) = default;
+
+AudioRendererSinkCacheImpl::AudioRendererSinkReference::
+ AudioRendererSinkReference(scoped_refptr<media::AudioRendererSink> sink,
+ bool used)
+ : sink(sink), used(used) {}
+
+AudioRendererSinkCacheImpl::AudioRendererSinkReference::
+ AudioRendererSinkReference(const AudioRendererSinkReference& other) =
+ default;
+
+AudioRendererSinkCacheImpl::AudioRendererSinkReference::
+ ~AudioRendererSinkReference() {}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698