Index: services/media/audio/audio_track_to_output_link.cc |
diff --git a/services/media/audio/audio_track_to_output_link.cc b/services/media/audio/audio_track_to_output_link.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9d400421fece47f648367d0b2299a316a4520e50 |
--- /dev/null |
+++ b/services/media/audio/audio_track_to_output_link.cc |
@@ -0,0 +1,144 @@ |
+// Copyright 2015 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/logging.h" |
+#include "services/media/audio/audio_track_to_output_link.h" |
+ |
+namespace mojo { |
+namespace media { |
+namespace audio { |
+ |
+AudioTrackToOutputLink::Bookkeeping::~Bookkeeping() {} |
+ |
+AudioTrackToOutputLink::AudioTrackToOutputLink(AudioTrackImplWeakPtr track, |
+ AudioOutputWeakPtr output) |
+ : track_(track), |
+ output_(output), |
+ pending_queue_(new PacketQueue) { |
+#if !(defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)) |
+ flush_lock_held_ = false; |
+#endif |
+} |
+ |
+AudioTrackToOutputLink::~AudioTrackToOutputLink() { |
+ ReleaseQueue(pending_queue_); |
+} |
+ |
+AudioTrackToOutputLinkPtr AudioTrackToOutputLink::New( |
+ AudioTrackImplWeakPtr track, |
+ AudioOutputWeakPtr output) { |
+ return AudioTrackToOutputLinkPtr(new AudioTrackToOutputLink(track, output)); |
+} |
+ |
+void AudioTrackToOutputLink::PushToPendingQueue( |
+ const AudioPipe::AudioPacketRefPtr& pkt) { |
+ base::AutoLock lock(pending_queue_lock_); |
+ pending_queue_->emplace_back(pkt); |
+} |
+ |
+void AudioTrackToOutputLink::FlushPendingQueue() { |
+ // Create a new (empty) queue before obtaining any locks. This will allow us |
+ // to quickly swap the empty queue for the current queue and get out of all |
+ // the locks, and then release the packets at our leisure instead of |
+ // potentially holding off a high priority mixing thread while releasing |
+ // packets. |
+ // |
+ // Note: the safety of this technique depends on Flush only ever being called |
+ // from the AudioTrack, and the AudioTrack's actions being serialized on the |
+ // AudioServer's message loop thread. If multiple flushes are allowed to be |
+ // invoked simultaniously, or if a packet is permitted to be added to the |
+ // queue while a flush operation is in progress, it is possible to return |
+ // packets to the user in an order different than the one that they were |
+ // queued in. |
+ PacketQueuePtr new_queue(new PacketQueue); |
+ |
+ { |
+ base::AutoLock lock(flush_lock_); |
+ { |
+ // TODO(johngro): Assuming that it is impossible to push a new packet |
+ // while a flush is in progress, it's pretty easy to show that this lock |
+ // can never be contended. Because of this, we could consider removing |
+ // this lock operation (although, flush is a relatively rare operation, so |
+ // the extra overhead is pretty insignificant. |
+ base::AutoLock lock(pending_queue_lock_); |
+ pending_queue_.swap(new_queue); |
+ } |
+ flushed_ = true; |
+ } |
+ |
+ ReleaseQueue(new_queue); |
+} |
+ |
+AudioPipe::AudioPacketRefPtr AudioTrackToOutputLink::LockPendingQueueFront( |
+ bool* was_flushed) { |
+#if !(defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)) |
+ // No one had better be holding the flush lock right now. If someone is, then |
+ // we either have multiple threads hitting this operation at the same time |
+ // (should be impossible), or an audio output forgot to unlock the front of |
+ // the queue before attempting to lock it again. |
+ bool was_held = false; |
+ flush_lock_held_.compare_exchange_strong(was_held, true); |
+ DCHECK(!was_held); |
+#endif |
+ flush_lock_.Acquire(); |
+ |
+ DCHECK(was_flushed); |
+ *was_flushed = flushed_; |
+ flushed_ = false; |
+ |
+ { |
+ base::AutoLock lock(pending_queue_lock_); |
+ if (pending_queue_->size()) { |
+ return pending_queue_->front(); |
+ } else { |
+ return nullptr; |
+ } |
+ } |
+} |
+ |
+void AudioTrackToOutputLink::UnlockPendingQueueFront( |
+ AudioPipe::AudioPacketRefPtr* pkt, |
+ bool release_packet) { |
+ { |
+ base::AutoLock lock(pending_queue_lock_); |
+ |
+ // Assert that the user either got no packet when they locked the queue |
+ // (because the queue was empty), or that they got the front of the queue |
+ // and that the front of the queue has not changed. |
+ DCHECK(pkt); |
+ DCHECK((*pkt == nullptr) || |
+ (pending_queue_->size() && (*pkt == pending_queue_->front()))); |
+ |
+ if (*pkt) { |
+ *pkt = nullptr; |
+ if (release_packet) { |
+ pending_queue_->pop_front(); |
+ } |
+ } |
+ } |
+ |
+ flush_lock_.Release(); |
+#if !(defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)) |
+ bool was_held = true; |
+ flush_lock_held_.compare_exchange_strong(was_held, false); |
+ DCHECK(was_held); |
+#endif |
+} |
+ |
+void AudioTrackToOutputLink::ReleaseQueue(const PacketQueuePtr& queue) { |
+ if (!queue) { |
+ return; |
+ } |
+ |
+ for (auto iter = queue->begin(); iter != queue->end(); ++iter) { |
+ (*iter).reset(); |
+ } |
+ |
+ queue->clear(); |
+} |
+ |
+} // namespace audio |
+} // namespace media |
+} // namespace mojo |
+ |