diff --git a/Userland/Applications/VideoPlayer/main.cpp b/Userland/Applications/VideoPlayer/main.cpp index fb56803a05..069b173012 100644 --- a/Userland/Applications/VideoPlayer/main.cpp +++ b/Userland/Applications/VideoPlayer/main.cpp @@ -93,6 +93,7 @@ ErrorOr 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); diff --git a/Userland/Libraries/LibVideo/MatroskaDocument.h b/Userland/Libraries/LibVideo/MatroskaDocument.h index dad55ecf7b..fbe7faf061 100644 --- a/Userland/Libraries/LibVideo/MatroskaDocument.h +++ b/Userland/Libraries/LibVideo/MatroskaDocument.h @@ -13,6 +13,7 @@ #include #include #include +#include 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; }; }; diff --git a/Userland/Libraries/LibVideo/MatroskaReader.cpp b/Userland/Libraries/LibVideo/MatroskaReader.cpp index e2665f4fae..78e6022832 100644 --- a/Userland/Libraries/LibVideo/MatroskaReader.cpp +++ b/Userland/Libraries/LibVideo/MatroskaReader.cpp @@ -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 MatroskaReader::parse_track_entry() return track_entry; } +Optional 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(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(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(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 MatroskaReader::parse_video_track_information() { TrackEntry::VideoTrack video_track {}; @@ -279,6 +337,10 @@ Optional 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(); } diff --git a/Userland/Libraries/LibVideo/MatroskaReader.h b/Userland/Libraries/LibVideo/MatroskaReader.h index 16dc5599bd..36c2a39165 100644 --- a/Userland/Libraries/LibVideo/MatroskaReader.h +++ b/Userland/Libraries/LibVideo/MatroskaReader.h @@ -159,6 +159,7 @@ private: bool parse_tracks(MatroskaDocument&); OwnPtr parse_track_entry(); Optional parse_video_track_information(); + Optional parse_video_color_information(); Optional parse_audio_track_information(); OwnPtr parse_cluster(); OwnPtr parse_simple_block();