blob: 3eed7a75a2fae2a3a237d6e3a9e91a7966d9f1ad [file] [log] [blame]
// Copyright (c) 2019 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2011 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 "libcef/browser/audio_capturer.h"
#include "libcef/browser/browser_host_impl.h"
#include "components/mirroring/service/captured_audio_input.h"
#include "content/public/browser/audio_loopback_stream_creator.h"
#include "media/audio/audio_input_device.h"
namespace {
media::ChannelLayout TranslateChannelLayout(
cef_channel_layout_t channel_layout) {
// Verify that our enum matches Chromium's values. The enum values match
// between those enums and existing values don't ever change, so it's enough
// to check that there are no new ones added.
static_assert(
static_cast<int>(CEF_CHANNEL_LAYOUT_MAX) ==
static_cast<int>(media::CHANNEL_LAYOUT_MAX),
"cef_channel_layout_t must match the ChannelLayout enum in Chromium");
return static_cast<media::ChannelLayout>(channel_layout);
}
void StreamCreatorHelper(
content::WebContents* source_web_contents,
content::AudioLoopbackStreamCreator* audio_stream_creator,
mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient> client,
const media::AudioParameters& params,
uint32_t total_segments) {
audio_stream_creator->CreateLoopbackStream(
source_web_contents, params, total_segments,
base::BindRepeating(
[](mojo::PendingRemote<mirroring::mojom::AudioStreamCreatorClient>
client,
mojo::PendingRemote<media::mojom::AudioInputStream> stream,
mojo::PendingReceiver<media::mojom::AudioInputStreamClient>
client_receiver,
media::mojom::ReadOnlyAudioDataPipePtr data_pipe) {
mojo::Remote<mirroring::mojom::AudioStreamCreatorClient>
audio_client(std::move(client));
audio_client->StreamCreated(
std::move(stream), std::move(client_receiver),
std::move(data_pipe), false /* initially_muted */);
},
base::Passed(&client)));
}
} // namespace
CefAudioCapturer::CefAudioCapturer(const CefAudioParameters& params,
CefRefPtr<CefBrowserHostImpl> browser,
CefRefPtr<CefAudioHandler> audio_handler)
: params_(params),
browser_(browser),
audio_handler_(audio_handler),
audio_stream_creator_(content::AudioLoopbackStreamCreator::
CreateInProcessAudioLoopbackStreamCreator()) {
media::AudioParameters audio_params(
media::AudioParameters::AUDIO_PCM_LINEAR,
TranslateChannelLayout(params.channel_layout), params.sample_rate,
params.frames_per_buffer);
if (!audio_params.IsValid()) {
LOG(ERROR) << "Invalid audio parameters";
return;
}
DCHECK(browser_);
DCHECK(audio_handler_);
DCHECK(browser_->web_contents());
channels_ = audio_params.channels();
audio_input_device_ = new media::AudioInputDevice(
std::make_unique<mirroring::CapturedAudioInput>(base::BindRepeating(
&StreamCreatorHelper, base::Unretained(browser_->web_contents()),
base::Unretained(audio_stream_creator_.get()))),
media::AudioInputDevice::kLoopback);
audio_input_device_->Initialize(audio_params, this);
audio_input_device_->Start();
}
CefAudioCapturer::~CefAudioCapturer() {
StopStream();
}
void CefAudioCapturer::OnCaptureStarted() {
audio_handler_->OnAudioStreamStarted(browser_, params_, channels_);
DCHECK(!capturing_);
capturing_ = true;
}
void CefAudioCapturer::Capture(const media::AudioBus* source,
base::TimeTicks audio_capture_time,
double /*volume*/,
bool /*key_pressed*/) {
const int channels = source->channels();
std::array<const float*, media::CHANNELS_MAX> data;
DCHECK(channels == channels_);
DCHECK(channels <= static_cast<int>(data.size()));
for (int c = 0; c < channels; ++c) {
data[c] = source->channel(c);
}
base::TimeDelta pts = audio_capture_time - base::TimeTicks::UnixEpoch();
audio_handler_->OnAudioStreamPacket(browser_, data.data(), source->frames(),
pts.InMilliseconds());
}
void CefAudioCapturer::OnCaptureError(const std::string& message) {
audio_handler_->OnAudioStreamError(browser_, message);
StopStream();
}
void CefAudioCapturer::StopStream() {
if (audio_input_device_)
audio_input_device_->Stop();
if (capturing_)
audio_handler_->OnAudioStreamStopped(browser_);
audio_input_device_ = nullptr;
capturing_ = false;
}