Refactors Vp9UncompressedHeaderParser.
Biggest change is a new helper class used to read data from the bitstream and then pass the result to a function if reading was successful. There's also helper to do if/else flow based on the read values. This avoids a bunch of temporaries and in my view makes the code esaier to read. For example, this block: uint32_t bit; RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1)); if (bit) { RETURN_FALSE_IF_ERROR(br->ConsumeBits(7)); } ...is now written as: RETURN_IF_FALSE( br->IfNextBoolean([br] { return br->ConsumeBits(7); })); In addition, we parse and put a few extra things in FrameInfo: show_existing_frame, is_keyframe, and base_qp. Bug: webrtc:12354 Change-Id: Ia0b707b223a1afe0a4521ce2b995437d41243c06 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/215239 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33776}
This commit is contained in:
@ -369,7 +369,10 @@ rtc_library("video_coding_utility") {
|
||||
"../../system_wrappers:field_trial",
|
||||
"../rtp_rtcp:rtp_rtcp_format",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/strings:strings",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("webrtc_h264") {
|
||||
|
@ -9,90 +9,195 @@
|
||||
*/
|
||||
#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 {
|
||||
|
||||
#define RETURN_FALSE_IF_ERROR(x) \
|
||||
// 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;
|
||||
|
||||
bool Vp9ReadProfile(rtc::BitBuffer* br, uint8_t* profile) {
|
||||
uint32_t high_bit;
|
||||
uint32_t low_bit;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&low_bit, 1));
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&high_bit, 1));
|
||||
*profile = (high_bit << 1) + low_bit;
|
||||
if (*profile > 2) {
|
||||
uint32_t reserved_bit;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
||||
if (reserved_bit) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to get QP. Unsupported bitstream profile.";
|
||||
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(&val, 1)) {
|
||||
return false;
|
||||
}
|
||||
if (val != 0) {
|
||||
return f_true();
|
||||
}
|
||||
return true;
|
||||
return f_false();
|
||||
}
|
||||
|
||||
bool Vp9ReadSyncCode(rtc::BitBuffer* br) {
|
||||
uint32_t sync_code;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&sync_code, 24));
|
||||
if (sync_code != 0x498342) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to get QP. Invalid sync code.";
|
||||
absl::optional<bool> ReadBoolean() {
|
||||
uint32_t val;
|
||||
if (!buffer_->ReadBits(&val, 1)) {
|
||||
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(&val, 1)) {
|
||||
return false;
|
||||
}
|
||||
if ((val != 0) != expected_val) {
|
||||
if (!error_msg.empty()) {
|
||||
RTC_LOG(LS_WARNING) << error_msg;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Vp9ReadColorConfig(rtc::BitBuffer* br,
|
||||
uint8_t profile,
|
||||
FrameInfo* frame_info) {
|
||||
if (profile == 0 || profile == 1) {
|
||||
frame_info->bit_detph = BitDept::k8Bit;
|
||||
} else if (profile == 2 || profile == 3) {
|
||||
uint32_t ten_or_twelve_bits;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&ten_or_twelve_bits, 1));
|
||||
// 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(&val, bits)) {
|
||||
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(&val, num_bits)) {
|
||||
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(&sign, 1)) {
|
||||
return {};
|
||||
}
|
||||
uint32_t val;
|
||||
if (!buffer_->ReadBits(&val, bits)) {
|
||||
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;
|
||||
}
|
||||
uint32_t color_space;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&color_space, 3));
|
||||
frame_info->color_space = static_cast<ColorSpace>(color_space);
|
||||
|
||||
// SRGB is 7.
|
||||
if (color_space != 7) {
|
||||
uint32_t color_range;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&color_range, 1));
|
||||
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 (profile == 1 || profile == 3) {
|
||||
uint32_t subsampling_x;
|
||||
uint32_t subsampling_y;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&subsampling_x, 1));
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&subsampling_y, 1));
|
||||
if (subsampling_x) {
|
||||
frame_info->sub_sampling =
|
||||
subsampling_y ? YuvSubsampling::k420 : YuvSubsampling::k422;
|
||||
} else {
|
||||
frame_info->sub_sampling =
|
||||
subsampling_y ? YuvSubsampling::k440 : YuvSubsampling::k444;
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
uint32_t reserved_bit;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
||||
if (reserved_bit) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
|
||||
return false;
|
||||
}
|
||||
RETURN_IF_FALSE(br->VerifyNextBooleanIs(
|
||||
0, "Failed to parse header. Reserved bit set."));
|
||||
} else {
|
||||
// Profile 0 or 2.
|
||||
frame_info->sub_sampling = YuvSubsampling::k420;
|
||||
@ -100,14 +205,10 @@ bool Vp9ReadColorConfig(rtc::BitBuffer* br,
|
||||
} else {
|
||||
// SRGB
|
||||
frame_info->color_range = ColorRange::kFull;
|
||||
if (profile == 1 || profile == 3) {
|
||||
if (frame_info->profile == 1 || frame_info->profile == 3) {
|
||||
frame_info->sub_sampling = YuvSubsampling::k444;
|
||||
uint32_t reserved_bit;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&reserved_bit, 1));
|
||||
if (reserved_bit) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse header. Reserved bit set.";
|
||||
return false;
|
||||
}
|
||||
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.";
|
||||
@ -118,44 +219,45 @@ bool Vp9ReadColorConfig(rtc::BitBuffer* br,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Vp9ReadFrameSize(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
||||
// 16 bits: frame width - 1.
|
||||
uint16_t frame_width_minus_one;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&frame_width_minus_one));
|
||||
// 16 bits: frame height - 1.
|
||||
uint16_t frame_height_minus_one;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&frame_height_minus_one));
|
||||
frame_info->frame_width = frame_width_minus_one + 1;
|
||||
frame_info->frame_height = frame_height_minus_one + 1;
|
||||
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(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
||||
uint32_t render_and_frame_size_different;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&render_and_frame_size_different, 1));
|
||||
if (render_and_frame_size_different) {
|
||||
// 16 bits: render width - 1.
|
||||
uint16_t render_width_minus_one;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&render_width_minus_one));
|
||||
// 16 bits: render height - 1.
|
||||
uint16_t render_height_minus_one;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadUInt16(&render_height_minus_one));
|
||||
frame_info->render_width = render_width_minus_one + 1;
|
||||
frame_info->render_height = render_height_minus_one + 1;
|
||||
} else {
|
||||
frame_info->render_width = frame_info->frame_width;
|
||||
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(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
||||
uint32_t found_ref = 0;
|
||||
for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) {
|
||||
bool Vp9ReadFrameSizeFromRefs(BitstreamReader* br, FrameInfo* frame_info) {
|
||||
bool found_ref = false;
|
||||
for (size_t i = 0; !found_ref && i < kVp9NumRefsPerFrame; i++) {
|
||||
// Size in refs.
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&found_ref, 1));
|
||||
if (found_ref)
|
||||
break;
|
||||
READ_OR_RETURN(br->ReadBoolean(), [&](bool ref) { found_ref = ref; });
|
||||
}
|
||||
|
||||
if (!found_ref) {
|
||||
@ -166,83 +268,156 @@ bool Vp9ReadFrameSizeFromRefs(rtc::BitBuffer* br, FrameInfo* frame_info) {
|
||||
return Vp9ReadRenderSize(br, frame_info);
|
||||
}
|
||||
|
||||
bool Vp9ReadInterpolationFilter(rtc::BitBuffer* br) {
|
||||
uint32_t bit;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
||||
if (bit)
|
||||
return true;
|
||||
|
||||
return br->ConsumeBits(2);
|
||||
}
|
||||
|
||||
bool Vp9ReadLoopfilter(rtc::BitBuffer* br) {
|
||||
bool Vp9ReadLoopfilter(BitstreamReader* br) {
|
||||
// 6 bits: filter level.
|
||||
// 3 bits: sharpness level.
|
||||
RETURN_FALSE_IF_ERROR(br->ConsumeBits(9));
|
||||
RETURN_IF_FALSE(br->ConsumeBits(9));
|
||||
|
||||
uint32_t mode_ref_delta_enabled;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_enabled, 1));
|
||||
if (mode_ref_delta_enabled) {
|
||||
uint32_t mode_ref_delta_update;
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&mode_ref_delta_update, 1));
|
||||
if (mode_ref_delta_update) {
|
||||
uint32_t bit;
|
||||
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_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
||||
if (bit) {
|
||||
RETURN_FALSE_IF_ERROR(br->ConsumeBits(7));
|
||||
}
|
||||
RETURN_IF_FALSE(br->IfNextBoolean([&] { return br->ConsumeBits(7); }));
|
||||
}
|
||||
for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) {
|
||||
RETURN_FALSE_IF_ERROR(br->ReadBits(&bit, 1));
|
||||
if (bit) {
|
||||
RETURN_FALSE_IF_ERROR(br->ConsumeBits(7));
|
||||
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 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, int* qp, FrameInfo* frame_info) {
|
||||
rtc::BitBuffer br(buf, length);
|
||||
bool Parse(const uint8_t* buf, size_t length, FrameInfo* frame_info) {
|
||||
rtc::BitBuffer bit_buffer(buf, length);
|
||||
BitstreamReader br(&bit_buffer);
|
||||
|
||||
// Frame marker.
|
||||
uint32_t frame_marker;
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_marker, 2));
|
||||
if (frame_marker != 0x2) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to parse header. Frame marker should be 2.";
|
||||
return false;
|
||||
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."));
|
||||
}
|
||||
|
||||
// Profile.
|
||||
uint8_t profile;
|
||||
if (!Vp9ReadProfile(&br, &profile))
|
||||
return false;
|
||||
frame_info->profile = profile;
|
||||
|
||||
// Show existing frame.
|
||||
uint32_t show_existing_frame;
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&show_existing_frame, 1));
|
||||
if (show_existing_frame)
|
||||
return false;
|
||||
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).
|
||||
uint32_t frame_type;
|
||||
uint32_t show_frame;
|
||||
uint32_t error_resilient;
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&frame_type, 1));
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&show_frame, 1));
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&error_resilient, 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_type == 0) {
|
||||
// Key-frame.
|
||||
if (!Vp9ReadSyncCode(&br))
|
||||
return false;
|
||||
if (!Vp9ReadColorConfig(&br, profile, frame_info))
|
||||
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;
|
||||
@ -250,76 +425,92 @@ bool Parse(const uint8_t* buf, size_t length, int* qp, FrameInfo* frame_info) {
|
||||
return false;
|
||||
} else {
|
||||
// Non-keyframe.
|
||||
uint32_t intra_only = 0;
|
||||
if (!show_frame)
|
||||
RETURN_FALSE_IF_ERROR(br.ReadBits(&intra_only, 1));
|
||||
if (!error_resilient)
|
||||
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2)); // Reset frame context.
|
||||
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 (intra_only) {
|
||||
if (!Vp9ReadSyncCode(&br))
|
||||
return false;
|
||||
if (is_intra_only) {
|
||||
RETURN_IF_FALSE(br.VerifyNextUnsignedIs(
|
||||
24, 0x498342, "Failed to get QP. Invalid sync code."));
|
||||
|
||||
if (profile > 0) {
|
||||
if (!Vp9ReadColorConfig(&br, profile, frame_info))
|
||||
if (frame_info->profile > 0) {
|
||||
if (!Vp9ReadColorConfig(&br, frame_info))
|
||||
return false;
|
||||
}
|
||||
// Refresh frame flags.
|
||||
RETURN_FALSE_IF_ERROR(br.ConsumeBits(8));
|
||||
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_FALSE_IF_ERROR(br.ConsumeBits(8));
|
||||
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_FALSE_IF_ERROR(br.ConsumeBits(4));
|
||||
RETURN_IF_FALSE(br.ConsumeBits(4));
|
||||
}
|
||||
|
||||
if (!Vp9ReadFrameSizeFromRefs(&br, frame_info))
|
||||
return false;
|
||||
|
||||
// Allow high precision mv.
|
||||
RETURN_FALSE_IF_ERROR(br.ConsumeBits(1));
|
||||
RETURN_IF_FALSE(br.ConsumeBits(1));
|
||||
// Interpolation filter.
|
||||
if (!Vp9ReadInterpolationFilter(&br))
|
||||
return false;
|
||||
RETURN_IF_FALSE(br.IfNextBoolean([] { return true; },
|
||||
[&br] { return br.ConsumeBits(2); }));
|
||||
}
|
||||
}
|
||||
|
||||
if (!error_resilient) {
|
||||
if (!frame_info->error_resilient) {
|
||||
// 1 bit: Refresh frame context.
|
||||
// 1 bit: Frame parallel decoding mode.
|
||||
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2));
|
||||
RETURN_IF_FALSE(br.ConsumeBits(2));
|
||||
}
|
||||
|
||||
// Frame context index.
|
||||
RETURN_FALSE_IF_ERROR(br.ConsumeBits(2));
|
||||
RETURN_IF_FALSE(br.ConsumeBits(2));
|
||||
|
||||
if (!Vp9ReadLoopfilter(&br))
|
||||
return false;
|
||||
|
||||
// Base QP.
|
||||
uint8_t base_q0;
|
||||
RETURN_FALSE_IF_ERROR(br.ReadUInt8(&base_q0));
|
||||
*qp = base_q0;
|
||||
// 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;
|
||||
return Parse(buf, length, qp, &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) {
|
||||
int qp = 0;
|
||||
FrameInfo frame_info;
|
||||
if (Parse(buf, length, &qp, &frame_info) && frame_info.frame_width > 0) {
|
||||
if (Parse(buf, length, &frame_info) && frame_info.frame_width > 0) {
|
||||
return frame_info;
|
||||
}
|
||||
return absl::nullopt;
|
||||
|
@ -65,6 +65,8 @@ enum class YuvSubsampling {
|
||||
|
||||
struct FrameInfo {
|
||||
int profile = 0; // Profile 0-3 are valid.
|
||||
absl::optional<uint8_t> show_existing_frame;
|
||||
bool is_keyframe = false;
|
||||
bool show_frame = false;
|
||||
bool error_resilient = false;
|
||||
BitDept bit_detph = BitDept::k8Bit;
|
||||
@ -75,6 +77,7 @@ struct FrameInfo {
|
||||
int frame_height = 0;
|
||||
int render_width = 0;
|
||||
int render_height = 0;
|
||||
int base_qp = 0;
|
||||
};
|
||||
|
||||
// Parses frame information for a VP9 key-frame or all-intra frame from a
|
||||
|
Reference in New Issue
Block a user