cef/libcef/browser/audio_push_sink.cc

173 lines
6.0 KiB
C++

// Copyright (c) 2018 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_push_sink.h"
#include "libcef/browser/thread_util.h"
#include "base/bind.h"
#include "base/logging.h"
#include "media/base/audio_sample_types.h"
#include "media/base/data_buffer.h"
using namespace media;
namespace {
cef_channel_layout_t TranslateChannelLayout(ChannelLayout channel) {
switch (channel) {
case CHANNEL_LAYOUT_UNSUPPORTED:
return CEF_CHANNEL_LAYOUT_UNSUPPORTED;
case CHANNEL_LAYOUT_MONO:
return CEF_CHANNEL_LAYOUT_MONO;
case CHANNEL_LAYOUT_STEREO:
return CEF_CHANNEL_LAYOUT_STEREO;
case CHANNEL_LAYOUT_2_1:
return CEF_CHANNEL_LAYOUT_2_1;
case CHANNEL_LAYOUT_SURROUND:
return CEF_CHANNEL_LAYOUT_SURROUND;
case CHANNEL_LAYOUT_4_0:
return CEF_CHANNEL_LAYOUT_4_0;
case CHANNEL_LAYOUT_2_2:
return CEF_CHANNEL_LAYOUT_2_2;
case CHANNEL_LAYOUT_QUAD:
return CEF_CHANNEL_LAYOUT_QUAD;
case CHANNEL_LAYOUT_5_0:
return CEF_CHANNEL_LAYOUT_5_0;
case CHANNEL_LAYOUT_5_1:
return CEF_CHANNEL_LAYOUT_5_1;
case CHANNEL_LAYOUT_5_0_BACK:
return CEF_CHANNEL_LAYOUT_5_0_BACK;
case CHANNEL_LAYOUT_5_1_BACK:
return CEF_CHANNEL_LAYOUT_5_1_BACK;
case CHANNEL_LAYOUT_7_0:
return CEF_CHANNEL_LAYOUT_7_0;
case CHANNEL_LAYOUT_7_1:
return CEF_CHANNEL_LAYOUT_7_1;
case CHANNEL_LAYOUT_7_1_WIDE:
return CEF_CHANNEL_LAYOUT_7_1_WIDE;
case CHANNEL_LAYOUT_STEREO_DOWNMIX:
return CEF_CHANNEL_LAYOUT_STEREO_DOWNMIX;
case CHANNEL_LAYOUT_2POINT1:
return CEF_CHANNEL_LAYOUT_2POINT1;
case CHANNEL_LAYOUT_3_1:
return CEF_CHANNEL_LAYOUT_3_1;
case CHANNEL_LAYOUT_4_1:
return CEF_CHANNEL_LAYOUT_4_1;
case CHANNEL_LAYOUT_6_0:
return CEF_CHANNEL_LAYOUT_6_0;
case CHANNEL_LAYOUT_6_0_FRONT:
return CEF_CHANNEL_LAYOUT_6_0_FRONT;
case CHANNEL_LAYOUT_HEXAGONAL:
return CEF_CHANNEL_LAYOUT_HEXAGONAL;
case CHANNEL_LAYOUT_6_1:
return CEF_CHANNEL_LAYOUT_6_1;
case CHANNEL_LAYOUT_6_1_BACK:
return CEF_CHANNEL_LAYOUT_6_1_BACK;
case CHANNEL_LAYOUT_6_1_FRONT:
return CEF_CHANNEL_LAYOUT_6_1_FRONT;
case CHANNEL_LAYOUT_7_0_FRONT:
return CEF_CHANNEL_LAYOUT_7_0_FRONT;
case CHANNEL_LAYOUT_7_1_WIDE_BACK:
return CEF_CHANNEL_LAYOUT_7_1_WIDE_BACK;
case CHANNEL_LAYOUT_OCTAGONAL:
return CEF_CHANNEL_LAYOUT_OCTAGONAL;
case CHANNEL_LAYOUT_DISCRETE:
return CEF_CHANNEL_LAYOUT_DISCRETE;
case CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC:
return CEF_CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
case CHANNEL_LAYOUT_4_1_QUAD_SIDE:
return CEF_CHANNEL_LAYOUT_4_1_QUAD_SIDE;
case CHANNEL_LAYOUT_BITSTREAM:
return CEF_CHANNEL_LAYOUT_BITSTREAM;
case CHANNEL_LAYOUT_NONE:
return CEF_CHANNEL_LAYOUT_NONE;
}
return CEF_CHANNEL_LAYOUT_NONE;
}
} // namespace
int CefAudioPushSink::audio_stream_id = 0;
CefAudioPushSink::CefAudioPushSink(const AudioParameters& params,
CefRefPtr<CefBrowserHostImpl> browser,
CefRefPtr<CefAudioHandler> cef_audio_handler,
const CloseCallback& callback)
: params_(params),
browser_(browser),
cef_audio_handler_(cef_audio_handler),
close_callback_(callback),
stop_stream_(false),
audio_stream_id_(++audio_stream_id) {
// Verify that our enum matches Chromium's values.
static_assert(
static_cast<int>(CEF_CHANNEL_LAYOUT_MAX) ==
static_cast<int>(CHANNEL_LAYOUT_MAX),
"cef_channel_layout_t must match the ChannelLayout enum in Chromium");
DCHECK(params_.IsValid());
DCHECK(browser);
DCHECK(cef_audio_handler);
// VAOS can be constructed on any thread, but will DCHECK that all
// AudioOutputStream methods are called from the same thread.
thread_checker_.DetachFromThread();
base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&CefAudioPushSink::InitOnUIThread,
base::Unretained(this)));
}
CefAudioPushSink::~CefAudioPushSink() = default;
void CefAudioPushSink::InitOnUIThread() {
DCHECK(thread_checker_.CalledOnValidThread());
cef_audio_handler_->OnAudioStreamStarted(
browser_.get(), audio_stream_id_, params_.channels(),
TranslateChannelLayout(params_.channel_layout()), params_.sample_rate(),
params_.frames_per_buffer());
}
void CefAudioPushSink::OnData(std::unique_ptr<media::AudioBus> source,
base::TimeTicks reference_time) {
// early exit if stream already stopped
if (stop_stream_)
return;
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefAudioPushSink::OnData,
base::Unretained(this),
std::move(source), reference_time));
return;
}
const int channels = source->channels();
std::vector<const float*> data(channels);
for (int c = 0; c < channels; ++c) {
data[c] = source->channel(c);
}
// Add the packet to the buffer.
base::TimeDelta pts = reference_time - base::TimeTicks::UnixEpoch();
cef_audio_handler_->OnAudioStreamPacket(browser_.get(), audio_stream_id_,
data.data(), source->frames(),
pts.InMilliseconds());
}
void CefAudioPushSink::Close() {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefAudioPushSink::Close,
base::Unretained(this)));
return;
}
if (!stop_stream_) {
stop_stream_ = true;
cef_audio_handler_->OnAudioStreamStopped(browser_.get(), audio_stream_id_);
const CloseCallback& cb = base::ResetAndReturn(&close_callback_);
if (!cb.is_null())
cb.Run(this);
}
}