LibVideo: Add CICP parsing to MatroskaReader

This will allow correct independent code points to be selected from VP9
in WebM, since the VP9 bitstream does not specify them independently.
This commit is contained in:
Zaggy1024 2022-10-10 05:04:28 -05:00 committed by Andreas Kling
parent cd127b65c3
commit b87398341b
Notes: sideshowbarker 2024-07-17 05:07:35 +09:00
4 changed files with 106 additions and 1 deletions

View File

@ -93,6 +93,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto uv_subsampling_x = vp9_decoder.get_uv_subsampling_x();
Gfx::IntSize uv_size { y_size.width() >> uv_subsampling_x, y_size.height() >> uv_subsampling_y };
auto cicp = vp9_decoder.get_cicp_color_space();
video_track.color_format.replace_code_points_if_specified(cicp);
cicp.default_code_points_if_unspecified(Video::ColorPrimaries::BT709, Video::TransferCharacteristics::BT709, Video::MatrixCoefficients::BT709);
auto color_converter_result = Video::ColorConverter::create(vp9_decoder.get_bit_depth(), cicp);

View File

@ -13,6 +13,7 @@
#include <AK/OwnPtr.h>
#include <AK/String.h>
#include <AK/Utf8View.h>
#include <LibVideo/Color/CodingIndependentCodePoints.h>
namespace Video {
@ -50,9 +51,49 @@ public:
Metadata = 33,
};
enum class ColorRange : u8 {
Unspecified = 0,
Broadcast = 1,
Full = 2,
UseCICP = 3, // defined by MatrixCoefficients / TransferCharacteristics
};
struct ColorFormat {
ColorPrimaries color_primaries = ColorPrimaries::Unspecified;
TransferCharacteristics transfer_characteristics = TransferCharacteristics::Unspecified;
MatrixCoefficients matrix_coefficients = MatrixCoefficients::Unspecified;
u64 bits_per_channel = 0;
ColorRange range = ColorRange::Unspecified;
Video::ColorRange full_or_studio_range() const
{
// FIXME: Figure out what UseCICP should do here. Matroska specification did not
// seem to explain in the 'colour' section. When this is fixed, change
// replace_code_points_if_specified to match.
VERIFY(range == ColorRange::Full || range == ColorRange::Broadcast);
if (range == ColorRange::Full)
return Video::ColorRange::Full;
return Video::ColorRange::Studio;
}
void replace_code_points_if_specified(CodingIndependentCodePoints& cicp) const
{
if (color_primaries != ColorPrimaries::Unspecified)
cicp.set_color_primaries(color_primaries);
if (transfer_characteristics != TransferCharacteristics::Unspecified)
cicp.set_transfer_characteristics(transfer_characteristics);
if (matrix_coefficients != MatrixCoefficients::Unspecified)
cicp.set_matrix_coefficients(matrix_coefficients);
if (range != ColorRange::Unspecified && range != ColorRange::UseCICP)
cicp.set_color_range(full_or_studio_range());
}
};
struct VideoTrack {
u64 pixel_width;
u64 pixel_height;
ColorFormat color_format;
};
struct AudioTrack {
@ -93,7 +134,7 @@ private:
FlyString m_codec_id;
union {
VideoTrack m_video_track;
VideoTrack m_video_track {};
AudioTrack m_audio_track;
};
};

View File

@ -26,6 +26,8 @@ constexpr u32 CLUSTER_ELEMENT_ID = 0x1F43B675;
constexpr u32 TIMESTAMP_SCALE_ID = 0x2AD7B1;
constexpr u32 MUXING_APP_ID = 0x4D80;
constexpr u32 WRITING_APP_ID = 0x5741;
// Tracks
constexpr u32 TRACK_ENTRY_ID = 0xAE;
constexpr u32 TRACK_NUMBER_ID = 0xD7;
constexpr u32 TRACK_UID_ID = 0x73C5;
@ -34,10 +36,21 @@ constexpr u32 TRACK_LANGUAGE_ID = 0x22B59C;
constexpr u32 TRACK_CODEC_ID = 0x86;
constexpr u32 TRACK_VIDEO_ID = 0xE0;
constexpr u32 TRACK_AUDIO_ID = 0xE1;
// Video
constexpr u32 PIXEL_WIDTH_ID = 0xB0;
constexpr u32 PIXEL_HEIGHT_ID = 0xBA;
constexpr u32 COLOR_ENTRY_ID = 0x55B0;
constexpr u32 PRIMARIES_ID = 0x55BB;
constexpr u32 TRANSFER_CHARACTERISTICS_ID = 0x55BA;
constexpr u32 MATRIX_COEFFICIENTS_ID = 0x55B1;
constexpr u32 BITS_PER_CHANNEL_ID = 0x55B2;
// Audio
constexpr u32 CHANNELS_ID = 0x9F;
constexpr u32 BIT_DEPTH_ID = 0x6264;
// Clusters
constexpr u32 SIMPLE_BLOCK_ID = 0xA3;
constexpr u32 TIMESTAMP_ID = 0xE7;
@ -264,6 +277,51 @@ OwnPtr<TrackEntry> MatroskaReader::parse_track_entry()
return track_entry;
}
Optional<TrackEntry::ColorFormat> MatroskaReader::parse_video_color_information()
{
TrackEntry::ColorFormat color_format {};
auto success = parse_master_element("Colour"sv, [&](u64 element_id) {
switch (element_id) {
case PRIMARIES_ID: {
auto primaries_result = read_u64_element();
CHECK_HAS_VALUE(primaries_result);
color_format.color_primaries = static_cast<ColorPrimaries>(primaries_result.value());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's Primaries attribute: {}", color_primaries_to_string(color_format.color_primaries));
break;
}
case TRANSFER_CHARACTERISTICS_ID: {
auto transfer_characteristics_result = read_u64_element();
CHECK_HAS_VALUE(transfer_characteristics_result);
color_format.transfer_characteristics = static_cast<TransferCharacteristics>(transfer_characteristics_result.value());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's TransferCharacteristics attribute: {}", transfer_characteristics_to_string(color_format.transfer_characteristics));
break;
}
case MATRIX_COEFFICIENTS_ID: {
auto matrix_coefficients_result = read_u64_element();
CHECK_HAS_VALUE(matrix_coefficients_result);
color_format.matrix_coefficients = static_cast<MatrixCoefficients>(matrix_coefficients_result.value());
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's MatrixCoefficients attribute: {}", matrix_coefficients_to_string(color_format.matrix_coefficients));
break;
}
case BITS_PER_CHANNEL_ID: {
auto bits_per_channel_result = read_u64_element();
CHECK_HAS_VALUE(bits_per_channel_result);
color_format.bits_per_channel = bits_per_channel_result.value();
dbgln_if(MATROSKA_TRACE_DEBUG, "Read Colour's BitsPerChannel attribute: {}", color_format.bits_per_channel);
break;
}
default:
return read_unknown_element();
}
return true;
});
if (!success)
return {};
return color_format;
}
Optional<TrackEntry::VideoTrack> MatroskaReader::parse_video_track_information()
{
TrackEntry::VideoTrack video_track {};
@ -279,6 +337,10 @@ Optional<TrackEntry::VideoTrack> MatroskaReader::parse_video_track_information()
CHECK_HAS_VALUE(pixel_height);
video_track.pixel_height = pixel_height.value();
dbgln_if(MATROSKA_TRACE_DEBUG, "Read VideoTrack's PixelHeight attribute: {}", pixel_height.value());
} else if (element_id == COLOR_ENTRY_ID) {
auto color_information_result = parse_video_color_information();
CHECK_HAS_VALUE(color_information_result);
video_track.color_format = color_information_result.value();
} else {
return read_unknown_element();
}

View File

@ -159,6 +159,7 @@ private:
bool parse_tracks(MatroskaDocument&);
OwnPtr<TrackEntry> parse_track_entry();
Optional<TrackEntry::VideoTrack> parse_video_track_information();
Optional<TrackEntry::ColorFormat> parse_video_color_information();
Optional<TrackEntry::AudioTrack> parse_audio_track_information();
OwnPtr<Cluster> parse_cluster();
OwnPtr<Block> parse_simple_block();