Commit 69055157 authored by Yuri Wiitala's avatar Yuri Wiitala Committed by Commit Bot

Introduce audio::LoopbackStream implementation.

The LoopbackStream is an audio::InputStream that provides the result of
looping-back and mixing-together all current and future audio output
streams belonging to the same "group." An example of this would be for
tab capture, where all output streams from one tab are recorded and then
re-mixed into a single capture stream.

Bug: 824019
Change-Id: I3374182798452bbf9d8688e318dc0546d3630f5e
Reviewed-on: https://chromium-review.googlesource.com/1041661
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: default avatarOlga Sharonova <olka@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#557440}
parent b97a2640
......@@ -43,6 +43,8 @@ source_set("lib") {
"input_stream.h",
"local_muter.cc",
"local_muter.h",
"loopback_stream.cc",
"loopback_stream.h",
"output_controller.cc",
"output_controller.h",
"output_stream.cc",
......@@ -82,6 +84,7 @@ source_set("tests") {
"group_coordinator_unittest.cc",
"input_stream_unittest.cc",
"local_muter_unittest.cc",
"loopback_stream_unittest.cc",
"output_controller_unittest.cc",
"output_stream_unittest.cc",
"snooper_node_unittest.cc",
......
This diff is collapsed.
// Copyright 2018 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 SERVICES_AUDIO_LOOPBACK_STREAM_H_
#define SERVICES_AUDIO_LOOPBACK_STREAM_H_
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
#include "media/audio/audio_input_controller.h"
#include "media/audio/audio_input_sync_writer.h"
#include "media/base/audio_parameters.h"
#include "media/mojo/interfaces/audio_data_pipe.mojom.h"
#include "media/mojo/interfaces/audio_input_stream.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/audio/group_coordinator.h"
#include "services/audio/group_member.h"
#include "services/audio/snooper_node.h"
namespace base {
class TickClock;
} // namespace base
namespace media {
class AudioBus;
} // namespace media
namespace audio {
// An AudioInputStream that provides the result of looping-back and
// mixing-together all current and future audio output streams in the same
// group. The loopback re-mixes the audio, if necessary, so that the resulting
// data stream's format matches the required AudioParameters.
//
// This is organized in three main components: 1) The LoopbackStream itself acts
// as a "shell" that manages mojo bindings and creates/controls the other
// components. 2) One or more SnooperNodes that buffer input data for each
// source OutputStream and format-convert it. 3) A "flow network" that runs via
// a different task runner, to take all the audio collected in the SnooperNodes
// and mix it into a single data stream.
class LoopbackStream : public media::mojom::AudioInputStream,
public GroupCoordinator::Observer {
public:
using CreatedCallback =
base::OnceCallback<void(media::mojom::AudioDataPipePtr)>;
using BindingLostCallback = base::OnceCallback<void(LoopbackStream*)>;
LoopbackStream(CreatedCallback created_callback,
BindingLostCallback binding_lost_callback,
scoped_refptr<base::SequencedTaskRunner> flow_task_runner,
media::mojom::AudioInputStreamRequest request,
media::mojom::AudioInputStreamClientPtr client,
media::mojom::AudioInputStreamObserverPtr observer,
const media::AudioParameters& params,
uint32_t shared_memory_count,
GroupCoordinator* coordinator,
const base::UnguessableToken& group_id);
~LoopbackStream() final;
bool is_recording() const { return network_ && network_->is_started(); }
// media::mojom::AudioInputStream implementation.
void Record() final;
void SetVolume(double volume) final;
// GroupCoordinator::Observer implementation. When a member joins a group, a
// SnooperNode is created for it, and a loopback flow from GroupMember →
// SnooperNode → FlowNetwork is built-up.
void OnMemberJoinedGroup(GroupMember* member) final;
void OnMemberLeftGroup(GroupMember* member) final;
// Overrides for unit testing. These must be called before Record().
void set_clock_for_testing(const base::TickClock* clock) {
network_->set_clock_for_testing(clock);
}
void set_sync_writer_for_testing(
std::unique_ptr<media::AudioInputController::SyncWriter> writer) {
network_->set_writer_for_testing(std::move(writer));
}
// Generally, a volume of 1.0 should be the maximum possible. However, there
// are cases where requests to amplify are made by specifying values higher
// than 1.0.
static constexpr double kMaxVolume = 2.0;
// The amount of time in the past from which to capture the audio. The audio
// recorded from each GroupMember is being generated with a target playout
// time in the near future (usually 1 to 20 ms). To avoid underflow,
// LoopbackStream fetches the audio from a position in the recent past.
static constexpr base::TimeDelta kCaptureDelay =
base::TimeDelta::FromMilliseconds(20);
private:
// Drives all audio flows, re-mixing the audio from multiple SnooperNodes into
// a single audio stream. This class mainly operates on a separate task runner
// from LoopbackStream and can only be destroyed by scheduling it to occur on
// that same task runner.
class FlowNetwork {
public:
FlowNetwork(scoped_refptr<base::SequencedTaskRunner> flow_task_runner,
const media::AudioParameters& output_params,
std::unique_ptr<media::AudioInputSyncWriter> writer);
// These must be called to override the Clock/SyncWriter before Start().
void set_clock_for_testing(const base::TickClock* clock) { clock_ = clock; }
void set_writer_for_testing(
std::unique_ptr<media::AudioInputController::SyncWriter> writer) {
writer_ = std::move(writer);
}
bool is_started() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(control_sequence_);
return !!timer_;
}
const media::AudioParameters& output_params() const {
return output_params_;
}
// Add/Remove an input into this flow network. These may be called at any
// time, before or after Start(). All inputs must be removed before the
// FlowNetwork is scheduled for destruction.
void AddInput(SnooperNode* node);
void RemoveInput(SnooperNode* node);
// This may be called at any time, before or after Start(), to change the
// volume setting.
void SetVolume(double volume);
// Start generating audio data. This must only be called once, and there is
// no "stop" until destruction time.
void Start();
private:
// Since this class guarantees its destructor will be called via the flow
// task runner, and destruction is carried out only by base::DeleteHelper,
// make the destructor is private.
friend class base::DeleteHelper<FlowNetwork>;
~FlowNetwork();
// Called periodically via the audio flow task runner to drive all the audio
// flows from the SnooperNodes, mix them together, and output to the
// AudioDataPipe. Each call schedules the next one until the |run_state_|
// becomes stopped.
void GenerateMoreAudio();
const base::TickClock* clock_;
// Task runner that calls GenerateMoreAudio() to drive all the audio data
// flows.
const scoped_refptr<base::SequencedTaskRunner> flow_task_runner_;
// The audio parameters of the output.
const media::AudioParameters output_params_;
// Destination for the output of this FlowNetwork.
std::unique_ptr<media::AudioInputController::SyncWriter> writer_;
// Ensures thread-safe access to changing the |inputs_| and |volume_| while
// running.
base::Lock lock_;
// The input nodes.
std::vector<SnooperNode*> inputs_; // Guarded by |lock_|.
// Current stream volume. The audio output from this FlowNetwork is scaled
// by this amount during mixing.
double volume_ = 1.0; // Guarded by |lock_|.
// This is set once Start() is called, and lives until this FlowNetwork is
// destroyed. It is used to schedule cancelable tasks run by the
// |flow_task_runner_|.
base::Optional<base::OneShotTimer> timer_;
// These are used to compute when the |timer_| fires and calls
// GenerateMoreAudio(). They ensure that each timer task is scheduled to
// fire with a delay that accounted for how much time was spent processing.
base::TimeTicks first_generate_time_;
int64_t frames_elapsed_ = 0;
base::TimeTicks next_generate_time_;
// Used to transfer the audio from each SnooperNode and mix them into a
// single audio signal. |transfer_bus_| is only allocated when first needed,
// but |mix_bus_| is allocated in the constructor because it is always
// needed.
std::unique_ptr<media::AudioBus> transfer_bus_;
const std::unique_ptr<media::AudioBus> mix_bus_;
SEQUENCE_CHECKER(control_sequence_);
DISALLOW_COPY_AND_ASSIGN(FlowNetwork);
};
// Reports a fatal error to the client, and then runs the BindingLostCallback.
void OnError();
// Run when any of |binding_|, |client_|, or |observer_| are closed. This
// callback is generally used to automatically terminate this LoopbackStream.
BindingLostCallback binding_lost_callback_;
// Mojo bindings. If any of these is closed, the LoopbackStream will call
// OnError(), which will run the |binding_lost_callback_|.
mojo::Binding<media::mojom::AudioInputStream> binding_;
media::mojom::AudioInputStreamClientPtr client_;
media::mojom::AudioInputStreamObserverPtr observer_;
// Used for identifying group members and snooping on their audio data flow.
GroupCoordinator* const coordinator_;
const base::UnguessableToken group_id_;
// The snoopers associated with each group member. This is not a flat_map
// because SnooperNodes cannot move around in memory while in operation.
std::map<GroupMember*, SnooperNode> snoopers_;
// The flow network that generates the single loopback result stream. It is
// owned by LoopbackStream, but it's destruction must be carried out by the
// flow task runner. This is never null, unless the system cannot support
// loopback (see constructor definition comments).
std::unique_ptr<FlowNetwork, base::OnTaskRunnerDeleter> network_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<LoopbackStream> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(LoopbackStream);
};
} // namespace audio
#endif // SERVICES_AUDIO_LOOPBACK_STREAM_H_
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment