blob: ef7d2557b697c5f371120cf4d0be6f2de7ab0f36 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/mojo/clients/mojo_audio_encoder.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "media/base/audio_buffer.h"
#include "media/mojo/common/media_type_converters.h"
namespace media {
MojoAudioEncoder::MojoAudioEncoder(
mojo::PendingRemote<mojom::AudioEncoder> remote_encoder)
: pending_remote_encoder_(std::move(remote_encoder)),
buffer_pool_(new AudioBufferMemoryPool()),
runner_(base::SequencedTaskRunner::GetCurrentDefault()) {
weak_this_ = weak_factory_.GetWeakPtr();
}
MojoAudioEncoder::~MojoAudioEncoder() = default;
void MojoAudioEncoder::Initialize(const Options& options,
OutputCB output_cb,
EncoderStatusCB done_cb) {
DCHECK(output_cb);
DCHECK(done_cb);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (remote_encoder_.is_bound() || client_receiver_.is_bound()) {
PostStatusCallback(std::move(done_cb),
EncoderStatus::Codes::kEncoderInitializeTwice);
return;
}
BindRemote();
if (!remote_encoder_.is_bound() || !remote_encoder_.is_connected()) {
PostStatusCallback(std::move(done_cb),
EncoderStatus::Codes::kEncoderInitializationError);
return;
}
output_cb_ = std::move(output_cb);
options_ = options;
remote_encoder_->Initialize(client_receiver_.BindNewEndpointAndPassRemote(),
options,
WrapCallbackAsPending(std::move(done_cb)));
}
void MojoAudioEncoder::Encode(std::unique_ptr<AudioBus> audio_bus,
base::TimeTicks capture_time,
EncoderStatusCB done_cb) {
DCHECK(audio_bus);
DCHECK(done_cb);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!remote_encoder_.is_bound() || !remote_encoder_.is_connected()) {
PostStatusCallback(std::move(done_cb),
EncoderStatus::Codes::kEncoderFailedEncode);
return;
}
auto buffer = AudioBuffer::CopyFrom(options_.sample_rate,
capture_time - base::TimeTicks(),
audio_bus.get(), buffer_pool_);
remote_encoder_->Encode(mojom::AudioBuffer::From(*buffer),
WrapCallbackAsPending(std::move(done_cb)));
}
void MojoAudioEncoder::Flush(EncoderStatusCB done_cb) {
DCHECK(done_cb);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!remote_encoder_.is_bound() || !remote_encoder_.is_connected()) {
PostStatusCallback(std::move(done_cb),
EncoderStatus::Codes::kEncoderFailedFlush);
return;
}
remote_encoder_->Flush(WrapCallbackAsPending(std::move(done_cb)));
}
void MojoAudioEncoder::OnEncodedBufferReady(media::EncodedAudioBuffer buffer,
const CodecDescription& desc) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
absl::optional<CodecDescription> opt_desc;
if (desc.size() > 0)
opt_desc = desc;
output_cb_.Run(std::move(buffer), std::move(opt_desc));
}
void MojoAudioEncoder::CallAndReleaseCallback(PendingCallbackHandle handle,
const EncoderStatus& status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!pending_callbacks_.empty()) {
EncoderStatusCB callback = std::move(*handle);
pending_callbacks_.erase(handle);
std::move(callback).Run(status);
}
}
MojoAudioEncoder::WrappedEncoderStatusCB
MojoAudioEncoder::WrapCallbackAsPending(EncoderStatusCB callback) {
PendingCallbackHandle handle =
pending_callbacks_.insert(pending_callbacks_.end(), std::move(callback));
return base::BindOnce(&MojoAudioEncoder::CallAndReleaseCallback, weak_this_,
handle);
}
void MojoAudioEncoder::CallAndReleaseAllPendingCallbacks(EncoderStatus status) {
for (auto& callback : pending_callbacks_)
PostStatusCallback(std::move(callback), status);
pending_callbacks_.clear();
}
void MojoAudioEncoder::BindRemote() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
remote_encoder_.Bind(std::move(pending_remote_encoder_));
remote_encoder_.set_disconnect_handler(
base::BindOnce(&MojoAudioEncoder::OnConnectionError, weak_this_));
}
void MojoAudioEncoder::OnConnectionError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!remote_encoder_.is_connected());
CallAndReleaseAllPendingCallbacks(
EncoderStatus::Codes::kEncoderMojoConnectionError);
weak_factory_.InvalidateWeakPtrs();
remote_encoder_.reset();
}
void MojoAudioEncoder::PostStatusCallback(EncoderStatusCB callback,
EncoderStatus status) {
runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), std::move(status)));
}
} // namespace media