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

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: Review comments addressed, map->vector in AudioRendererCacheImpl 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..2a16606baa88a33b4928a3686a73851569e076e3
--- /dev/null
+++ b/content/renderer/media/audio_renderer_sink_cache_impl.cc
@@ -0,0 +1,294 @@
+// 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 <algorithm>
+
+#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 "media/audio/audio_device_description.h"
+#include "media/base/audio_renderer_sink.h"
+#include "url/origin.h"
+
+namespace {
+constexpr int kDeleteTimeoutMs = 5000;
+} // namespace
+
+namespace content {
+
+// Cached sink data.
+struct AudioRendererSinkCacheImpl::CacheEntry {
+ int source_render_frame_id;
+ std::string device_id;
+ url::Origin security_origin;
+ scoped_refptr<media::AudioRendererSink> sink; // Sink instance
+ bool used; // True if in use by a client.
+};
+
+// Helper class for CacheEntry lookup.
DaleCurtis 2016/05/23 18:29:08 Why choose a helper class vs a lambda?
o1ka 2016/05/23 19:21:34 It's used in two places and requires a quite lot o
DaleCurtis 2016/05/23 20:20:26 Hmm, looks like my original comment got eaten. I c
o1ka 2016/05/24 15:00:41 Yes, this is another way to implement it. I don't
+class CacheEntryFinder {
+ public:
+ CacheEntryFinder(int source_render_frame_id,
+ const std::string& device_id,
+ const url::Origin& security_origin,
+ bool unused_only)
+ : source_render_frame_id_(source_render_frame_id),
+ device_id_(device_id),
+ security_origin_(security_origin),
+ unused_only_(unused_only) {}
+
+ bool operator()(const AudioRendererSinkCacheImpl::CacheEntry& s) const {
+ if (s.used && unused_only_)
+ return false;
+
+ if (s.source_render_frame_id != source_render_frame_id_)
+ return false;
+
+ if (media::AudioDeviceDescription::IsDefaultDevice(device_id_) &&
+ media::AudioDeviceDescription::IsDefaultDevice(s.device_id)) {
+ // Both device IDs represent the same default device => do not compare
+ // them; the default device is always authorized => ignoring security
+ // origin.
+ return true;
+ }
+
+ return (s.device_id == device_id_) &&
+ (s.security_origin == security_origin_);
+ }
+
+ private:
+ int source_render_frame_id_;
+ std::string device_id_;
+ url::Origin security_origin_;
+ bool unused_only_;
+};
+
+// 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)
+ : task_runner_(task_runner),
DaleCurtis 2016/05/23 18:29:08 Isn't std::move necessary now whenever you pass-by
o1ka 2016/05/24 15:00:41 Done.
+ create_sink_cb_(create_sink_cb),
+ delete_timeout_ms_(delete_timeout_ms),
DaleCurtis 2016/05/23 18:29:08 store as timedelta, FromMilliseconds(...)
o1ka 2016/05/24 15:00:41 Done.
+ weak_ptr_factory_(this) {}
DaleCurtis 2016/05/23 18:29:08 If you take my suggestion below to use a Repeating
o1ka 2016/05/24 15:00:41 This is a nice suggestion. The advantage of the cu
+
+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) {
+ CacheEntry cache_entry;
+ cache_entry.source_render_frame_id = source_render_frame_id;
+ cache_entry.security_origin = security_origin;
+ cache_entry.used = false;
+
+ 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.
+ cache_entry.sink = create_sink_cb_.Run(source_render_frame_id, session_id,
+ device_id, security_origin);
+ cache_entry.device_id = cache_entry.sink->GetOutputDeviceInfo().device_id();
+
+ DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
+ << " - used session to create new sink.";
+ } else {
+ // Ignore session id.
+ {
+ base::AutoLock auto_lock(cache_lock_);
DaleCurtis 2016/05/23 18:29:08 Instead of dropping in and out of lock state (some
o1ka 2016/05/23 19:21:34 Ok will do.
o1ka 2016/05/24 15:00:41 Done.
+ auto cache_iter = std::find_if(
+ cache_.begin(), cache_.end(),
+ CacheEntryFinder(source_render_frame_id, device_id, security_origin,
+ false /* unused_only */));
+ if (cache_iter != cache_.end()) {
+ // A matching cached sink is found.
+ DVLOG(1)
+ << "GetSinkInfo: address: " << cache_iter->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: ["
+ << cache_iter->sink->GetOutputDeviceInfo().AsHumanReadableString()
+ << "] ";
+
+ return cache_iter->sink->GetOutputDeviceInfo();
+ } // if (cache_iter != cache_.end())
+ } // Lock scope.
+
+ // No matching sink found, create a new one.
+ cache_entry.device_id = device_id;
+ cache_entry.sink = create_sink_cb_.Run(
+ source_render_frame_id, 0 /* session_id */, device_id, security_origin);
+
+ DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
+ << " - no matching cached sink found, created a new one.";
+ }
+
+ {
+ // Cache a newly-created sink.
+ base::AutoLock auto_lock(cache_lock_);
+ cache_.push_back(cache_entry);
+ }
+
+ // Schedule it for deletion.
DaleCurtis 2016/05/23 18:29:08 Why schedule them individually for deletion vs hav
o1ka 2016/05/23 19:21:34 Because it's quite a rare occasion when output par
o1ka 2016/05/23 19:26:27 (It was like that in the original draft version wh
DaleCurtis 2016/05/23 20:20:26 You wouldn't run the timer the whole time. You'd o
o1ka 2016/05/24 15:00:41 I looked into CancelableCallback before implementi
DaleCurtis 2016/05/24 20:41:42 This isn't true of WeakPtr either. You have a race
o1ka 2016/05/25 12:20:51 Oh, I've had a feeling that I might be missing som
DaleCurtis 2016/05/25 16:48:52 The multiple threads thing isn't a problem if you
o1ka 2016/05/25 17:00:56 Would't I need to use a weak pointer to post that
+ DeleteLaterIfUnused(cache_entry.sink.get());
+
+ DVLOG(1) << "GetSinkInfo: address: " << cache_entry.sink.get()
+ << " inserted. source_render_frame_id: " << source_render_frame_id
+ << " session_id: " << session_id << " device_id: " << device_id
+ << " security_origin: " << security_origin << " Device info: ["
+ << cache_entry.sink->GetOutputDeviceInfo().AsHumanReadableString()
+ << "] ";
+
+ return cache_entry.sink->GetOutputDeviceInfo();
+}
+
+scoped_refptr<media::AudioRendererSink> AudioRendererSinkCacheImpl::GetSink(
+ int source_render_frame_id,
+ const std::string& device_id,
+ const url::Origin& security_origin) {
+ {
+ base::AutoLock auto_lock(cache_lock_);
DaleCurtis 2016/05/23 18:29:08 Ditto on lock once.
o1ka 2016/05/24 15:00:41 Done.
+
+ auto cache_iter =
+ std::find_if(cache_.begin(), cache_.end(),
+ CacheEntryFinder(source_render_frame_id, device_id,
+ security_origin, true /* unused_only */));
+
+ if (cache_iter != cache_.end()) {
+ // Found unused sink; mark it as used and return.
+ DVLOG(1) << "GetSink: address: " << cache_iter->sink.get()
+ << " - found unused cached sink, reusing it."
+ << " source_render_frame_id: " << source_render_frame_id
+ << " device_id: " << device_id
+ << " security_origin: " << security_origin;
+
+ cache_iter->used = true;
+ return cache_iter->sink;
+ } // for
+ } // Lock scope.
+
+ // No unused sink is found, create one, mark it used, cache it and return.
+ CacheEntry cache_entry;
+ cache_entry.source_render_frame_id = source_render_frame_id;
+ cache_entry.device_id = device_id;
+ cache_entry.security_origin = security_origin;
+ cache_entry.used = true;
+
+ cache_entry.sink = create_sink_cb_.Run(
+ source_render_frame_id, 0 /* session_id */, device_id, security_origin);
+
+ base::AutoLock auto_lock(cache_lock_);
+ cache_.push_back(cache_entry);
+
+ DVLOG(1) << "GetSink: address: " << cache_entry.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 cache_entry.sink;
+}
+
+void AudioRendererSinkCacheImpl::ReleaseSink(
+ const media::AudioRendererSink* sink_ptr) {
+ // We don't know the sink state, so won't reuse it. Delete it immediately.
+ DeleteSink(sink_ptr, true);
+}
+
+void AudioRendererSinkCacheImpl::DeleteLaterIfUnused(
+ const media::AudioRendererSink* sink_ptr) {
+ DVLOG(1) << "DeleteLaterIfUnused: address: " << sink_ptr;
+
+ task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&AudioRendererSinkCacheImpl::DeleteSink,
+ weak_ptr_factory_.GetWeakPtr(), sink_ptr,
+ false /*do not delete if used*/),
+ base::TimeDelta::FromMilliseconds(delete_timeout_ms_));
+}
+
+void AudioRendererSinkCacheImpl::DeleteSink(
+ const media::AudioRendererSink* sink_ptr,
+ bool force_delete_used) {
+ DCHECK(sink_ptr);
+
+ scoped_refptr<media::AudioRendererSink> sink_to_stop;
+
+ {
+ base::AutoLock auto_lock(cache_lock_);
+
+ // Looking up the sink by its pointer.
+ auto cache_iter = std::find_if(cache_.begin(), cache_.end(),
+ [&sink_ptr](const CacheEntry& val) {
+ return val.sink.get() == sink_ptr;
+ });
+
+ if (cache_iter == cache_.end()) {
+ // 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_ptr
+ << " could not find a sink which is supposed to be in use";
+
+ DVLOG(1) << "DeleteSink: address: " << sink_ptr
+ << " force_delete_used = true - already deleted.";
+ return;
+ }
+
+ DVLOG(1) << "DeleteSink: address: " << sink_ptr
+ << " force_delete_used: " << force_delete_used
+ << " in use: " << cache_iter->used << " source_render_frame_id: "
+ << cache_iter->source_render_frame_id
+ << " device_id: " << cache_iter->device_id
+ << " security_origin: " << cache_iter->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 && cache_iter->used))
+ << "Attempt to delete a non-aquired sink.";
+
+ if (!force_delete_used && cache_iter->used) {
+ DVLOG(1) << "DeleteSink: address: " << sink_ptr
+ << " sink in use, skipping deletion.";
+ return;
+ }
+
+ // To stop the sink before deletion if it's not used, we need to hold
+ // a ref to it.
+ if (!cache_iter->used)
+ sink_to_stop = cache_iter->sink;
+
+ cache_.erase(cache_iter);
+ DVLOG(1) << "DeleteSink: address: " << sink_ptr;
+ } // Lock scope;
+
+ // Stop the sink out of the lock scope.
+ if (sink_to_stop.get()) {
+ DCHECK_EQ(sink_ptr, sink_to_stop.get());
+ sink_to_stop->Stop();
+ DVLOG(1) << "DeleteSink: address: " << sink_ptr << " stopped.";
+ }
+}
+
+int AudioRendererSinkCacheImpl::GetCacheSizeForTesting() {
+ return cache_.size();
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698