Change ParseUncompressedVp9Header implementation to use BitstreamReader

Bug: None
Change-Id: I91010b0102622fd8154f8ba941e61298b0584eae
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230802
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34899}
This commit is contained in:
Danil Chapovalov 2021-09-01 16:27:04 +02:00 committed by WebRTC LUCI CQ
parent 7cca016721
commit faba0fa1c7
4 changed files with 204 additions and 450 deletions

View file

@ -355,6 +355,7 @@ rtc_library("video_coding_utility") {
"../../api/video_codecs:video_codecs_api", "../../api/video_codecs:video_codecs_api",
"../../common_video", "../../common_video",
"../../modules/rtp_rtcp", "../../modules/rtp_rtcp",
"../../rtc_base:bitstream_reader",
"../../rtc_base:checks", "../../rtc_base:checks",
"../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_base_approved",
"../../rtc_base:rtc_numerics", "../../rtc_base:rtc_numerics",
@ -374,6 +375,7 @@ rtc_library("video_coding_utility") {
"../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_rtcp_format",
] ]
absl_deps = [ absl_deps = [
"//third_party/abseil-cpp/absl/numeric:bits",
"//third_party/abseil-cpp/absl/strings:strings", "//third_party/abseil-cpp/absl/strings:strings",
"//third_party/abseil-cpp/absl/types:optional", "//third_party/abseil-cpp/absl/types:optional",
] ]

View file

@ -9,30 +9,13 @@
*/ */
#include "modules/video_coding/utility/vp9_uncompressed_header_parser.h" #include "modules/video_coding/utility/vp9_uncompressed_header_parser.h"
#include "absl/numeric/bits.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "rtc_base/bit_buffer.h" #include "rtc_base/bitstream_reader.h"
#include "rtc_base/logging.h" #include "rtc_base/logging.h"
#include "rtc_base/strings/string_builder.h" #include "rtc_base/strings/string_builder.h"
namespace webrtc { 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 { namespace {
const size_t kVp9NumRefsPerFrame = 3; const size_t kVp9NumRefsPerFrame = 3;
const size_t kVp9MaxRefLFDeltas = 4; const size_t kVp9MaxRefLFDeltas = 4;
@ -40,169 +23,32 @@ const size_t kVp9MaxModeLFDeltas = 2;
const size_t kVp9MinTileWidthB64 = 4; const size_t kVp9MinTileWidthB64 = 4;
const size_t kVp9MaxTileWidthB64 = 64; const size_t kVp9MaxTileWidthB64 = 64;
class BitstreamReader { void Vp9ReadColorConfig(BitstreamReader& br,
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); }
void GetPosition(size_t* out_byte_offset, size_t* out_bit_offset) const {
buffer_->GetCurrentOffset(out_byte_offset, out_bit_offset);
}
private:
rtc::BitBuffer* buffer_;
};
bool Vp9ReadColorConfig(BitstreamReader* br,
Vp9UncompressedHeader* frame_info) { Vp9UncompressedHeader* frame_info) {
if (frame_info->profile == 2 || frame_info->profile == 3) { 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 =
frame_info->bit_detph = br.Read<bool>() ? Vp9BitDept::k12Bit : Vp9BitDept::k10Bit;
ten_or_twelve_bits ? Vp9BitDept::k12Bit : Vp9BitDept::k10Bit;
});
} else { } else {
frame_info->bit_detph = Vp9BitDept::k8Bit; frame_info->bit_detph = Vp9BitDept::k8Bit;
} }
READ_OR_RETURN( frame_info->color_space = static_cast<Vp9ColorSpace>(br.ReadBits(3));
br->ReadUnsigned<uint8_t>(3), [frame_info](uint8_t color_space) {
frame_info->color_space = static_cast<Vp9ColorSpace>(color_space);
});
if (frame_info->color_space != Vp9ColorSpace::CS_RGB) { if (frame_info->color_space != Vp9ColorSpace::CS_RGB) {
READ_OR_RETURN(br->ReadBoolean(), [frame_info](bool color_range) { frame_info->color_range =
frame_info->color_range = br.Read<bool>() ? Vp9ColorRange::kFull : Vp9ColorRange::kStudio;
color_range ? Vp9ColorRange::kFull : Vp9ColorRange::kStudio;
});
if (frame_info->profile == 1 || frame_info->profile == 3) { if (frame_info->profile == 1 || frame_info->profile == 3) {
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(2), static constexpr Vp9YuvSubsampling kSubSamplings[] = {
[frame_info](uint8_t subsampling) { Vp9YuvSubsampling::k444, Vp9YuvSubsampling::k440,
switch (subsampling) { Vp9YuvSubsampling::k422, Vp9YuvSubsampling::k420};
case 0b00: frame_info->sub_sampling = kSubSamplings[br.ReadBits(2)];
frame_info->sub_sampling = Vp9YuvSubsampling::k444;
break;
case 0b01:
frame_info->sub_sampling = Vp9YuvSubsampling::k440;
break;
case 0b10:
frame_info->sub_sampling = Vp9YuvSubsampling::k422;
break;
case 0b11:
frame_info->sub_sampling = Vp9YuvSubsampling::k420;
break;
}
});
RETURN_IF_FALSE(br->VerifyNextBooleanIs( if (br.Read<bool>()) {
0, "Failed to parse header. Reserved bit set.")); RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
br.Invalidate();
return;
}
} else { } else {
// Profile 0 or 2. // Profile 0 or 2.
frame_info->sub_sampling = Vp9YuvSubsampling::k420; frame_info->sub_sampling = Vp9YuvSubsampling::k420;
@ -212,206 +58,158 @@ bool Vp9ReadColorConfig(BitstreamReader* br,
frame_info->color_range = Vp9ColorRange::kFull; frame_info->color_range = Vp9ColorRange::kFull;
if (frame_info->profile == 1 || frame_info->profile == 3) { if (frame_info->profile == 1 || frame_info->profile == 3) {
frame_info->sub_sampling = Vp9YuvSubsampling::k444; frame_info->sub_sampling = Vp9YuvSubsampling::k444;
RETURN_IF_FALSE(br->VerifyNextBooleanIs( if (br.Read<bool>()) {
0, "Failed to parse header. Reserved bit set.")); RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
br.Invalidate();
}
} else { } else {
RTC_LOG(LS_WARNING) << "Failed to parse header. 4:4:4 color not supported" RTC_LOG(LS_WARNING) << "Failed to parse header. 4:4:4 color not supported"
" in profile 0 or 2."; " in profile 0 or 2.";
return false; br.Invalidate();
} }
} }
return true;
} }
bool ReadRefreshFrameFlags(BitstreamReader* br, void ReadRefreshFrameFlags(BitstreamReader& br,
Vp9UncompressedHeader* frame_info) { Vp9UncompressedHeader* frame_info) {
// Refresh frame flags. // Refresh frame flags.
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(), [frame_info](uint8_t flags) { uint8_t flags = br.Read<uint8_t>();
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
frame_info->updated_buffers.set(i, (flags & (0x01 << (7 - i))) != 0); frame_info->updated_buffers.set(i, (flags & (0x01 << (7 - i))) != 0);
} }
});
return true;
} }
bool Vp9ReadFrameSize(BitstreamReader* br, Vp9UncompressedHeader* frame_info) { void Vp9ReadFrameSize(BitstreamReader& br, Vp9UncompressedHeader* frame_info) {
// 16 bits: frame (width|height) - 1. // 16 bits: frame (width|height) - 1.
READ_OR_RETURN(br->ReadUnsigned<uint16_t>(), [frame_info](uint16_t width) { frame_info->frame_width = br.Read<uint16_t>() + 1;
frame_info->frame_width = width + 1; frame_info->frame_height = br.Read<uint16_t>() + 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, Vp9UncompressedHeader* frame_info) { void Vp9ReadRenderSize(size_t total_buffer_size_bits,
BitstreamReader& br,
Vp9UncompressedHeader* frame_info) {
// render_and_frame_size_different // render_and_frame_size_different
return br->IfNextBoolean( if (br.Read<bool>()) {
[&] { frame_info->render_size_offset_bits =
auto& pos = frame_info->render_size_position.emplace(); total_buffer_size_bits - br.RemainingBitCount();
br->GetPosition(&pos.byte_offset, &pos.bit_offset); // 16 bits: render (width|height) - 1.
// 16 bits: render (width|height) - 1. frame_info->render_width = br.Read<uint16_t>() + 1;
READ_OR_RETURN(br->ReadUnsigned<uint16_t>(), frame_info->render_height = br.Read<uint16_t>() + 1;
[frame_info](uint16_t width) { } else {
frame_info->render_width = width + 1; frame_info->render_height = frame_info->frame_height;
}); frame_info->render_width = frame_info->frame_width;
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, void Vp9ReadFrameSizeFromRefs(BitstreamReader& br,
Vp9UncompressedHeader* frame_info) { Vp9UncompressedHeader* frame_info) {
bool found_ref = false; for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
for (size_t i = 0; !found_ref && i < kVp9NumRefsPerFrame; i++) {
// Size in refs. // Size in refs.
br->IfNextBoolean([&] { if (br.Read<bool>()) {
frame_info->infer_size_from_reference = frame_info->reference_buffers[i]; frame_info->infer_size_from_reference = frame_info->reference_buffers[i];
found_ref = true; return;
return true;
});
}
if (!found_ref) {
if (!Vp9ReadFrameSize(br, frame_info)) {
return false;
} }
} }
return Vp9ReadRenderSize(br, frame_info);
Vp9ReadFrameSize(br, frame_info);
} }
bool Vp9ReadLoopfilter(BitstreamReader* br) { void Vp9ReadLoopfilter(BitstreamReader& br) {
// 6 bits: filter level. // 6 bits: filter level.
// 3 bits: sharpness level. // 3 bits: sharpness level.
RETURN_IF_FALSE(br->ConsumeBits(9)); br.ConsumeBits(9);
return br->IfNextBoolean([&] { // if mode_ref_delta_enabled if (!br.Read<bool>()) { // mode_ref_delta_enabled
return br->IfNextBoolean([&] { // if mode_ref_delta_update return;
for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) { }
RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); })); if (!br.Read<bool>()) { // mode_ref_delta_update
} return;
for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) { }
RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); }));
} for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) {
return true; 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);
}
}
} }
bool Vp9ReadQp(BitstreamReader* br, Vp9UncompressedHeader* frame_info) { void Vp9ReadQp(BitstreamReader& br, Vp9UncompressedHeader* frame_info) {
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(), frame_info->base_qp = br.Read<uint8_t>();
[frame_info](uint8_t qp) { frame_info->base_qp = qp; });
// yuv offsets // yuv offsets
frame_info->is_lossless = frame_info->base_qp == 0; frame_info->is_lossless = frame_info->base_qp == 0;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
RETURN_IF_FALSE(br->IfNextBoolean([&] { // if delta_coded if (br.Read<bool>()) { // if delta_coded
READ_OR_RETURN(br->ReadUnsigned<int>(4), [&](int delta) { if (br.ReadBits(4) != 0) {
if (delta != 0) { frame_info->is_lossless = false;
frame_info->is_lossless = false; }
} }
});
return true;
}));
} }
return true;
} }
bool Vp9ReadSegmentationParams(BitstreamReader* br, void Vp9ReadSegmentationParams(BitstreamReader& br,
Vp9UncompressedHeader* frame_info) { Vp9UncompressedHeader* frame_info) {
constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0}; constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0};
constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {1, 1, 0, 0}; constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {1, 1, 0, 0};
return br->IfNextBoolean([&] { // segmentation_enabled frame_info->segmentation_enabled = br.Read<bool>();
frame_info->segmentation_enabled = true; if (!frame_info->segmentation_enabled) {
RETURN_IF_FALSE(br->IfNextBoolean([&] { // update_map return;
frame_info->segmentation_tree_probs.emplace(); }
for (int i = 0; i < 7; ++i) {
RETURN_IF_FALSE(br->IfNextBoolean( if (br.Read<bool>()) { // update_map
[&] { frame_info->segmentation_tree_probs.emplace();
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(), [&](uint8_t prob) { for (int i = 0; i < 7; ++i) {
(*frame_info->segmentation_tree_probs)[i] = prob; if (br.Read<bool>()) {
}); (*frame_info->segmentation_tree_probs)[i] = br.Read<uint8_t>();
return true; } else {
}, (*frame_info->segmentation_tree_probs)[i] = 255;
[&] {
(*frame_info->segmentation_tree_probs)[i] = 255;
return true;
}));
} }
}
// temporal_update // temporal_update
frame_info->segmentation_pred_prob.emplace(); frame_info->segmentation_pred_prob.emplace();
return br->IfNextBoolean( if (br.Read<bool>()) {
[&] { for (int i = 0; i < 3; ++i) {
for (int i = 0; i < 3; ++i) { if (br.Read<bool>()) {
RETURN_IF_FALSE(br->IfNextBoolean( (*frame_info->segmentation_pred_prob)[i] = br.Read<uint8_t>();
[&] { } else {
READ_OR_RETURN( (*frame_info->segmentation_pred_prob)[i] = 255;
br->ReadUnsigned<uint8_t>(), [&](uint8_t prob) {
(*frame_info->segmentation_pred_prob)[i] = prob;
});
return true;
},
[&] {
(*frame_info->segmentation_pred_prob)[i] = 255;
return true;
}));
}
return true;
},
[&] {
frame_info->segmentation_pred_prob->fill(255);
return true;
});
}));
return br->IfNextBoolean([&] { // segmentation_update_data
RETURN_IF_FALSE(br->IfNextBoolean([&] {
frame_info->segmentation_is_delta = true;
return true;
}));
for (size_t i = 0; i < kVp9MaxSegments; ++i) {
for (size_t j = 0; j < kVp9SegLvlMax; ++j) {
RETURN_IF_FALSE(br->IfNextBoolean([&] { // feature_enabled
if (kSegmentationFeatureBits[j] == 0) {
// No feature bits used and no sign, just mark it and return.
frame_info->segmentation_features[i][j] = 1;
return true;
}
READ_OR_RETURN(
br->ReadUnsigned<uint8_t>(kSegmentationFeatureBits[j]),
[&](uint8_t feature_value) {
frame_info->segmentation_features[i][j] = feature_value;
});
if (kSegmentationFeatureSigned[j]) {
RETURN_IF_FALSE(br->IfNextBoolean([&] {
(*frame_info->segmentation_features[i][j]) *= -1;
return true;
}));
}
return true;
}));
} }
} }
return true; } 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;
}
}
}
}
} }
bool Vp9ReadTileInfo(BitstreamReader* br, Vp9UncompressedHeader* frame_info) { void Vp9ReadTileInfo(BitstreamReader& br, Vp9UncompressedHeader* frame_info) {
size_t mi_cols = (frame_info->frame_width + 7) >> 3; size_t mi_cols = (frame_info->frame_width + 7) >> 3;
size_t sb64_cols = (mi_cols + 7) >> 3; size_t sb64_cols = (mi_cols + 7) >> 3;
@ -427,27 +225,20 @@ bool Vp9ReadTileInfo(BitstreamReader* br, Vp9UncompressedHeader* frame_info) {
--max_log2; --max_log2;
frame_info->tile_cols_log2 = min_log2; frame_info->tile_cols_log2 = min_log2;
bool done = false; while (frame_info->tile_cols_log2 < max_log2) {
while (!done && frame_info->tile_cols_log2 < max_log2) { if (br.Read<bool>()) {
RETURN_IF_FALSE(br->IfNextBoolean( ++frame_info->tile_cols_log2;
[&] { } else {
++frame_info->tile_cols_log2; break;
return true; }
},
[&] {
done = true;
return true;
}));
} }
frame_info->tile_rows_log2 = 0; frame_info->tile_rows_log2 = 0;
RETURN_IF_FALSE(br->IfNextBoolean([&] { if (br.Read<bool>()) {
++frame_info->tile_rows_log2; ++frame_info->tile_rows_log2;
return br->IfNextBoolean([&] { if (br.Read<bool>()) {
++frame_info->tile_rows_log2; ++frame_info->tile_rows_log2;
return true; }
}); }
}));
return true;
} }
const Vp9InterpolationFilter kLiteralToType[4] = { const Vp9InterpolationFilter kLiteralToType[4] = {
@ -586,60 +377,49 @@ std::string Vp9UncompressedHeader::ToString() const {
return oss.str(); return oss.str();
} }
bool Parse(rtc::ArrayView<const uint8_t> buf, void Parse(BitstreamReader& br,
Vp9UncompressedHeader* frame_info, Vp9UncompressedHeader* frame_info,
bool qp_only) { bool qp_only) {
rtc::BitBuffer bit_buffer(buf.data(), buf.size()); const size_t total_buffer_size_bits = br.RemainingBitCount();
BitstreamReader br(&bit_buffer);
// Frame marker. // Frame marker.
RETURN_IF_FALSE(br.VerifyNextUnsignedIs( if (br.ReadBits(2) != 0b10) {
2, 0x2, "Failed to parse header. Frame marker should be 2.")); RTC_LOG(LS_WARNING) << "Failed to parse header. Frame marker should be 2.";
br.Invalidate();
return;
}
// Profile has low bit first. // Profile has low bit first.
READ_OR_RETURN(br.ReadBoolean(), frame_info->profile = br.ReadBit();
[frame_info](bool low) { frame_info->profile = int{low}; }); frame_info->profile |= br.ReadBit() << 1;
READ_OR_RETURN(br.ReadBoolean(), [frame_info](bool high) { if (frame_info->profile > 2 && br.Read<bool>()) {
frame_info->profile |= int{high} << 1; RTC_LOG(LS_WARNING)
}); << "Failed to parse header. Unsupported bitstream profile.";
if (frame_info->profile > 2) { br.Invalidate();
RETURN_IF_FALSE(br.VerifyNextBooleanIs( return;
false, "Failed to get QP. Unsupported bitstream profile."));
} }
// Show existing frame. // Show existing frame.
RETURN_IF_FALSE(br.IfNextBoolean([&] { if (br.Read<bool>()) {
READ_OR_RETURN(br.ReadUnsigned<uint8_t>(3), frame_info->show_existing_frame = br.ReadBits(3);
[frame_info](uint8_t frame_idx) { return;
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 type: KEY_FRAME(0), INTER_FRAME(1). frame_info->is_keyframe = !br.Read<bool>();
frame_info->is_keyframe = frame_type == 0; frame_info->show_frame = br.Read<bool>();
}); frame_info->error_resilient = br.Read<bool>();
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) { if (frame_info->is_keyframe) {
RETURN_IF_FALSE(br.VerifyNextUnsignedIs( if (br.ReadBits(24) != 0x498342) {
24, 0x498342, "Failed to get QP. Invalid sync code.")); RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code.";
br.Invalidate();
return;
}
if (!Vp9ReadColorConfig(&br, frame_info)) Vp9ReadColorConfig(br, frame_info);
return false; Vp9ReadFrameSize(br, frame_info);
if (!Vp9ReadFrameSize(&br, frame_info)) Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info);
return false;
if (!Vp9ReadRenderSize(&br, frame_info))
return false;
// Key-frames implicitly update all buffers. // Key-frames implicitly update all buffers.
frame_info->updated_buffers.set(); frame_info->updated_buffers.set();
@ -647,108 +427,86 @@ bool Parse(rtc::ArrayView<const uint8_t> buf,
// Non-keyframe. // Non-keyframe.
bool is_intra_only = false; bool is_intra_only = false;
if (!frame_info->show_frame) { if (!frame_info->show_frame) {
READ_OR_RETURN(br.ReadBoolean(), is_intra_only = br.Read<bool>();
[&](bool intra_only) { is_intra_only = intra_only; });
} }
if (!frame_info->error_resilient) { if (!frame_info->error_resilient) {
RETURN_IF_FALSE(br.ConsumeBits(2)); // Reset frame context. br.ConsumeBits(2); // Reset frame context.
} }
if (is_intra_only) { if (is_intra_only) {
RETURN_IF_FALSE(br.VerifyNextUnsignedIs( if (br.ReadBits(24) != 0x498342) {
24, 0x498342, "Failed to get QP. Invalid sync code.")); RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code.";
br.Invalidate();
return;
}
if (frame_info->profile > 0) { if (frame_info->profile > 0) {
if (!Vp9ReadColorConfig(&br, frame_info)) Vp9ReadColorConfig(br, frame_info);
return false;
} else { } else {
frame_info->color_space = Vp9ColorSpace::CS_BT_601; frame_info->color_space = Vp9ColorSpace::CS_BT_601;
frame_info->sub_sampling = Vp9YuvSubsampling::k420; frame_info->sub_sampling = Vp9YuvSubsampling::k420;
frame_info->bit_detph = Vp9BitDept::k8Bit; frame_info->bit_detph = Vp9BitDept::k8Bit;
} }
frame_info->reference_buffers.fill(-1); frame_info->reference_buffers.fill(-1);
RETURN_IF_FALSE(ReadRefreshFrameFlags(&br, frame_info)); ReadRefreshFrameFlags(br, frame_info);
RETURN_IF_FALSE(Vp9ReadFrameSize(&br, frame_info)); Vp9ReadFrameSize(br, frame_info);
RETURN_IF_FALSE(Vp9ReadRenderSize(&br, frame_info)); Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info);
} else { } else {
RETURN_IF_FALSE(ReadRefreshFrameFlags(&br, frame_info)); ReadRefreshFrameFlags(br, frame_info);
frame_info->reference_buffers_sign_bias[0] = false; frame_info->reference_buffers_sign_bias[0] = false;
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
READ_OR_RETURN(br.ReadUnsigned<uint8_t>(3), [&](uint8_t idx) { frame_info->reference_buffers[i] = br.ReadBits(3);
frame_info->reference_buffers[i] = idx; frame_info->reference_buffers_sign_bias[Vp9ReferenceFrame::kLast + i] =
}); br.Read<bool>();
READ_OR_RETURN(br.ReadBoolean(), [&](bool sign_bias) {
frame_info
->reference_buffers_sign_bias[Vp9ReferenceFrame::kLast + i] =
sign_bias;
});
} }
if (!Vp9ReadFrameSizeFromRefs(&br, frame_info)) Vp9ReadFrameSizeFromRefs(br, frame_info);
return false; Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info);
READ_OR_RETURN(br.ReadBoolean(), [&](bool allow_high_precision_mv) { frame_info->allow_high_precision_mv = br.Read<bool>();
frame_info->allow_high_precision_mv = allow_high_precision_mv;
});
// Interpolation filter. // Interpolation filter.
RETURN_IF_FALSE(br.IfNextBoolean( if (br.Read<bool>()) {
[frame_info] { frame_info->interpolation_filter = Vp9InterpolationFilter::kSwitchable;
frame_info->interpolation_filter = } else {
Vp9InterpolationFilter::kSwitchable; frame_info->interpolation_filter = kLiteralToType[br.ReadBits(2)];
return true; }
},
[&] {
READ_OR_RETURN(
br.ReadUnsigned<uint8_t>(2), [frame_info](uint8_t filter) {
frame_info->interpolation_filter = kLiteralToType[filter];
});
return true;
}));
} }
} }
if (!frame_info->error_resilient) { if (!frame_info->error_resilient) {
// 1 bit: Refresh frame context. // 1 bit: Refresh frame context.
// 1 bit: Frame parallel decoding mode. // 1 bit: Frame parallel decoding mode.
RETURN_IF_FALSE(br.ConsumeBits(2)); br.ConsumeBits(2);
} }
// Frame context index. // Frame context index.
READ_OR_RETURN(br.ReadUnsigned<uint8_t>(2), frame_info->frame_context_idx = br.ReadBits(2);
[&](uint8_t idx) { frame_info->frame_context_idx = idx; });
if (!Vp9ReadLoopfilter(&br)) Vp9ReadLoopfilter(br);
return false;
// Read base QP. // Read base QP.
RETURN_IF_FALSE(Vp9ReadQp(&br, frame_info)); Vp9ReadQp(br, frame_info);
if (qp_only) { if (qp_only) {
// Not interested in the rest of the header, return early. // Not interested in the rest of the header, return early.
return true; return;
} }
RETURN_IF_FALSE(Vp9ReadSegmentationParams(&br, frame_info)); Vp9ReadSegmentationParams(br, frame_info);
RETURN_IF_FALSE(Vp9ReadTileInfo(&br, frame_info)); Vp9ReadTileInfo(br, frame_info);
READ_OR_RETURN(br.ReadUnsigned<uint16_t>(), [frame_info](uint16_t size) { frame_info->compressed_header_size = br.Read<uint16_t>();
frame_info->compressed_header_size = size;
});
// Trailing bits.
RETURN_IF_FALSE(br.ConsumeBits(bit_buffer.RemainingBitCount() % 8));
frame_info->uncompressed_header_size = frame_info->uncompressed_header_size =
buf.size() - (bit_buffer.RemainingBitCount() / 8); (total_buffer_size_bits / 8) - (br.RemainingBitCount() / 8);
return true;
} }
absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header( absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header(
rtc::ArrayView<const uint8_t> buf) { rtc::ArrayView<const uint8_t> buf) {
BitstreamReader reader(buf);
Vp9UncompressedHeader frame_info; Vp9UncompressedHeader frame_info;
if (Parse(buf, &frame_info, /*qp_only=*/false) && Parse(reader, &frame_info, /*qp_only=*/false);
frame_info.frame_width > 0) { if (reader.Ok() && frame_info.frame_width > 0) {
return frame_info; return frame_info;
} }
return absl::nullopt; return absl::nullopt;
@ -757,8 +515,10 @@ absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header(
namespace vp9 { namespace vp9 {
bool GetQp(const uint8_t* buf, size_t length, int* qp) { bool GetQp(const uint8_t* buf, size_t length, int* qp) {
BitstreamReader reader(rtc::MakeArrayView(buf, length));
Vp9UncompressedHeader frame_info; Vp9UncompressedHeader frame_info;
if (!Parse(rtc::MakeArrayView(buf, length), &frame_info, /*qp_only=*/true)) { Parse(reader, &frame_info, /*qp_only=*/true);
if (!reader.Ok()) {
return false; return false;
} }
*qp = frame_info.base_qp; *qp = frame_info.base_qp;

View file

@ -105,11 +105,7 @@ struct Vp9UncompressedHeader {
// Width/height of the tiles used (in units of 8x8 blocks). // Width/height of the tiles used (in units of 8x8 blocks).
size_t tile_cols_log2 = 0; // tile_cols = 1 << tile_cols_log2 size_t tile_cols_log2 = 0; // tile_cols = 1 << tile_cols_log2
size_t tile_rows_log2 = 0; // tile_rows = 1 << tile_rows_log2 size_t tile_rows_log2 = 0; // tile_rows = 1 << tile_rows_log2
struct BitstreamPosition { absl::optional<size_t> render_size_offset_bits;
size_t byte_offset = 0;
size_t bit_offset = 0;
};
absl::optional<BitstreamPosition> render_size_position;
Vp9InterpolationFilter interpolation_filter = Vp9InterpolationFilter interpolation_filter =
Vp9InterpolationFilter::kEightTap; Vp9InterpolationFilter::kEightTap;
bool allow_high_precision_mv = false; bool allow_high_precision_mv = false;

View file

@ -55,11 +55,7 @@ TEST(Vp9UncompressedHeaderParserTest, FrameWithSegmentation) {
EXPECT_EQ(frame_info->updated_buffers, 0b10000000); EXPECT_EQ(frame_info->updated_buffers, 0b10000000);
EXPECT_EQ(frame_info->tile_cols_log2, 0u); EXPECT_EQ(frame_info->tile_cols_log2, 0u);
EXPECT_EQ(frame_info->tile_rows_log2, 0u); EXPECT_EQ(frame_info->tile_rows_log2, 0u);
EXPECT_THAT( EXPECT_EQ(frame_info->render_size_offset_bits, 64u);
frame_info->render_size_position,
Optional(AllOf(
Field(&Vp9UncompressedHeader::BitstreamPosition::byte_offset, 8u),
Field(&Vp9UncompressedHeader::BitstreamPosition::bit_offset, 0u))));
EXPECT_EQ(frame_info->compressed_header_size, 23u); EXPECT_EQ(frame_info->compressed_header_size, 23u);
EXPECT_EQ(frame_info->uncompressed_header_size, 37u); EXPECT_EQ(frame_info->uncompressed_header_size, 37u);