/* * 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/strings/string_view.h" #include "rtc_base/bit_buffer.h" #include "rtc_base/logging.h" namespace webrtc { // Evaluates x and returns false if false. #define RETURN_IF_FALSE(x) \ if (!(x)) { \ return false; \ } // Evaluates x, which is intended to return an optional. If result is nullopt, // returns false. Else, calls fun() with the dereferenced optional as parameter. #define READ_OR_RETURN(x, fun) \ do { \ if (auto optional_val = (x)) { \ fun(*optional_val); \ } else { \ return false; \ } \ } while (false) namespace vp9 { 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; class BitstreamReader { public: explicit BitstreamReader(rtc::BitBuffer* buffer) : buffer_(buffer) {} // Reads on bit from the input stream and: // * returns false if bit cannot be read // * calls f_true() if bit is true, returns return value of that function // * calls f_else() if bit is false, returns return value of that function bool IfNextBoolean( std::function f_true, std::function f_false = [] { return true; }) { uint32_t val; if (!buffer_->ReadBits(1, val)) { return false; } if (val != 0) { return f_true(); } return f_false(); } absl::optional ReadBoolean() { uint32_t val; if (!buffer_->ReadBits(1, val)) { return {}; } return {val != 0}; } // Reads a bit from the input stream and returns: // * false if bit cannot be read // * true if bit matches expected_val // * false if bit does not match expected_val - in which case |error_msg| is // logged as warning, if provided. bool VerifyNextBooleanIs(bool expected_val, absl::string_view error_msg) { uint32_t val; if (!buffer_->ReadBits(1, val)) { return false; } if ((val != 0) != expected_val) { if (!error_msg.empty()) { RTC_LOG(LS_WARNING) << error_msg; } return false; } return true; } // Reads |bits| bits from the bitstream and interprets them as an unsigned // integer that gets cast to the type T before returning. // Returns nullopt if all bits cannot be read. // If number of bits matches size of data type, the bits parameter may be // omitted. Ex: // ReadUnsigned(2); // Returns uint8_t with 2 LSB populated. // ReadUnsigned(); // Returns uint8_t with all 8 bits populated. template absl::optional ReadUnsigned(int bits = sizeof(T) * 8) { RTC_DCHECK_LE(bits, 32); RTC_DCHECK_LE(bits, sizeof(T) * 8); uint32_t val; if (!buffer_->ReadBits(bits, val)) { return {}; } return (static_cast(val)); } // Helper method that reads |num_bits| from the bitstream, returns: // * false if bits cannot be read. // * true if |expected_val| matches the read bits // * false if |expected_val| does not match the read bits, and logs // |error_msg| as a warning (if provided). bool VerifyNextUnsignedIs(int num_bits, uint32_t expected_val, absl::string_view error_msg) { uint32_t val; if (!buffer_->ReadBits(num_bits, val)) { return false; } if (val != expected_val) { if (!error_msg.empty()) { RTC_LOG(LS_WARNING) << error_msg; } return false; } return true; } // Basically the same as ReadUnsigned() - but for signed integers. // Here |bits| indicates the size of the value - number of bits read from the // bit buffer is one higher (the sign bit). This is made to matche the spec in // which eg s(4) = f(1) sign-bit, plus an f(4). template absl::optional ReadSigned(int bits = sizeof(T) * 8) { uint32_t sign; if (!buffer_->ReadBits(1, sign)) { return {}; } uint32_t val; if (!buffer_->ReadBits(bits, val)) { return {}; } int64_t sign_val = val; if (sign != 0) { sign_val = -sign_val; } return {static_cast(sign_val)}; } // Reads |bits| from the bitstream, disregarding their value. // Returns true if full number of bits were read, false otherwise. bool ConsumeBits(int bits) { return buffer_->ConsumeBits(bits); } private: rtc::BitBuffer* buffer_; }; bool Vp9ReadColorConfig(BitstreamReader* br, FrameInfo* frame_info) { if (frame_info->profile == 2 || frame_info->profile == 3) { READ_OR_RETURN(br->ReadBoolean(), [frame_info](bool ten_or_twelve_bits) { frame_info->bit_detph = ten_or_twelve_bits ? BitDept::k12Bit : BitDept::k10Bit; }); } else { frame_info->bit_detph = BitDept::k8Bit; } READ_OR_RETURN( br->ReadUnsigned(3), [frame_info](uint8_t color_space) { frame_info->color_space = static_cast(color_space); }); if (frame_info->color_space != ColorSpace::CS_RGB) { READ_OR_RETURN(br->ReadBoolean(), [frame_info](bool color_range) { frame_info->color_range = color_range ? ColorRange::kFull : ColorRange::kStudio; }); if (frame_info->profile == 1 || frame_info->profile == 3) { READ_OR_RETURN(br->ReadUnsigned(2), [frame_info](uint8_t subsampling) { switch (subsampling) { case 0b00: frame_info->sub_sampling = YuvSubsampling::k444; break; case 0b01: frame_info->sub_sampling = YuvSubsampling::k440; break; case 0b10: frame_info->sub_sampling = YuvSubsampling::k422; break; case 0b11: frame_info->sub_sampling = YuvSubsampling::k420; break; } }); RETURN_IF_FALSE(br->VerifyNextBooleanIs( 0, "Failed to parse header. Reserved bit set.")); } else { // Profile 0 or 2. frame_info->sub_sampling = YuvSubsampling::k420; } } else { // SRGB frame_info->color_range = ColorRange::kFull; if (frame_info->profile == 1 || frame_info->profile == 3) { frame_info->sub_sampling = YuvSubsampling::k444; RETURN_IF_FALSE(br->VerifyNextBooleanIs( 0, "Failed to parse header. Reserved bit set.")); } else { RTC_LOG(LS_WARNING) << "Failed to parse header. 4:4:4 color not supported" " in profile 0 or 2."; return false; } } return true; } bool Vp9ReadFrameSize(BitstreamReader* br, FrameInfo* frame_info) { // 16 bits: frame (width|height) - 1. READ_OR_RETURN(br->ReadUnsigned(), [frame_info](uint16_t width) { frame_info->frame_width = width + 1; }); READ_OR_RETURN(br->ReadUnsigned(), [frame_info](uint16_t height) { frame_info->frame_height = height + 1; }); return true; } bool Vp9ReadRenderSize(BitstreamReader* br, FrameInfo* frame_info) { // render_and_frame_size_different return br->IfNextBoolean( [&] { // 16 bits: render (width|height) - 1. READ_OR_RETURN(br->ReadUnsigned(), [frame_info](uint16_t width) { frame_info->render_width = width + 1; }); READ_OR_RETURN(br->ReadUnsigned(), [frame_info](uint16_t height) { frame_info->render_height = height + 1; }); return true; }, /*else*/ [&] { frame_info->render_height = frame_info->frame_height; frame_info->render_width = frame_info->frame_width; return true; }); } bool Vp9ReadFrameSizeFromRefs(BitstreamReader* br, FrameInfo* frame_info) { bool found_ref = false; for (size_t i = 0; !found_ref && i < kVp9NumRefsPerFrame; i++) { // Size in refs. READ_OR_RETURN(br->ReadBoolean(), [&](bool ref) { found_ref = ref; }); } if (!found_ref) { if (!Vp9ReadFrameSize(br, frame_info)) { return false; } } return Vp9ReadRenderSize(br, frame_info); } bool Vp9ReadLoopfilter(BitstreamReader* br) { // 6 bits: filter level. // 3 bits: sharpness level. RETURN_IF_FALSE(br->ConsumeBits(9)); return br->IfNextBoolean([&] { // if mode_ref_delta_enabled return br->IfNextBoolean([&] { // if mode_ref_delta_update for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) { RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); })); } for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) { RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); })); } return true; }); }); } bool Vp9ReadQp(BitstreamReader* br, FrameInfo* frame_info) { READ_OR_RETURN(br->ReadUnsigned(), [frame_info](uint8_t qp) { frame_info->base_qp = qp; }); // yuv offsets for (int i = 0; i < 3; ++i) { RETURN_IF_FALSE(br->IfNextBoolean([br] { // if delta_coded return br->ConsumeBits(5); })); } return true; } bool Vp9ReadSegmentationParams(BitstreamReader* br) { constexpr int kVp9MaxSegments = 8; constexpr int kVp9SegLvlMax = 4; constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0}; constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {1, 1, 0, 0}; RETURN_IF_FALSE(br->IfNextBoolean([&] { // segmentation_enabled return br->IfNextBoolean([&] { // update_map // Consume probs. for (int i = 0; i < 7; ++i) { RETURN_IF_FALSE(br->IfNextBoolean([br] { return br->ConsumeBits(7); })); } return br->IfNextBoolean([&] { // temporal_update // Consume probs. for (int i = 0; i < 3; ++i) { RETURN_IF_FALSE( br->IfNextBoolean([br] { return br->ConsumeBits(7); })); } return true; }); }); })); return br->IfNextBoolean([&] { RETURN_IF_FALSE(br->ConsumeBits(1)); // abs_or_delta for (int i = 0; i < kVp9MaxSegments; ++i) { for (int j = 0; j < kVp9SegLvlMax; ++j) { RETURN_IF_FALSE(br->IfNextBoolean([&] { // feature_enabled return br->ConsumeBits(kSegmentationFeatureBits[j] + kSegmentationFeatureSigned[j]); })); } } return true; }); } bool Vp9ReadTileInfo(BitstreamReader* br, FrameInfo* 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; size_t cols_log2 = min_log2; bool done = false; while (!done && cols_log2 < max_log2) { RETURN_IF_FALSE(br->IfNextBoolean( [&] { ++cols_log2; return true; }, [&] { done = true; return true; })); } // rows_log2; return br->IfNextBoolean([&] { return br->ConsumeBits(1); }); } } // namespace bool Parse(const uint8_t* buf, size_t length, FrameInfo* frame_info) { rtc::BitBuffer bit_buffer(buf, length); BitstreamReader br(&bit_buffer); // Frame marker. RETURN_IF_FALSE(br.VerifyNextUnsignedIs( 2, 0x2, "Failed to parse header. Frame marker should be 2.")); // Profile has low bit first. READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool low) { frame_info->profile = int{low}; }); READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool high) { frame_info->profile |= int{high} << 1; }); if (frame_info->profile > 2) { RETURN_IF_FALSE(br.VerifyNextBooleanIs( false, "Failed to get QP. Unsupported bitstream profile.")); } // Show existing frame. RETURN_IF_FALSE(br.IfNextBoolean([&] { READ_OR_RETURN(br.ReadUnsigned(3), [frame_info](uint8_t frame_idx) { frame_info->show_existing_frame = frame_idx; }); return true; })); if (frame_info->show_existing_frame.has_value()) { return true; } READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool frame_type) { // Frame type: KEY_FRAME(0), INTER_FRAME(1). frame_info->is_keyframe = frame_type == 0; }); READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool show_frame) { frame_info->show_frame = show_frame; }); READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool error_resilient) { frame_info->error_resilient = error_resilient; }); if (frame_info->is_keyframe) { RETURN_IF_FALSE(br.VerifyNextUnsignedIs( 24, 0x498342, "Failed to get QP. Invalid sync code.")); if (!Vp9ReadColorConfig(&br, frame_info)) return false; if (!Vp9ReadFrameSize(&br, frame_info)) return false; if (!Vp9ReadRenderSize(&br, frame_info)) return false; } else { // Non-keyframe. bool is_intra_only = false; if (!frame_info->show_frame) { READ_OR_RETURN(br.ReadBoolean(), [&](bool intra_only) { is_intra_only = intra_only; }); } if (!frame_info->error_resilient) { RETURN_IF_FALSE(br.ConsumeBits(2)); // Reset frame context. } if (is_intra_only) { RETURN_IF_FALSE(br.VerifyNextUnsignedIs( 24, 0x498342, "Failed to get QP. Invalid sync code.")); if (frame_info->profile > 0) { if (!Vp9ReadColorConfig(&br, frame_info)) return false; } // Refresh frame flags. RETURN_IF_FALSE(br.ConsumeBits(8)); if (!Vp9ReadFrameSize(&br, frame_info)) return false; if (!Vp9ReadRenderSize(&br, frame_info)) return false; } else { // Refresh frame flags. RETURN_IF_FALSE(br.ConsumeBits(8)); for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { // 3 bits: Ref frame index. // 1 bit: Ref frame sign biases. RETURN_IF_FALSE(br.ConsumeBits(4)); } if (!Vp9ReadFrameSizeFromRefs(&br, frame_info)) return false; // Allow high precision mv. RETURN_IF_FALSE(br.ConsumeBits(1)); // Interpolation filter. RETURN_IF_FALSE(br.IfNextBoolean([] { return true; }, [&br] { return br.ConsumeBits(2); })); } } if (!frame_info->error_resilient) { // 1 bit: Refresh frame context. // 1 bit: Frame parallel decoding mode. RETURN_IF_FALSE(br.ConsumeBits(2)); } // Frame context index. RETURN_IF_FALSE(br.ConsumeBits(2)); if (!Vp9ReadLoopfilter(&br)) return false; // Read base QP. RETURN_IF_FALSE(Vp9ReadQp(&br, frame_info)); const bool kParseFullHeader = false; if (kParseFullHeader) { // Currently not used, but will be needed when parsing beyond the // uncompressed header. RETURN_IF_FALSE(Vp9ReadSegmentationParams(&br)); RETURN_IF_FALSE(Vp9ReadTileInfo(&br, frame_info)); RETURN_IF_FALSE(br.ConsumeBits(16)); // header_size_in_bytes } return true; } bool GetQp(const uint8_t* buf, size_t length, int* qp) { FrameInfo frame_info; if (!Parse(buf, length, &frame_info)) { return false; } *qp = frame_info.base_qp; return true; } absl::optional ParseIntraFrameInfo(const uint8_t* buf, size_t length) { FrameInfo frame_info; if (Parse(buf, length, &frame_info) && frame_info.frame_width > 0) { return frame_info; } return absl::nullopt; } } // namespace vp9 } // namespace webrtc