Chromium Code Reviews| Index: content/renderer/media/media_stream_audio_deliverer.h |
| diff --git a/content/renderer/media/media_stream_audio_deliverer.h b/content/renderer/media/media_stream_audio_deliverer.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5991a5f168533874ff2a0283aa4073a893f27dea |
| --- /dev/null |
| +++ b/content/renderer/media/media_stream_audio_deliverer.h |
| @@ -0,0 +1,156 @@ |
| +// 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. |
| + |
| +#ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_DELIVERER_H_ |
| +#define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_DELIVERER_H_ |
| + |
| +#include <algorithm> |
| +#include <vector> |
| + |
| +#include "base/synchronization/lock.h" |
| +#include "base/threading/thread_checker.h" |
| +#include "media/audio/audio_parameters.h" |
| + |
| +namespace content { |
| + |
| +// Template containing functionality common to both MediaStreamAudioSource and |
| +// MediaStreamAudioTrack. This is used for managing the connections between |
| +// objects through which audio data flows, and doing so in a thread-safe manner. |
| +// |
| +// The Consumer parameter of the template is the type of the objects to which |
| +// audio data is delivered: MediaStreamAudioTrack or MediaStreamAudioSink. It's |
| +// assumed the Consumer class defines methods named OnSetFormat() and OnData() |
| +// that have the same signature as the ones defined in this template. |
| +// MediaStreamAudioDeliverer will always guarantee the Consumer's OnSetFormat() |
| +// and OnData() methods are called sequentially. |
| +template <typename Consumer> |
| +class MediaStreamAudioDeliverer { |
| + public: |
| + MediaStreamAudioDeliverer() {} |
| + ~MediaStreamAudioDeliverer() {} |
| + |
| + // Returns the current audio parameters. These will be invalid before the |
| + // first call to OnSetFormat(). This method is thread-safe. |
| + media::AudioParameters GetAudioParameters() const { |
| + base::AutoLock auto_lock(params_lock_); |
| + return params_; |
| + } |
| + |
| + // Begin delivering audio to |consumer|. The caller must guarantee |consumer| |
| + // is not destroyed until after calling RemoveConsumer(consumer). This method |
| + // must be called on the main thread. |
| + void AddConsumer(Consumer* consumer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(consumer); |
| + base::AutoLock auto_lock(consumers_lock_); |
| + DCHECK(std::find(consumers_.begin(), consumers_.end(), consumer) == |
| + consumers_.end()); |
| + DCHECK(std::find(pending_consumers_.begin(), pending_consumers_.end(), |
| + consumer) == pending_consumers_.end()); |
| + pending_consumers_.push_back(consumer); |
| + } |
| + |
| + // Stop delivering audio to |consumer|. Returns true if |consumer| was the |
| + // last consumer removed, false otherwise. When this method returns, no |
| + // further calls will be made to OnSetFormat() or OnData() on any thread. |
| + // This method must be called on the main thread. |
| + bool RemoveConsumer(Consumer* consumer) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + base::AutoLock auto_lock(consumers_lock_); |
| + const bool had_consumers = |
| + !consumers_.empty() || !pending_consumers_.empty(); |
| + auto it = std::find(consumers_.begin(), consumers_.end(), consumer); |
| + if (it != consumers_.end()) { |
| + consumers_.erase(it); |
| + } else { |
| + it = std::find(pending_consumers_.begin(), pending_consumers_.end(), |
| + consumer); |
| + if (it != pending_consumers_.end()) |
| + pending_consumers_.erase(it); |
| + } |
| + return had_consumers && consumers_.empty() && pending_consumers_.empty(); |
| + } |
| + |
| + // Returns the current list of connected Consumers. This is normally used to |
| + // send a notification to all consumers. This method must be called on the |
| + // main thread. |
| + void GetConsumerList(std::vector<Consumer*>* consumer_list) const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + base::AutoLock auto_lock(consumers_lock_); |
| + *consumer_list = consumers_; |
| + consumer_list->insert(consumer_list->end(), |
| + pending_consumers_.begin(), pending_consumers_.end()); |
| + } |
| + |
| + // Change the format of the audio passed in the next call to OnData(). This |
| + // method may be called on any thread but, logically, should only be called |
| + // between calls to OnData(). |
| + void OnSetFormat(const media::AudioParameters& params) { |
| + DCHECK(params.IsValid()); |
| + base::AutoLock auto_lock(consumers_lock_); |
| + { |
|
o1ka
2016/04/21 18:51:22
nit: it would probably be more consistent to have
miu
2016/04/21 20:42:30
This method *is* the setter. ;) But, I see that
|
| + base::AutoLock auto_params_lock(params_lock_); |
| + if (params_.Equals(params)) |
| + return; |
| + params_ = params; |
| + } |
| + pending_consumers_.insert(pending_consumers_.end(), |
| + consumers_.begin(), consumers_.end()); |
| + consumers_.clear(); |
| + } |
| + |
| + // Deliver data to all consumers. This method may be called on any thread. |
| + void OnData(const media::AudioBus& audio_bus, |
| + base::TimeTicks reference_time) { |
| + base::AutoLock auto_lock(consumers_lock_); |
| + |
| + // Call OnSetFormat() for all pending consumers and move them to the |
| + // active-delivery list. |
| + if (!pending_consumers_.empty()) { |
| + const media::AudioParameters params = GetAudioParameters(); |
| + DCHECK(params.IsValid()); |
| + for (Consumer* consumer : pending_consumers_) |
| + consumer->OnSetFormat(params); |
| + consumers_.insert(consumers_.end(), |
| + pending_consumers_.begin(), pending_consumers_.end()); |
| + pending_consumers_.clear(); |
| + } |
| + |
| + // Deliver the audio data to each consumer. |
| + for (Consumer* consumer : consumers_) |
| + consumer->OnData(audio_bus, reference_time); |
| + } |
| + |
| + private: |
| + // In debug builds, check that all methods that could cause object graph or |
| + // data flow changes are being called on the main thread. |
| + base::ThreadChecker thread_checker_; |
| + |
| + // Protects concurrent access to |pending_consumers_| and |consumers_|. |
| + mutable base::Lock consumers_lock_; |
| + |
| + // Any consumers needing a call to OnSetFormat(), to be notified of the |
| + // changed audio format, are placed in this list. This includes consumers |
| + // added via AddConsumer() that need to have an initial OnSetFormat() call |
| + // before audio data is first delivered. Consumers are moved from this list to |
| + // |consumers_| on the audio thread. |
| + std::vector<Consumer*> pending_consumers_; |
| + |
| + // Consumers that are up-to-date on the current audio format and are receiving |
| + // audio data are placed in this list. |
| + std::vector<Consumer*> consumers_; |
| + |
| + // Protects concurrent access to |params_|. |
| + mutable base::Lock params_lock_; |
| + |
| + // Specifies the current format of the audio passing through this |
| + // MediaStreamAudioDeliverer. |
| + media::AudioParameters params_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MediaStreamAudioDeliverer); |
| +}; |
| + |
| +} // namespace content |
| + |
| +#endif // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_DELIVERER_H_ |