diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc new file mode 100644 index 0000000000..bf9d51f692 --- /dev/null +++ b/third_party/libwebrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +#include "modules/video_coding/utility/vp9_uncompressed_header_parser.h" + +#include "absl/numeric/bits.h" +#include "absl/strings/string_view.h" +#include "rtc_base/bitstream_reader.h" +#include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +namespace { +const size_t kVp9NumRefsPerFrame = 3; +const size_t kVp9MaxRefLFDeltas = 4; +const size_t kVp9MaxModeLFDeltas = 2; +const size_t kVp9MinTileWidthB64 = 4; +const size_t kVp9MaxTileWidthB64 = 64; + +void Vp9ReadColorConfig(BitstreamReader& br, + Vp9UncompressedHeader* frame_info) { + if (frame_info->profile == 2 || frame_info->profile == 3) { + frame_info->bit_detph = + br.Read<bool>() ? Vp9BitDept::k12Bit : Vp9BitDept::k10Bit; + } else { + frame_info->bit_detph = Vp9BitDept::k8Bit; + } + + frame_info->color_space = static_cast<Vp9ColorSpace>(br.ReadBits(3)); + + if (frame_info->color_space != Vp9ColorSpace::CS_RGB) { + frame_info->color_range = + br.Read<bool>() ? Vp9ColorRange::kFull : Vp9ColorRange::kStudio; + + if (frame_info->profile == 1 || frame_info->profile == 3) { + static constexpr Vp9YuvSubsampling kSubSamplings[] = { + Vp9YuvSubsampling::k444, Vp9YuvSubsampling::k440, + Vp9YuvSubsampling::k422, Vp9YuvSubsampling::k420}; + frame_info->sub_sampling = kSubSamplings[br.ReadBits(2)]; + + if (br.Read<bool>()) { + RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set."; + br.Invalidate(); + return; + } + } else { + // Profile 0 or 2. + frame_info->sub_sampling = Vp9YuvSubsampling::k420; + } + } else { + // SRGB + frame_info->color_range = Vp9ColorRange::kFull; + if (frame_info->profile == 1 || frame_info->profile == 3) { + frame_info->sub_sampling = Vp9YuvSubsampling::k444; + if (br.Read<bool>()) { + RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set."; + br.Invalidate(); + } + } else { + RTC_LOG(LS_WARNING) << "Failed to parse header. 4:4:4 color not supported" + " in profile 0 or 2."; + br.Invalidate(); + } + } +} + +void ReadRefreshFrameFlags(BitstreamReader& br, + Vp9UncompressedHeader* frame_info) { + // Refresh frame flags. + uint8_t flags = br.Read<uint8_t>(); + for (int i = 0; i < 8; ++i) { + frame_info->updated_buffers.set(i, (flags & (0x01 << (7 - i))) != 0); + } +} + +void Vp9ReadFrameSize(BitstreamReader& br, Vp9UncompressedHeader* frame_info) { + // 16 bits: frame (width|height) - 1. + frame_info->frame_width = br.Read<uint16_t>() + 1; + frame_info->frame_height = br.Read<uint16_t>() + 1; +} + +void Vp9ReadRenderSize(size_t total_buffer_size_bits, + BitstreamReader& br, + Vp9UncompressedHeader* frame_info) { + // render_and_frame_size_different + if (br.Read<bool>()) { + frame_info->render_size_offset_bits = + total_buffer_size_bits - br.RemainingBitCount(); + // 16 bits: render (width|height) - 1. + frame_info->render_width = br.Read<uint16_t>() + 1; + frame_info->render_height = br.Read<uint16_t>() + 1; + } else { + frame_info->render_height = frame_info->frame_height; + frame_info->render_width = frame_info->frame_width; + } +} + +void Vp9ReadFrameSizeFromRefs(BitstreamReader& br, + Vp9UncompressedHeader* frame_info) { + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + // Size in refs. + if (br.Read<bool>()) { + frame_info->infer_size_from_reference = frame_info->reference_buffers[i]; + return; + } + } + + Vp9ReadFrameSize(br, frame_info); +} + +void Vp9ReadLoopfilter(BitstreamReader& br) { + // 6 bits: filter level. + // 3 bits: sharpness level. + br.ConsumeBits(9); + + if (!br.Read<bool>()) { // mode_ref_delta_enabled + return; + } + if (!br.Read<bool>()) { // mode_ref_delta_update + return; + } + + for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) { + if (br.Read<bool>()) { // update_ref_delta + br.ConsumeBits(7); + } + } + for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) { + if (br.Read<bool>()) { // update_mode_delta + br.ConsumeBits(7); + } + } +} + +void Vp9ReadQp(BitstreamReader& br, Vp9UncompressedHeader* frame_info) { + frame_info->base_qp = br.Read<uint8_t>(); + + // yuv offsets + frame_info->is_lossless = frame_info->base_qp == 0; + for (int i = 0; i < 3; ++i) { + if (br.Read<bool>()) { // if delta_coded + // delta_q is a signed integer with leading 4 bits containing absolute + // value and last bit containing sign. There are are two ways to represent + // zero with such encoding. + if ((br.ReadBits(5) & 0b1111'0) != 0) { // delta_q + frame_info->is_lossless = false; + } + } + } +} + +void Vp9ReadSegmentationParams(BitstreamReader& br, + Vp9UncompressedHeader* frame_info) { + constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0}; + constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {true, true, false, + false}; + + frame_info->segmentation_enabled = br.Read<bool>(); + if (!frame_info->segmentation_enabled) { + return; + } + + if (br.Read<bool>()) { // update_map + frame_info->segmentation_tree_probs.emplace(); + for (int i = 0; i < 7; ++i) { + if (br.Read<bool>()) { + (*frame_info->segmentation_tree_probs)[i] = br.Read<uint8_t>(); + } else { + (*frame_info->segmentation_tree_probs)[i] = 255; + } + } + + // temporal_update + frame_info->segmentation_pred_prob.emplace(); + if (br.Read<bool>()) { + for (int i = 0; i < 3; ++i) { + if (br.Read<bool>()) { + (*frame_info->segmentation_pred_prob)[i] = br.Read<uint8_t>(); + } else { + (*frame_info->segmentation_pred_prob)[i] = 255; + } + } + } else { + frame_info->segmentation_pred_prob->fill(255); + } + } + + if (br.Read<bool>()) { // segmentation_update_data + frame_info->segmentation_is_delta = br.Read<bool>(); + for (size_t i = 0; i < kVp9MaxSegments; ++i) { + for (size_t j = 0; j < kVp9SegLvlMax; ++j) { + if (!br.Read<bool>()) { // feature_enabled + continue; + } + if (kSegmentationFeatureBits[j] == 0) { + // No feature bits used and no sign, just mark it and return. + frame_info->segmentation_features[i][j] = 1; + continue; + } + frame_info->segmentation_features[i][j] = + br.ReadBits(kSegmentationFeatureBits[j]); + if (kSegmentationFeatureSigned[j] && br.Read<bool>()) { + (*frame_info->segmentation_features[i][j]) *= -1; + } + } + } + } +} + +void Vp9ReadTileInfo(BitstreamReader& br, Vp9UncompressedHeader* frame_info) { + size_t mi_cols = (frame_info->frame_width + 7) >> 3; + size_t sb64_cols = (mi_cols + 7) >> 3; + + size_t min_log2 = 0; + while ((kVp9MaxTileWidthB64 << min_log2) < sb64_cols) { + ++min_log2; + } + + size_t max_log2 = 1; + while ((sb64_cols >> max_log2) >= kVp9MinTileWidthB64) { + ++max_log2; + } + --max_log2; + + frame_info->tile_cols_log2 = min_log2; + while (frame_info->tile_cols_log2 < max_log2) { + if (br.Read<bool>()) { + ++frame_info->tile_cols_log2; + } else { + break; + } + } + frame_info->tile_rows_log2 = 0; + if (br.Read<bool>()) { + ++frame_info->tile_rows_log2; + if (br.Read<bool>()) { + ++frame_info->tile_rows_log2; + } + } +} + +const Vp9InterpolationFilter kLiteralToType[4] = { + Vp9InterpolationFilter::kEightTapSmooth, Vp9InterpolationFilter::kEightTap, + Vp9InterpolationFilter::kEightTapSharp, Vp9InterpolationFilter::kBilinear}; +} // namespace + +std::string Vp9UncompressedHeader::ToString() const { + char buf[1024]; + rtc::SimpleStringBuilder oss(buf); + + oss << "Vp9UncompressedHeader { " + << "profile = " << profile; + + if (show_existing_frame) { + oss << ", show_existing_frame = " << *show_existing_frame << " }"; + return oss.str(); + } + + oss << ", frame type = " << (is_keyframe ? "key" : "delta") + << ", show_frame = " << (show_frame ? "true" : "false") + << ", error_resilient = " << (error_resilient ? "true" : "false"); + + oss << ", bit_depth = "; + switch (bit_detph) { + case Vp9BitDept::k8Bit: + oss << "8bit"; + break; + case Vp9BitDept::k10Bit: + oss << "10bit"; + break; + case Vp9BitDept::k12Bit: + oss << "12bit"; + break; + } + + if (color_space) { + oss << ", color_space = "; + switch (*color_space) { + case Vp9ColorSpace::CS_UNKNOWN: + oss << "unknown"; + break; + case Vp9ColorSpace::CS_BT_601: + oss << "CS_BT_601 Rec. ITU-R BT.601-7"; + break; + case Vp9ColorSpace::CS_BT_709: + oss << "Rec. ITU-R BT.709-6"; + break; + case Vp9ColorSpace::CS_SMPTE_170: + oss << "SMPTE-170"; + break; + case Vp9ColorSpace::CS_SMPTE_240: + oss << "SMPTE-240"; + break; + case Vp9ColorSpace::CS_BT_2020: + oss << "Rec. ITU-R BT.2020-2"; + break; + case Vp9ColorSpace::CS_RESERVED: + oss << "Reserved"; + break; + case Vp9ColorSpace::CS_RGB: + oss << "sRGB (IEC 61966-2-1)"; + break; + } + } + + if (color_range) { + oss << ", color_range = "; + switch (*color_range) { + case Vp9ColorRange::kFull: + oss << "full"; + break; + case Vp9ColorRange::kStudio: + oss << "studio"; + break; + } + } + + if (sub_sampling) { + oss << ", sub_sampling = "; + switch (*sub_sampling) { + case Vp9YuvSubsampling::k444: + oss << "444"; + break; + case Vp9YuvSubsampling::k440: + oss << "440"; + break; + case Vp9YuvSubsampling::k422: + oss << "422"; + break; + case Vp9YuvSubsampling::k420: + oss << "420"; + break; + } + } + + if (infer_size_from_reference) { + oss << ", infer_frame_resolution_from = " << *infer_size_from_reference; + } else { + oss << ", frame_width = " << frame_width + << ", frame_height = " << frame_height; + } + if (render_width != 0 && render_height != 0) { + oss << ", render_width = " << render_width + << ", render_height = " << render_height; + } + + oss << ", base qp = " << base_qp; + if (reference_buffers[0] != -1) { + oss << ", last_buffer = " << reference_buffers[0]; + } + if (reference_buffers[1] != -1) { + oss << ", golden_buffer = " << reference_buffers[1]; + } + if (reference_buffers[2] != -1) { + oss << ", altref_buffer = " << reference_buffers[2]; + } + + oss << ", updated buffers = { "; + bool first = true; + for (int i = 0; i < 8; ++i) { + if (updated_buffers.test(i)) { + if (first) { + first = false; + } else { + oss << ", "; + } + oss << i; + } + } + oss << " }"; + + oss << ", compressed_header_size_bytes = " << compressed_header_size; + + oss << " }"; + return oss.str(); +} + +void Parse(BitstreamReader& br, + Vp9UncompressedHeader* frame_info, + bool qp_only) { + const size_t total_buffer_size_bits = br.RemainingBitCount(); + + // Frame marker. + if (br.ReadBits(2) != 0b10) { + RTC_LOG(LS_WARNING) << "Failed to parse header. Frame marker should be 2."; + br.Invalidate(); + return; + } + + // Profile has low bit first. + frame_info->profile = br.ReadBit(); + frame_info->profile |= br.ReadBit() << 1; + if (frame_info->profile > 2 && br.Read<bool>()) { + RTC_LOG(LS_WARNING) + << "Failed to parse header. Unsupported bitstream profile."; + br.Invalidate(); + return; + } + + // Show existing frame. + if (br.Read<bool>()) { + frame_info->show_existing_frame = br.ReadBits(3); + return; + } + + // Frame type: KEY_FRAME(0), INTER_FRAME(1). + frame_info->is_keyframe = !br.Read<bool>(); + frame_info->show_frame = br.Read<bool>(); + frame_info->error_resilient = br.Read<bool>(); + + if (frame_info->is_keyframe) { + if (br.ReadBits(24) != 0x498342) { + RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code."; + br.Invalidate(); + return; + } + + Vp9ReadColorConfig(br, frame_info); + Vp9ReadFrameSize(br, frame_info); + Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info); + + // Key-frames implicitly update all buffers. + frame_info->updated_buffers.set(); + } else { + // Non-keyframe. + bool is_intra_only = false; + if (!frame_info->show_frame) { + is_intra_only = br.Read<bool>(); + } + if (!frame_info->error_resilient) { + br.ConsumeBits(2); // Reset frame context. + } + + if (is_intra_only) { + if (br.ReadBits(24) != 0x498342) { + RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code."; + br.Invalidate(); + return; + } + + if (frame_info->profile > 0) { + Vp9ReadColorConfig(br, frame_info); + } else { + frame_info->color_space = Vp9ColorSpace::CS_BT_601; + frame_info->sub_sampling = Vp9YuvSubsampling::k420; + frame_info->bit_detph = Vp9BitDept::k8Bit; + } + frame_info->reference_buffers.fill(-1); + ReadRefreshFrameFlags(br, frame_info); + Vp9ReadFrameSize(br, frame_info); + Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info); + } else { + ReadRefreshFrameFlags(br, frame_info); + + frame_info->reference_buffers_sign_bias[0] = false; + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + frame_info->reference_buffers[i] = br.ReadBits(3); + frame_info->reference_buffers_sign_bias[Vp9ReferenceFrame::kLast + i] = + br.Read<bool>(); + } + + Vp9ReadFrameSizeFromRefs(br, frame_info); + Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info); + + frame_info->allow_high_precision_mv = br.Read<bool>(); + + // Interpolation filter. + if (br.Read<bool>()) { + frame_info->interpolation_filter = Vp9InterpolationFilter::kSwitchable; + } else { + frame_info->interpolation_filter = kLiteralToType[br.ReadBits(2)]; + } + } + } + + if (!frame_info->error_resilient) { + // 1 bit: Refresh frame context. + // 1 bit: Frame parallel decoding mode. + br.ConsumeBits(2); + } + + // Frame context index. + frame_info->frame_context_idx = br.ReadBits(2); + + Vp9ReadLoopfilter(br); + + // Read base QP. + Vp9ReadQp(br, frame_info); + + if (qp_only) { + // Not interested in the rest of the header, return early. + return; + } + + Vp9ReadSegmentationParams(br, frame_info); + Vp9ReadTileInfo(br, frame_info); + frame_info->compressed_header_size = br.Read<uint16_t>(); + frame_info->uncompressed_header_size = + (total_buffer_size_bits / 8) - (br.RemainingBitCount() / 8); +} + +absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header( + rtc::ArrayView<const uint8_t> buf) { + BitstreamReader reader(buf); + Vp9UncompressedHeader frame_info; + Parse(reader, &frame_info, /*qp_only=*/false); + if (reader.Ok() && frame_info.frame_width > 0) { + return frame_info; + } + return absl::nullopt; +} + +namespace vp9 { + +bool GetQp(const uint8_t* buf, size_t length, int* qp) { + BitstreamReader reader(rtc::MakeArrayView(buf, length)); + Vp9UncompressedHeader frame_info; + Parse(reader, &frame_info, /*qp_only=*/true); + if (!reader.Ok()) { + return false; + } + *qp = frame_info.base_qp; + return true; +} + +} // namespace vp9 +} // namespace webrtc |