| // 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 |