From 20acdf24437319dbd2832a4468b6ec606b25c07c Mon Sep 17 00:00:00 2001 From: jianj Date: Wed, 24 May 2017 10:00:16 -0700 Subject: [PATCH] Add vp9 QP parser. BUG=webrtc:7662 Review-Url: https://codereview.webrtc.org/2891803003 Cr-Commit-Position: refs/heads/master@{#18260} --- webrtc/modules/video_coding/BUILD.gn | 2 + webrtc/modules/video_coding/qp_parser.cc | 4 + .../utility/vp9_uncompressed_header_parser.cc | 236 ++++++++++++++++++ .../utility/vp9_uncompressed_header_parser.h | 61 +++++ 4 files changed, 303 insertions(+) create mode 100644 webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc create mode 100644 webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn index 19942a12fc..22db6e6974 100644 --- a/webrtc/modules/video_coding/BUILD.gn +++ b/webrtc/modules/video_coding/BUILD.gn @@ -122,6 +122,8 @@ rtc_static_library("video_coding_utility") { "utility/quality_scaler.h", "utility/vp8_header_parser.cc", "utility/vp8_header_parser.h", + "utility/vp9_uncompressed_header_parser.cc", + "utility/vp9_uncompressed_header_parser.h", ] if (!build_with_chromium && is_clang) { diff --git a/webrtc/modules/video_coding/qp_parser.cc b/webrtc/modules/video_coding/qp_parser.cc index 976cfff963..26b46f17fb 100644 --- a/webrtc/modules/video_coding/qp_parser.cc +++ b/webrtc/modules/video_coding/qp_parser.cc @@ -12,6 +12,7 @@ #include "webrtc/common_types.h" #include "webrtc/modules/video_coding/utility/vp8_header_parser.h" +#include "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h" namespace webrtc { @@ -20,6 +21,9 @@ bool QpParser::GetQp(const VCMEncodedFrame& frame, int* qp) { case kVideoCodecVP8: // QP range: [0, 127]. return vp8::GetQp(frame.Buffer(), frame.Length(), qp); + case kVideoCodecVP9: + // QP range: [0, 255]. + return vp9::GetQp(frame.Buffer(), frame.Length(), qp); default: return false; } diff --git a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc new file mode 100644 index 0000000000..571eaeaba4 --- /dev/null +++ b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.cc @@ -0,0 +1,236 @@ +/* + * 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 "webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h" + +namespace webrtc { + +namespace vp9 { +namespace { +const size_t kVp9MaxProfile = 4; +const size_t kVp9NumRefsPerFrame = 3; +const size_t kVp9MaxRefLFDeltas = 4; +const size_t kVp9MaxModeLFDeltas = 2; +} // namespace + +static uint8_t VP9ReadProfile(VP9BitReader* br) { + uint8_t profile = 0; + if (br->GetBit()) + profile |= 1; + if (br->GetBit()) + profile |= 2; + if (profile > 2 && br->GetBit()) + profile += 1; + return profile; +} + +static bool VP9ReadColorConfig(VP9BitReader* br, uint8_t profile) { + if (profile == 2 || profile == 3) { + // Bitdepth. + br->GetBit(); + } + + uint8_t color_space = br->GetValue(3); + // SRGB is 7. + if (color_space != 7) { + // YUV range flag. + br->GetBit(); + if (profile == 1 || profile == 3) { + // Subsampling x. + br->GetBit(); + // Subsampling y. + br->GetBit(); + // Reserved. + if (br->GetBit()) { + LOG(LS_WARNING) << "Failed to get QP. Reserved bit set."; + return false; + } + } + } else { + if (profile == 1 || profile == 3) { + // Reserved. + if (br->GetBit()) { + LOG(LS_WARNING) << "Failed to get QP. Reserved bit set."; + return false; + } + } else { + LOG(LS_WARNING) << "Failed to get QP. 4:4:4 color not supported in " + "profile 0 or 2."; + return false; + } + } + + return true; +} + +static void VP9ReadFrameSize(VP9BitReader* br) { + // Frame width. + br->GetValue(16); + // Frame height. + br->GetValue(16); +} + +static void VP9ReadRenderSize(VP9BitReader* br) { + // Scaling. + if (br->GetBit()) { + // Render width. + br->GetValue(16); + // Render height. + br->GetValue(16); + } +} + +static void VP9ReadFrameSizeFromRefs(VP9BitReader* br) { + int found_ref = 0; + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + // Size in refs. + found_ref = br->GetBit(); + if (found_ref) + break; + } + + if (!found_ref) + VP9ReadFrameSize(br); + + VP9ReadRenderSize(br); +} + +static void VP9ReadInterpolationFilter(VP9BitReader* br) { + if (br->GetBit()) + return; + + br->GetValue(2); +} + +static void VP9ReadLoopfilter(VP9BitReader* br) { + // Filter level. + br->GetValue(6); + // Sharpness level. + br->GetValue(3); + uint32_t mode_ref_delta_enabled = br->GetBit(); + if (mode_ref_delta_enabled) { + uint32_t mode_ref_delta_update = br->GetBit(); + if (mode_ref_delta_update) { + for (size_t i = 0; i < kVp9MaxRefLFDeltas; i++) { + if (br->GetBit()) + br->GetSignedValue(6); + } + for (size_t i = 0; i < kVp9MaxModeLFDeltas; i++) { + if (br->GetBit()) + br->GetSignedValue(6); + } + } + } +} + +bool GetQp(const uint8_t* buf, size_t length, int* qp) { + VP9BitReader br(buf, length); + + // Frame marker. + if (br.GetValue(2) != 0x2) { + LOG(LS_WARNING) << "Failed to get QP. Frame marker should be 2."; + return false; + } + + // Profile. + uint8_t profile = VP9ReadProfile(&br); + if (profile > kVp9MaxProfile) { + LOG(LS_WARNING) << "Failed to get QP. Unsupported bitstream profile: " + << profile; + return false; + } + + // Show existing frame. + if (br.GetBit()) + return false; + + // Frame type: KEY_FRAME(0), INTER_FRAME(1). + uint8_t frame_type = br.GetBit(); + // Show frame. + uint8_t show_frame = br.GetBit(); + // Error resilient. + uint8_t error_resilient = br.GetBit(); + + if (!frame_type) { + // Sync code. + uint32_t sync_code = br.GetValue(24); + if (sync_code != 0x498342) { + LOG(LS_WARNING) << "Failed to get QP. Invalid sync code."; + return false; + } + + if (!VP9ReadColorConfig(&br, profile)) + return false; + + VP9ReadFrameSize(&br); + VP9ReadRenderSize(&br); + } else { + uint8_t intra_only = 0; + if (!show_frame) + intra_only = br.GetBit(); + + if (!error_resilient) + // Reset frame context. + br.GetValue(2); + + if (intra_only) { + // Sync code. + if (br.GetValue(24) != 0x498342) { + LOG(LS_WARNING) << "Failed to get QP. Invalid sync code."; + return false; + } + if (profile > 0) { + if (!VP9ReadColorConfig(&br, profile)) + return false; + } + // Refresh frame flags. + br.GetValue(8); + + VP9ReadFrameSize(&br); + VP9ReadRenderSize(&br); + } else { + // Refresh frame flags. + br.GetValue(8); + + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + // Ref frame index. + br.GetValue(3); + // Ref frame sign biases. + br.GetBit(); + } + + VP9ReadFrameSizeFromRefs(&br); + // Allow high precision mv. + br.GetBit(); + // Interpolation filter. + VP9ReadInterpolationFilter(&br); + } + } + + if (!error_resilient) { + // Refresh frame context. + br.GetBit(); + // Frame parallel decoding mode. + br.GetBit(); + } + + // Frame context index. + br.GetValue(2); + + VP9ReadLoopfilter(&br); + + // Base QP. + const int base_q0 = br.GetValue(8); + *qp = base_q0; + return true; +} + +} // namespace vp9 + +} // namespace webrtc diff --git a/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h new file mode 100644 index 0000000000..ef6f268e51 --- /dev/null +++ b/webrtc/modules/video_coding/utility/vp9_uncompressed_header_parser.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_VP9_UNCOMPRESSED_HEADER_PARSER_H_ +#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_VP9_UNCOMPRESSED_HEADER_PARSER_H_ + +#include +#include + +#include "webrtc/base/bitbuffer.h" +#include "webrtc/base/logging.h" + +namespace webrtc { + +namespace vp9 { + +class VP9BitReader : public ::rtc::BitBuffer { + public: + VP9BitReader(const uint8_t* buffer, size_t length_) + : BitBuffer(buffer, length_) {} + + uint32_t GetBit() { + uint32_t bit = 0; + if (ReadBits(&bit, 1)) + return bit; + + LOG(LS_WARNING) << "Failed to get bit. Reached EOF."; + return 0; + } + + uint32_t GetValue(int bits) { + uint32_t value = 0; + if (ReadBits(&value, bits)) + return value; + + LOG(LS_WARNING) << "Failed to get bit. Reached EOF."; + return 0; + } + + int32_t GetSignedValue(int bits) { + const int32_t value = static_cast(GetValue(bits)); + return GetBit() ? -value : value; + } +}; + +// Gets the QP, QP range: [0, 255]. +// Returns true on success, false otherwise. +bool GetQp(const uint8_t* buf, size_t length, int* qp); + +} // namespace vp9 + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_VP9_UNCOMPRESSED_HEADER_PARSER_H_