Compositor video frame transport should not flatten frames into a shareable bitmap before crossing an IPC boundary. That conversion loses the native YUV representation and makes Web-side transport code own the frame wire format. Teach VideoFrame to encode its YUV planes into a shared anonymous buffer with the color space, timing, subsampling, bit depth, and CICP metadata needed to rebuild the frame on decode. Add YUVData helpers for checked plane sizing and construction from validated plane bytes so malformed buffers are rejected before a frame is created. This is preparatory work required to add IPC between the main and compositor threads.
145 lines
5.3 KiB
C++
145 lines
5.3 KiB
C++
/*
|
|
* Copyright (c) 2022-2026, Gregory Bertilson <gregory@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibGfx/YUVData.h>
|
|
#include <LibIPC/Decoder.h>
|
|
#include <LibIPC/Encoder.h>
|
|
|
|
#include "VideoFrame.h"
|
|
|
|
#include <LibCore/AnonymousBuffer.h>
|
|
#include <LibMedia/Color/CodingIndependentCodePoints.h>
|
|
|
|
namespace Media {
|
|
|
|
VideoFrame::VideoFrame(
|
|
AK::Duration timestamp,
|
|
AK::Duration duration,
|
|
Gfx::Size<u32> size,
|
|
u8 bit_depth,
|
|
Gfx::ColorSpace color_space,
|
|
NonnullOwnPtr<Gfx::YUVData> yuv_data)
|
|
: m_timestamp(timestamp)
|
|
, m_duration(duration)
|
|
, m_size(size)
|
|
, m_bit_depth(bit_depth)
|
|
, m_color_space(move(color_space))
|
|
, m_yuv_data(move(yuv_data))
|
|
{
|
|
}
|
|
|
|
VideoFrame::~VideoFrame() = default;
|
|
|
|
}
|
|
|
|
namespace IPC {
|
|
|
|
static bool color_primaries_ipc_value_valid(Media::ColorPrimaries color_primaries)
|
|
{
|
|
return color_primaries == Media::ColorPrimaries::Unspecified
|
|
|| Media::color_primaries_valid(color_primaries);
|
|
}
|
|
|
|
static bool transfer_characteristics_ipc_value_valid(Media::TransferCharacteristics transfer_characteristics)
|
|
{
|
|
return transfer_characteristics == Media::TransferCharacteristics::Unspecified
|
|
|| Media::transfer_characteristics_valid(transfer_characteristics);
|
|
}
|
|
|
|
static bool matrix_coefficients_ipc_value_valid(Media::MatrixCoefficients matrix_coefficients)
|
|
{
|
|
return matrix_coefficients == Media::MatrixCoefficients::Unspecified
|
|
|| Media::matrix_coefficients_valid(matrix_coefficients);
|
|
}
|
|
|
|
static bool video_full_range_flag_ipc_value_valid(Media::VideoFullRangeFlag video_full_range_flag)
|
|
{
|
|
return video_full_range_flag == Media::VideoFullRangeFlag::Unspecified
|
|
|| Media::video_full_range_flag_valid(video_full_range_flag);
|
|
}
|
|
|
|
static ErrorOr<Core::AnonymousBuffer> encode_yuv_data(Gfx::YUVData const& yuv_data)
|
|
{
|
|
auto sizes = TRY(Gfx::YUVData::plane_sizes(yuv_data.size(), yuv_data.bit_depth(), yuv_data.subsampling()));
|
|
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(sizes.total));
|
|
|
|
auto bytes = Bytes { buffer.data<u8>(), buffer.size() };
|
|
yuv_data.y_data().copy_to(bytes.slice(0, sizes.y));
|
|
yuv_data.u_data().copy_to(bytes.slice(sizes.y, sizes.u));
|
|
yuv_data.v_data().copy_to(bytes.slice(sizes.y + sizes.u, sizes.v));
|
|
return buffer;
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> encode(Encoder& encoder, Media::VideoFrame const& frame)
|
|
{
|
|
auto const& yuv_data = frame.yuv_data();
|
|
auto yuv_data_buffer = TRY(encode_yuv_data(yuv_data));
|
|
TRY(encoder.encode(yuv_data_buffer));
|
|
TRY(encoder.encode(frame.color_space()));
|
|
TRY(encoder.encode(frame.timestamp()));
|
|
TRY(encoder.encode(frame.duration()));
|
|
TRY(encoder.encode(yuv_data.size()));
|
|
TRY(encoder.encode(yuv_data.bit_depth()));
|
|
TRY(encoder.encode(yuv_data.subsampling().x()));
|
|
TRY(encoder.encode(yuv_data.subsampling().y()));
|
|
TRY(encoder.encode(yuv_data.cicp().color_primaries()));
|
|
TRY(encoder.encode(yuv_data.cicp().transfer_characteristics()));
|
|
TRY(encoder.encode(yuv_data.cicp().matrix_coefficients()));
|
|
TRY(encoder.encode(yuv_data.cicp().video_full_range_flag()));
|
|
return {};
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<void> encode(Encoder& encoder, NonnullRefPtr<Media::VideoFrame const> const& frame)
|
|
{
|
|
return encoder.encode(*frame);
|
|
}
|
|
|
|
template<>
|
|
ErrorOr<NonnullRefPtr<Media::VideoFrame const>> decode(Decoder& decoder)
|
|
{
|
|
auto yuv_data_buffer = TRY(decoder.decode<Core::AnonymousBuffer>());
|
|
if (!yuv_data_buffer.is_valid())
|
|
return Error::from_string_literal("IPC: VideoFrame contained invalid YUV data");
|
|
|
|
auto color_space = TRY(decoder.decode<Gfx::ColorSpace>());
|
|
auto timestamp = TRY(decoder.decode<AK::Duration>());
|
|
auto duration = TRY(decoder.decode<AK::Duration>());
|
|
auto size = TRY(decoder.decode<Gfx::IntSize>());
|
|
auto bit_depth = TRY(decoder.decode<u8>());
|
|
auto subsampling = Media::Subsampling {
|
|
TRY(decoder.decode<bool>()),
|
|
TRY(decoder.decode<bool>()),
|
|
};
|
|
auto cicp = Media::CodingIndependentCodePoints {
|
|
TRY(decoder.decode<Media::ColorPrimaries>()),
|
|
TRY(decoder.decode<Media::TransferCharacteristics>()),
|
|
TRY(decoder.decode<Media::MatrixCoefficients>()),
|
|
TRY(decoder.decode<Media::VideoFullRangeFlag>()),
|
|
};
|
|
|
|
if (!color_primaries_ipc_value_valid(cicp.color_primaries())
|
|
|| !transfer_characteristics_ipc_value_valid(cicp.transfer_characteristics())
|
|
|| !matrix_coefficients_ipc_value_valid(cicp.matrix_coefficients())
|
|
|| !video_full_range_flag_ipc_value_valid(cicp.video_full_range_flag()))
|
|
return Error::from_string_literal("IPC: VideoFrame contained invalid CICP metadata");
|
|
|
|
auto sizes = TRY(Gfx::YUVData::plane_sizes(size, bit_depth, subsampling));
|
|
if (yuv_data_buffer.size() != sizes.total)
|
|
return Error::from_string_literal("IPC: VideoFrame contained invalid YUV data size");
|
|
|
|
auto bytes = yuv_data_buffer.bytes();
|
|
auto y_data = bytes.slice(0, sizes.y);
|
|
auto u_data = bytes.slice(sizes.y, sizes.u);
|
|
auto v_data = bytes.slice(sizes.y + sizes.u, sizes.v);
|
|
|
|
auto yuv_data = TRY(Gfx::YUVData::create_from_data(size, bit_depth, subsampling, cicp, y_data, u_data, v_data));
|
|
auto frame = TRY(try_make_ref_counted<Media::VideoFrame>(timestamp, duration, size.to_type<u32>(), bit_depth, move(color_space), move(yuv_data)));
|
|
return NonnullRefPtr<Media::VideoFrame const> { *frame };
|
|
}
|
|
|
|
}
|