| /* |
| * 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); |
| |
| frame_info->loop_filter_params_offset_bits = |
| total_buffer_size_bits - br.RemainingBitCount(); |
| 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); |
| } |
| |
| std::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 std::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 |