blob: 40628d440b2df84f553589eda9e1c2ae38efe15b [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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 COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_
#define COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_
#include <algorithm>
#include <vector>
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/media_stream/audio_parameters.h"
#include "cobalt/media/base/shell_audio_bus.h"
namespace cobalt {
namespace media_stream {
// 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_stream::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_CALLED_ON_VALID_THREAD(thread_checker_);
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_CALLED_ON_VALID_THREAD(thread_checker_);
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_CALLED_ON_VALID_THREAD(thread_checker_);
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_stream::AudioParameters& params) {
DCHECK(params.IsValid());
base::AutoLock auto_lock(consumers_lock_);
{
base::AutoLock auto_params_lock(params_lock_);
if (params_ == 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::ShellAudioBus& audio_bus,
base::TimeTicks reference_time) {
TRACE_EVENT1("media_stream", "MediaStreamAudioDeliverer::OnData",
"reference time (ms)",
(reference_time - base::TimeTicks()).InMillisecondsF());
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_stream::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.
THREAD_CHECKER(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_stream::AudioParameters params_;
DISALLOW_COPY_AND_ASSIGN(MediaStreamAudioDeliverer);
};
} // namespace media_stream
} // namespace cobalt
#endif // COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_DELIVERER_H_