blob: 07ba3255c624d1b710eac9eed9995577275664b9 [file] [log] [blame]
/*
* 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<bool()> f_true,
std::function<bool()> 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<bool> 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<uint8_t>(2); // Returns uint8_t with 2 LSB populated.
// ReadUnsigned<uint8_t>(); // Returns uint8_t with all 8 bits populated.
template <typename T>
absl::optional<T> 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<T>(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 <typename T>
absl::optional<T> 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<T>(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<uint8_t>(3), [frame_info](uint8_t color_space) {
frame_info->color_space = static_cast<ColorSpace>(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<uint8_t>(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<uint16_t>(), [frame_info](uint16_t width) {
frame_info->frame_width = width + 1;
});
READ_OR_RETURN(br->ReadUnsigned<uint16_t>(), [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<uint16_t>(),
[frame_info](uint16_t width) {
frame_info->render_width = width + 1;
});
READ_OR_RETURN(br->ReadUnsigned<uint16_t>(),
[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<uint8_t>(),
[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<uint8_t>(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<FrameInfo> 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