blob: ec3c56a2d88593a027317c7ee3867e5a556cb260 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "frame_encoder.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/component/cpp/startup_context.h>
#include <src/lib/fxl/logging.h>
#include <thread>
namespace {
// This code only has one stream_lifetime_ordinal which is 1.
constexpr uint64_t kStreamLifetimeOrdinal = 1;
std::vector<FrameEncoder::EncodedFrame> take_encoded_frames_from_codec(
CodecClient* client, bool expect_access_units) {
std::vector<FrameEncoder::EncodedFrame> frames;
std::unique_ptr<CodecOutput> output;
FXL_VLOG(6) << "Waiting on output packets...";
while ((output = client->BlockingGetEmittedOutput()) &&
!output->end_of_stream()) {
const auto& packet = output->packet();
const auto& buffer = client->GetOutputBufferByIndex(packet.buffer_index());
if (expect_access_units) {
FXL_CHECK(packet.start_access_unit());
FXL_CHECK(packet.known_end_access_unit());
}
FXL_VLOG(8) << "Got output packet with length: "
<< packet.valid_length_bytes();
std::vector<uint8_t> payload;
payload.resize(packet.valid_length_bytes());
memcpy(payload.data(), buffer.base() + packet.start_offset(),
packet.valid_length_bytes());
std::optional<uint64_t> timestamp_ish;
if (packet.has_timestamp_ish()) {
FXL_VLOG(8) << "Output packet has timestamp: " << packet.timestamp_ish();
timestamp_ish = packet.timestamp_ish();
}
frames.push_back({
.data = std::move(payload),
.timestamp_ish = timestamp_ish,
});
client->RecycleOutputPacket(fidl::Clone(packet.header()));
}
FXL_VLOG(3) << "Encoder returned EOS.";
return frames;
}
void feed_raw_frames_into_codec(const FrameEncoder::Payload& payload,
CodecClient* client) {
auto payload_len = [&payload](size_t frame_index) -> size_t {
if (frame_index >= payload.offsets.size()) {
return 0;
}
if (frame_index + 1 == payload.offsets.size()) {
return payload.data.size() - payload.offsets[frame_index].position;
}
return payload.offsets[frame_index + 1].position -
payload.offsets[frame_index].position;
};
size_t frame = 0;
size_t len = 0;
while ((len = payload_len(frame)) > 0) {
FXL_VLOG(10) << "Waiting on input packet.";
auto packet = client->BlockingGetFreeInputPacket();
FXL_VLOG(10) << "Got input packet: " << packet.get();
FXL_CHECK(packet);
const auto& buffer = client->GetInputBufferByIndex(packet->buffer_index());
auto* packet_start = &payload.data[payload.offsets[frame].position];
auto maybe_timestamp_ish = payload.offsets[frame++].timestamp_ish;
packet->set_stream_lifetime_ordinal(kStreamLifetimeOrdinal);
packet->set_start_offset(0);
packet->set_valid_length_bytes(len);
if (maybe_timestamp_ish) {
packet->set_timestamp_ish(*maybe_timestamp_ish);
}
memcpy(buffer.base(), packet_start, len);
client->QueueInputPacket(std::move(packet));
}
client->QueueInputEndOfStream(kStreamLifetimeOrdinal);
FXL_VLOG(3) << "Finished sending frames and EOS to encoder.";
} // namespace
void ConnectToCodec(
fidl::InterfaceRequest<fuchsia::media::StreamProcessor> request,
const fuchsia::media::FormatDetails& format,
component::StartupContext* startup_context) {
fuchsia::mediacodec::CodecFactoryPtr codec_factory;
codec_factory.set_error_handler([](zx_status_t status) {
FXL_LOG(ERROR) << "codec_factory failed - unexpected; status: " << status;
});
startup_context
->ConnectToEnvironmentService<fuchsia::mediacodec::CodecFactory>(
codec_factory.NewRequest());
FXL_VLOG(3) << "Connected to CodecFactory service.";
fuchsia::mediacodec::CreateEncoder_Params params;
params.set_input_details(fidl::Clone(format));
codec_factory->CreateEncoder(std::move(params), std::move(request));
FXL_VLOG(3) << "Requested encoder from factory.";
}
} // namespace
// static
std::vector<FrameEncoder::EncodedFrame> FrameEncoder::EncodeFrames(
const Payload& payload, const fuchsia::media::FormatDetails& format,
component::StartupContext* startup_context, bool expect_access_units) {
fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem;
startup_context->ConnectToEnvironmentService<fuchsia::sysmem::Allocator>(
sysmem.NewRequest());
async::Loop fidl_loop(&kAsyncLoopConfigNoAttachToThread);
auto client = std::make_unique<CodecClient>(&fidl_loop, std::move(sysmem));
fidl_loop.StartThread("FrameEncoder_fidl");
ConnectToCodec(client->GetTheRequestOnce(), format, startup_context);
fuchsia::media::FormatDetails encoded_format_details;
std::vector<EncodedFrame> encoded_frames;
client->Start();
auto consumer_thread = std::make_unique<std::thread>(
[&fidl_loop, &encoded_frames,
expect_access_units, client = client.get()] {
FXL_VLOG(3) << "Starting to receive frames from codec...";
encoded_frames =
take_encoded_frames_from_codec(client, expect_access_units);
async::PostTask(fidl_loop.dispatcher(),
[&fidl_loop] { fidl_loop.Quit(); });
});
feed_raw_frames_into_codec(payload, client.get());
consumer_thread->join();
fidl_loop.JoinThreads();
return encoded_frames;
}