mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00
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:
parent
7cca016721
commit
faba0fa1c7
4 changed files with 204 additions and 450 deletions
|
@ -355,6 +355,7 @@ rtc_library("video_coding_utility") {
|
|||
"../../api/video_codecs:video_codecs_api",
|
||||
"../../common_video",
|
||||
"../../modules/rtp_rtcp",
|
||||
"../../rtc_base:bitstream_reader",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../rtc_base:rtc_numerics",
|
||||
|
@ -374,6 +375,7 @@ rtc_library("video_coding_utility") {
|
|||
"../rtp_rtcp:rtp_rtcp_format",
|
||||
]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/numeric:bits",
|
||||
"//third_party/abseil-cpp/absl/strings:strings",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
]
|
||||
|
|
|
@ -9,30 +9,13 @@
|
|||
*/
|
||||
#include "modules/video_coding/utility/vp9_uncompressed_header_parser.h"
|
||||
|
||||
#include "absl/numeric/bits.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/strings/string_builder.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 {
|
||||
const size_t kVp9NumRefsPerFrame = 3;
|
||||
const size_t kVp9MaxRefLFDeltas = 4;
|
||||
|
@ -40,169 +23,32 @@ 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); }
|
||||
|
||||
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,
|
||||
void Vp9ReadColorConfig(BitstreamReader& br,
|
||||
Vp9UncompressedHeader* 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 ? Vp9BitDept::k12Bit : Vp9BitDept::k10Bit;
|
||||
});
|
||||
br.Read<bool>() ? Vp9BitDept::k12Bit : Vp9BitDept::k10Bit;
|
||||
} else {
|
||||
frame_info->bit_detph = Vp9BitDept::k8Bit;
|
||||
}
|
||||
|
||||
READ_OR_RETURN(
|
||||
br->ReadUnsigned<uint8_t>(3), [frame_info](uint8_t color_space) {
|
||||
frame_info->color_space = static_cast<Vp9ColorSpace>(color_space);
|
||||
});
|
||||
frame_info->color_space = static_cast<Vp9ColorSpace>(br.ReadBits(3));
|
||||
|
||||
if (frame_info->color_space != Vp9ColorSpace::CS_RGB) {
|
||||
READ_OR_RETURN(br->ReadBoolean(), [frame_info](bool color_range) {
|
||||
frame_info->color_range =
|
||||
color_range ? Vp9ColorRange::kFull : Vp9ColorRange::kStudio;
|
||||
});
|
||||
br.Read<bool>() ? Vp9ColorRange::kFull : Vp9ColorRange::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 = 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;
|
||||
}
|
||||
});
|
||||
static constexpr Vp9YuvSubsampling kSubSamplings[] = {
|
||||
Vp9YuvSubsampling::k444, Vp9YuvSubsampling::k440,
|
||||
Vp9YuvSubsampling::k422, Vp9YuvSubsampling::k420};
|
||||
frame_info->sub_sampling = kSubSamplings[br.ReadBits(2)];
|
||||
|
||||
RETURN_IF_FALSE(br->VerifyNextBooleanIs(
|
||||
0, "Failed to parse header. Reserved bit set."));
|
||||
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;
|
||||
|
@ -212,206 +58,158 @@ bool Vp9ReadColorConfig(BitstreamReader* br,
|
|||
frame_info->color_range = Vp9ColorRange::kFull;
|
||||
if (frame_info->profile == 1 || frame_info->profile == 3) {
|
||||
frame_info->sub_sampling = Vp9YuvSubsampling::k444;
|
||||
RETURN_IF_FALSE(br->VerifyNextBooleanIs(
|
||||
0, "Failed to parse header. Reserved bit set."));
|
||||
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.";
|
||||
return false;
|
||||
br.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadRefreshFrameFlags(BitstreamReader* br,
|
||||
void ReadRefreshFrameFlags(BitstreamReader& br,
|
||||
Vp9UncompressedHeader* frame_info) {
|
||||
// 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) {
|
||||
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.
|
||||
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;
|
||||
frame_info->frame_width = br.Read<uint16_t>() + 1;
|
||||
frame_info->frame_height = br.Read<uint16_t>() + 1;
|
||||
}
|
||||
|
||||
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
|
||||
return br->IfNextBoolean(
|
||||
[&] {
|
||||
auto& pos = frame_info->render_size_position.emplace();
|
||||
br->GetPosition(&pos.byte_offset, &pos.bit_offset);
|
||||
if (br.Read<bool>()) {
|
||||
frame_info->render_size_offset_bits =
|
||||
total_buffer_size_bits - br.RemainingBitCount();
|
||||
// 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_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;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool Vp9ReadFrameSizeFromRefs(BitstreamReader* br,
|
||||
void Vp9ReadFrameSizeFromRefs(BitstreamReader& br,
|
||||
Vp9UncompressedHeader* frame_info) {
|
||||
bool found_ref = false;
|
||||
for (size_t i = 0; !found_ref && i < kVp9NumRefsPerFrame; i++) {
|
||||
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
|
||||
// Size in refs.
|
||||
br->IfNextBoolean([&] {
|
||||
if (br.Read<bool>()) {
|
||||
frame_info->infer_size_from_reference = frame_info->reference_buffers[i];
|
||||
found_ref = true;
|
||||
return true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// 3 bits: sharpness level.
|
||||
RETURN_IF_FALSE(br->ConsumeBits(9));
|
||||
br.ConsumeBits(9);
|
||||
|
||||
if (!br.Read<bool>()) { // mode_ref_delta_enabled
|
||||
return;
|
||||
}
|
||||
if (!br.Read<bool>()) { // mode_ref_delta_update
|
||||
return;
|
||||
}
|
||||
|
||||
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); }));
|
||||
if (br.Read<bool>()) { // update_ref_delta
|
||||
br.ConsumeBits(7);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); }));
|
||||
if (br.Read<bool>()) { // update_mode_delta
|
||||
br.ConsumeBits(7);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool Vp9ReadQp(BitstreamReader* br, Vp9UncompressedHeader* frame_info) {
|
||||
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(),
|
||||
[frame_info](uint8_t qp) { frame_info->base_qp = qp; });
|
||||
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) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] { // if delta_coded
|
||||
READ_OR_RETURN(br->ReadUnsigned<int>(4), [&](int delta) {
|
||||
if (delta != 0) {
|
||||
if (br.Read<bool>()) { // if delta_coded
|
||||
if (br.ReadBits(4) != 0) {
|
||||
frame_info->is_lossless = false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Vp9ReadSegmentationParams(BitstreamReader* br,
|
||||
void Vp9ReadSegmentationParams(BitstreamReader& br,
|
||||
Vp9UncompressedHeader* frame_info) {
|
||||
constexpr int kSegmentationFeatureBits[kVp9SegLvlMax] = {8, 6, 2, 0};
|
||||
constexpr bool kSegmentationFeatureSigned[kVp9SegLvlMax] = {1, 1, 0, 0};
|
||||
|
||||
return br->IfNextBoolean([&] { // segmentation_enabled
|
||||
frame_info->segmentation_enabled = true;
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] { // update_map
|
||||
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) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean(
|
||||
[&] {
|
||||
READ_OR_RETURN(br->ReadUnsigned<uint8_t>(), [&](uint8_t prob) {
|
||||
(*frame_info->segmentation_tree_probs)[i] = prob;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&] {
|
||||
if (br.Read<bool>()) {
|
||||
(*frame_info->segmentation_tree_probs)[i] = br.Read<uint8_t>();
|
||||
} else {
|
||||
(*frame_info->segmentation_tree_probs)[i] = 255;
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// temporal_update
|
||||
frame_info->segmentation_pred_prob.emplace();
|
||||
return br->IfNextBoolean(
|
||||
[&] {
|
||||
if (br.Read<bool>()) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean(
|
||||
[&] {
|
||||
READ_OR_RETURN(
|
||||
br->ReadUnsigned<uint8_t>(), [&](uint8_t prob) {
|
||||
(*frame_info->segmentation_pred_prob)[i] = prob;
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&] {
|
||||
if (br.Read<bool>()) {
|
||||
(*frame_info->segmentation_pred_prob)[i] = br.Read<uint8_t>();
|
||||
} else {
|
||||
(*frame_info->segmentation_pred_prob)[i] = 255;
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[&] {
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] { // feature_enabled
|
||||
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;
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
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] =
|
||||
br.ReadBits(kSegmentationFeatureBits[j]);
|
||||
if (kSegmentationFeatureSigned[j] && br.Read<bool>()) {
|
||||
(*frame_info->segmentation_features[i][j]) *= -1;
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 sb64_cols = (mi_cols + 7) >> 3;
|
||||
|
||||
|
@ -427,27 +225,20 @@ bool Vp9ReadTileInfo(BitstreamReader* br, Vp9UncompressedHeader* frame_info) {
|
|||
--max_log2;
|
||||
|
||||
frame_info->tile_cols_log2 = min_log2;
|
||||
bool done = false;
|
||||
while (!done && frame_info->tile_cols_log2 < max_log2) {
|
||||
RETURN_IF_FALSE(br->IfNextBoolean(
|
||||
[&] {
|
||||
while (frame_info->tile_cols_log2 < max_log2) {
|
||||
if (br.Read<bool>()) {
|
||||
++frame_info->tile_cols_log2;
|
||||
return true;
|
||||
},
|
||||
[&] {
|
||||
done = true;
|
||||
return true;
|
||||
}));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
frame_info->tile_rows_log2 = 0;
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] {
|
||||
if (br.Read<bool>()) {
|
||||
++frame_info->tile_rows_log2;
|
||||
return br->IfNextBoolean([&] {
|
||||
if (br.Read<bool>()) {
|
||||
++frame_info->tile_rows_log2;
|
||||
return true;
|
||||
});
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Vp9InterpolationFilter kLiteralToType[4] = {
|
||||
|
@ -586,60 +377,49 @@ std::string Vp9UncompressedHeader::ToString() const {
|
|||
return oss.str();
|
||||
}
|
||||
|
||||
bool Parse(rtc::ArrayView<const uint8_t> buf,
|
||||
void Parse(BitstreamReader& br,
|
||||
Vp9UncompressedHeader* frame_info,
|
||||
bool qp_only) {
|
||||
rtc::BitBuffer bit_buffer(buf.data(), buf.size());
|
||||
BitstreamReader br(&bit_buffer);
|
||||
const size_t total_buffer_size_bits = br.RemainingBitCount();
|
||||
|
||||
// Frame marker.
|
||||
RETURN_IF_FALSE(br.VerifyNextUnsignedIs(
|
||||
2, 0x2, "Failed to parse header. Frame marker should be 2."));
|
||||
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.
|
||||
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."));
|
||||
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.
|
||||
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;
|
||||
if (br.Read<bool>()) {
|
||||
frame_info->show_existing_frame = br.ReadBits(3);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
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) {
|
||||
RETURN_IF_FALSE(br.VerifyNextUnsignedIs(
|
||||
24, 0x498342, "Failed to get QP. Invalid sync code."));
|
||||
if (br.ReadBits(24) != 0x498342) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code.";
|
||||
br.Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Vp9ReadColorConfig(&br, frame_info))
|
||||
return false;
|
||||
if (!Vp9ReadFrameSize(&br, frame_info))
|
||||
return false;
|
||||
if (!Vp9ReadRenderSize(&br, frame_info))
|
||||
return false;
|
||||
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();
|
||||
|
@ -647,108 +427,86 @@ bool Parse(rtc::ArrayView<const uint8_t> buf,
|
|||
// 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; });
|
||||
is_intra_only = br.Read<bool>();
|
||||
}
|
||||
if (!frame_info->error_resilient) {
|
||||
RETURN_IF_FALSE(br.ConsumeBits(2)); // Reset frame context.
|
||||
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 (br.ReadBits(24) != 0x498342) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse header. Invalid sync code.";
|
||||
br.Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame_info->profile > 0) {
|
||||
if (!Vp9ReadColorConfig(&br, frame_info))
|
||||
return false;
|
||||
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);
|
||||
RETURN_IF_FALSE(ReadRefreshFrameFlags(&br, frame_info));
|
||||
RETURN_IF_FALSE(Vp9ReadFrameSize(&br, frame_info));
|
||||
RETURN_IF_FALSE(Vp9ReadRenderSize(&br, frame_info));
|
||||
ReadRefreshFrameFlags(br, frame_info);
|
||||
Vp9ReadFrameSize(br, frame_info);
|
||||
Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info);
|
||||
} else {
|
||||
RETURN_IF_FALSE(ReadRefreshFrameFlags(&br, frame_info));
|
||||
ReadRefreshFrameFlags(br, frame_info);
|
||||
|
||||
frame_info->reference_buffers_sign_bias[0] = false;
|
||||
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
|
||||
READ_OR_RETURN(br.ReadUnsigned<uint8_t>(3), [&](uint8_t idx) {
|
||||
frame_info->reference_buffers[i] = idx;
|
||||
});
|
||||
READ_OR_RETURN(br.ReadBoolean(), [&](bool sign_bias) {
|
||||
frame_info
|
||||
->reference_buffers_sign_bias[Vp9ReferenceFrame::kLast + i] =
|
||||
sign_bias;
|
||||
});
|
||||
frame_info->reference_buffers[i] = br.ReadBits(3);
|
||||
frame_info->reference_buffers_sign_bias[Vp9ReferenceFrame::kLast + i] =
|
||||
br.Read<bool>();
|
||||
}
|
||||
|
||||
if (!Vp9ReadFrameSizeFromRefs(&br, frame_info))
|
||||
return false;
|
||||
Vp9ReadFrameSizeFromRefs(br, frame_info);
|
||||
Vp9ReadRenderSize(total_buffer_size_bits, br, frame_info);
|
||||
|
||||
READ_OR_RETURN(br.ReadBoolean(), [&](bool allow_high_precision_mv) {
|
||||
frame_info->allow_high_precision_mv = allow_high_precision_mv;
|
||||
});
|
||||
frame_info->allow_high_precision_mv = br.Read<bool>();
|
||||
|
||||
// Interpolation filter.
|
||||
RETURN_IF_FALSE(br.IfNextBoolean(
|
||||
[frame_info] {
|
||||
frame_info->interpolation_filter =
|
||||
Vp9InterpolationFilter::kSwitchable;
|
||||
return true;
|
||||
},
|
||||
[&] {
|
||||
READ_OR_RETURN(
|
||||
br.ReadUnsigned<uint8_t>(2), [frame_info](uint8_t filter) {
|
||||
frame_info->interpolation_filter = kLiteralToType[filter];
|
||||
});
|
||||
return true;
|
||||
}));
|
||||
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.
|
||||
RETURN_IF_FALSE(br.ConsumeBits(2));
|
||||
br.ConsumeBits(2);
|
||||
}
|
||||
|
||||
// Frame context index.
|
||||
READ_OR_RETURN(br.ReadUnsigned<uint8_t>(2),
|
||||
[&](uint8_t idx) { frame_info->frame_context_idx = idx; });
|
||||
frame_info->frame_context_idx = br.ReadBits(2);
|
||||
|
||||
if (!Vp9ReadLoopfilter(&br))
|
||||
return false;
|
||||
Vp9ReadLoopfilter(br);
|
||||
|
||||
// Read base QP.
|
||||
RETURN_IF_FALSE(Vp9ReadQp(&br, frame_info));
|
||||
Vp9ReadQp(br, frame_info);
|
||||
|
||||
if (qp_only) {
|
||||
// Not interested in the rest of the header, return early.
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
RETURN_IF_FALSE(Vp9ReadSegmentationParams(&br, frame_info));
|
||||
RETURN_IF_FALSE(Vp9ReadTileInfo(&br, frame_info));
|
||||
READ_OR_RETURN(br.ReadUnsigned<uint16_t>(), [frame_info](uint16_t size) {
|
||||
frame_info->compressed_header_size = size;
|
||||
});
|
||||
|
||||
// Trailing bits.
|
||||
RETURN_IF_FALSE(br.ConsumeBits(bit_buffer.RemainingBitCount() % 8));
|
||||
Vp9ReadSegmentationParams(br, frame_info);
|
||||
Vp9ReadTileInfo(br, frame_info);
|
||||
frame_info->compressed_header_size = br.Read<uint16_t>();
|
||||
frame_info->uncompressed_header_size =
|
||||
buf.size() - (bit_buffer.RemainingBitCount() / 8);
|
||||
|
||||
return true;
|
||||
(total_buffer_size_bits / 8) - (br.RemainingBitCount() / 8);
|
||||
}
|
||||
|
||||
absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header(
|
||||
rtc::ArrayView<const uint8_t> buf) {
|
||||
BitstreamReader reader(buf);
|
||||
Vp9UncompressedHeader frame_info;
|
||||
if (Parse(buf, &frame_info, /*qp_only=*/false) &&
|
||||
frame_info.frame_width > 0) {
|
||||
Parse(reader, &frame_info, /*qp_only=*/false);
|
||||
if (reader.Ok() && frame_info.frame_width > 0) {
|
||||
return frame_info;
|
||||
}
|
||||
return absl::nullopt;
|
||||
|
@ -757,8 +515,10 @@ absl::optional<Vp9UncompressedHeader> ParseUncompressedVp9Header(
|
|||
namespace vp9 {
|
||||
|
||||
bool GetQp(const uint8_t* buf, size_t length, int* qp) {
|
||||
BitstreamReader reader(rtc::MakeArrayView(buf, length));
|
||||
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;
|
||||
}
|
||||
*qp = frame_info.base_qp;
|
||||
|
|
|
@ -105,11 +105,7 @@ struct Vp9UncompressedHeader {
|
|||
// 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_rows_log2 = 0; // tile_rows = 1 << tile_rows_log2
|
||||
struct BitstreamPosition {
|
||||
size_t byte_offset = 0;
|
||||
size_t bit_offset = 0;
|
||||
};
|
||||
absl::optional<BitstreamPosition> render_size_position;
|
||||
absl::optional<size_t> render_size_offset_bits;
|
||||
Vp9InterpolationFilter interpolation_filter =
|
||||
Vp9InterpolationFilter::kEightTap;
|
||||
bool allow_high_precision_mv = false;
|
||||
|
|
|
@ -55,11 +55,7 @@ TEST(Vp9UncompressedHeaderParserTest, FrameWithSegmentation) {
|
|||
EXPECT_EQ(frame_info->updated_buffers, 0b10000000);
|
||||
EXPECT_EQ(frame_info->tile_cols_log2, 0u);
|
||||
EXPECT_EQ(frame_info->tile_rows_log2, 0u);
|
||||
EXPECT_THAT(
|
||||
frame_info->render_size_position,
|
||||
Optional(AllOf(
|
||||
Field(&Vp9UncompressedHeader::BitstreamPosition::byte_offset, 8u),
|
||||
Field(&Vp9UncompressedHeader::BitstreamPosition::bit_offset, 0u))));
|
||||
EXPECT_EQ(frame_info->render_size_offset_bits, 64u);
|
||||
EXPECT_EQ(frame_info->compressed_header_size, 23u);
|
||||
EXPECT_EQ(frame_info->uncompressed_header_size, 37u);
|
||||
|
||||
|
|
Loading…
Reference in a new issue