blob: 7d7cf6948418d17272350a90bfa9174ce60d92af [file] [log] [blame]
// Copyright 2019 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/fuchsia/common/stream_processor_helper.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/functional/bind.h"
#include "media/base/timestamp_constants.h"
namespace media {
namespace {
// Input buffers are allocate once per decoder, the |buffer_lifetime_ordinal| is
// always the same.
constexpr uint64_t kInputBufferLifetimeOrdinal = 1;
} // namespace
StreamProcessorHelper::IoPacket::IoPacket(size_t index,
size_t offset,
size_t size,
base::TimeDelta timestamp,
bool unit_end,
bool key_frame,
base::OnceClosure destroy_cb)
: index_(index),
offset_(offset),
size_(size),
timestamp_(timestamp),
unit_end_(unit_end),
key_frame_(key_frame) {
destroy_callbacks_.push_front(std::move(destroy_cb));
}
StreamProcessorHelper::IoPacket::~IoPacket() {
for (auto& cb : destroy_callbacks_) {
std::move(cb).Run();
}
}
StreamProcessorHelper::IoPacket::IoPacket(IoPacket&&) = default;
StreamProcessorHelper::IoPacket& StreamProcessorHelper::IoPacket::operator=(
IoPacket&&) = default;
void StreamProcessorHelper::IoPacket::AddOnDestroyClosure(
base::OnceClosure closure) {
destroy_callbacks_.push_front(std::move(closure));
}
StreamProcessorHelper::StreamProcessorHelper(
fuchsia::media::StreamProcessorPtr processor,
Client* client)
: processor_(std::move(processor)), client_(client), weak_factory_(this) {
DCHECK(processor_);
DCHECK(client_);
weak_this_ = weak_factory_.GetWeakPtr();
processor_.set_error_handler(
[this](zx_status_t status) {
ZX_LOG(ERROR, status)
<< "The fuchsia.media.StreamProcessor channel was terminated.";
OnError();
});
processor_.events().OnStreamFailed =
fit::bind_member(this, &StreamProcessorHelper::OnStreamFailed);
processor_.events().OnFreeInputPacket =
fit::bind_member(this, &StreamProcessorHelper::OnFreeInputPacket);
processor_.events().OnInputConstraints =
fit::bind_member(this, &StreamProcessorHelper::OnInputConstraints);
processor_.events().OnOutputConstraints =
fit::bind_member(this, &StreamProcessorHelper::OnOutputConstraints);
processor_.events().OnOutputFormat =
fit::bind_member(this, &StreamProcessorHelper::OnOutputFormat);
processor_.events().OnOutputPacket =
fit::bind_member(this, &StreamProcessorHelper::OnOutputPacket);
processor_.events().OnOutputEndOfStream =
fit::bind_member(this, &StreamProcessorHelper::OnOutputEndOfStream);
processor_->EnableOnStreamFailed();
}
StreamProcessorHelper::~StreamProcessorHelper() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void StreamProcessorHelper::Process(IoPacket input) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(processor_);
fuchsia::media::Packet packet;
packet.mutable_header()->set_buffer_lifetime_ordinal(
kInputBufferLifetimeOrdinal);
packet.mutable_header()->set_packet_index(input.buffer_index());
packet.set_buffer_index(packet.header().packet_index());
packet.set_timestamp_ish(input.timestamp().InNanoseconds());
packet.set_stream_lifetime_ordinal(stream_lifetime_ordinal_);
packet.set_start_offset(input.offset());
packet.set_valid_length_bytes(input.size());
packet.set_known_end_access_unit(input.unit_end());
active_stream_ = true;
if (!input.format().IsEmpty()) {
processor_->QueueInputFormatDetails(stream_lifetime_ordinal_,
fidl::Clone(input.format()));
}
DCHECK(!input_packets_.contains(input.buffer_index()));
input_packets_.insert_or_assign(input.buffer_index(), std::move(input));
processor_->QueueInputPacket(std::move(packet));
}
void StreamProcessorHelper::ProcessEos() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(processor_);
active_stream_ = true;
processor_->QueueInputEndOfStream(stream_lifetime_ordinal_);
processor_->FlushEndOfStreamAndCloseStream(stream_lifetime_ordinal_);
}
void StreamProcessorHelper::Reset() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!active_stream_) {
// Nothing to do if we don't have an active stream.
return;
}
if (processor_) {
processor_->CloseCurrentStream(stream_lifetime_ordinal_,
/*release_input_buffers=*/false,
/*release_output_buffers=*/false);
}
stream_lifetime_ordinal_ += 2;
active_stream_ = false;
}
void StreamProcessorHelper::OnStreamFailed(uint64_t stream_lifetime_ordinal,
fuchsia::media::StreamError error) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (stream_lifetime_ordinal_ != stream_lifetime_ordinal) {
return;
}
if (error == fuchsia::media::StreamError::DECRYPTOR_NO_KEY) {
// Always reset the stream since the current one has failed.
Reset();
client_->OnStreamProcessorNoKey();
return;
}
OnError();
}
void StreamProcessorHelper::OnFreeInputPacket(
fuchsia::media::PacketHeader free_input_packet) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!free_input_packet.has_packet_index()) {
DLOG(ERROR) << "Received OnFreeInputPacket() with missing required fields.";
OnError();
return;
}
auto it = input_packets_.find(free_input_packet.packet_index());
if (it == input_packets_.end()) {
DLOG(ERROR) << "Received OnFreeInputPacket() with invalid packet index.";
OnError();
return;
}
// The packet should be destroyed only after it's removed from
// |input_packets_|. Otherwise packet destruction observer may call Process()
// for the next packet while the current packet is still in |input_packets_|.
auto packet = std::move(it->second);
input_packets_.erase(it);
}
void StreamProcessorHelper::OnInputConstraints(
fuchsia::media::StreamBufferConstraints input_constraints) {
if (!input_constraints.has_buffer_constraints_version_ordinal()) {
DLOG(ERROR)
<< "Received OnInputConstraints() with missing required fields.";
OnError();
return;
}
if (input_constraints.buffer_constraints_version_ordinal() !=
kInputBufferLifetimeOrdinal) {
DLOG(ERROR) << "Received OnInputConstraints() unexpected version ordinal: "
<< input_constraints.buffer_constraints_version_ordinal();
OnError();
return;
}
client_->OnStreamProcessorAllocateInputBuffers(input_constraints);
}
void StreamProcessorHelper::OnOutputConstraints(
fuchsia::media::StreamOutputConstraints output_constraints) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!output_constraints.has_stream_lifetime_ordinal()) {
DLOG(ERROR)
<< "Received OnOutputConstraints() with missing required fields.";
OnError();
return;
}
if (output_constraints.stream_lifetime_ordinal() !=
stream_lifetime_ordinal_) {
return;
}
if (!output_constraints.has_buffer_constraints_action_required() ||
!output_constraints.buffer_constraints_action_required()) {
return;
}
if (!output_constraints.has_buffer_constraints()) {
DLOG(ERROR) << "Received OnOutputConstraints() which requires buffer "
"constraints action, but without buffer constraints.";
OnError();
return;
}
// StreamProcessor API expects odd buffer lifetime ordinal, which is
// incremented by 2 for each buffer generation.
output_buffer_lifetime_ordinal_ += 2;
output_buffer_constraints_ =
std::move(*output_constraints.mutable_buffer_constraints());
client_->OnStreamProcessorAllocateOutputBuffers(output_buffer_constraints_);
}
void StreamProcessorHelper::OnOutputFormat(
fuchsia::media::StreamOutputFormat output_format) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!output_format.has_stream_lifetime_ordinal() ||
!output_format.has_format_details()) {
DLOG(ERROR) << "Received OnOutputFormat() with missing required fields.";
OnError();
return;
}
if (output_format.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
return;
}
client_->OnStreamProcessorOutputFormat(std::move(output_format));
}
void StreamProcessorHelper::OnOutputPacket(fuchsia::media::Packet output_packet,
bool error_detected_before,
bool error_detected_during) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!output_packet.has_header() ||
!output_packet.header().has_buffer_lifetime_ordinal() ||
!output_packet.header().has_packet_index() ||
!output_packet.has_stream_lifetime_ordinal() ||
!output_packet.has_buffer_index()) {
DLOG(ERROR) << "Received OnOutputPacket() with missing required fields.";
OnError();
return;
}
if (output_packet.header().buffer_lifetime_ordinal() !=
output_buffer_lifetime_ordinal_) {
return;
}
if (output_packet.stream_lifetime_ordinal() != stream_lifetime_ordinal_) {
// Output packets from old streams still need to be recycled.
OnRecycleOutputBuffer(output_buffer_lifetime_ordinal_,
output_packet.header().packet_index());
return;
}
auto buffer_index = output_packet.buffer_index();
auto packet_index = output_packet.header().packet_index();
base::TimeDelta timestamp =
output_packet.has_timestamp_ish()
? base::Nanoseconds(output_packet.timestamp_ish())
: kNoTimestamp;
client_->OnStreamProcessorOutputPacket(IoPacket(
buffer_index, output_packet.start_offset(),
output_packet.valid_length_bytes(), timestamp,
output_packet.known_end_access_unit(),
output_packet.has_key_frame() ? output_packet.key_frame() : false,
base::BindOnce(&StreamProcessorHelper::OnRecycleOutputBuffer, weak_this_,
output_buffer_lifetime_ordinal_, packet_index)));
}
void StreamProcessorHelper::OnOutputEndOfStream(
uint64_t stream_lifetime_ordinal,
bool error_detected_before) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (stream_lifetime_ordinal != stream_lifetime_ordinal_) {
return;
}
stream_lifetime_ordinal_ += 2;
active_stream_ = false;
client_->OnStreamProcessorEndOfStream();
}
void StreamProcessorHelper::OnError() {
processor_.Unbind();
client_->OnStreamProcessorError();
}
void StreamProcessorHelper::SetInputBufferCollectionToken(
fuchsia::sysmem::BufferCollectionTokenPtr sysmem_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
fuchsia::media::StreamBufferPartialSettings settings;
settings.set_buffer_lifetime_ordinal(kInputBufferLifetimeOrdinal);
settings.set_buffer_constraints_version_ordinal(0);
settings.set_sysmem_token(std::move(sysmem_token));
processor_->SetInputBufferPartialSettings(std::move(settings));
}
void StreamProcessorHelper::CompleteOutputBuffersAllocation(
fuchsia::sysmem::BufferCollectionTokenPtr collection_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!output_buffer_constraints_.IsEmpty());
// Pass new output buffer settings to the stream processor.
fuchsia::media::StreamBufferPartialSettings settings;
settings.set_buffer_lifetime_ordinal(output_buffer_lifetime_ordinal_);
settings.set_buffer_constraints_version_ordinal(
output_buffer_constraints_.buffer_constraints_version_ordinal());
settings.set_sysmem_token(std::move(collection_token));
processor_->SetOutputBufferPartialSettings(std::move(settings));
processor_->CompleteOutputBufferPartialSettings(
output_buffer_lifetime_ordinal_);
}
void StreamProcessorHelper::OnRecycleOutputBuffer(
uint64_t buffer_lifetime_ordinal,
uint32_t packet_index) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!processor_)
return;
if (buffer_lifetime_ordinal != output_buffer_lifetime_ordinal_)
return;
fuchsia::media::PacketHeader header;
header.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
header.set_packet_index(packet_index);
processor_->RecycleOutputPacket(std::move(header));
}
} // namespace media