From 98d8cf58ee8c7c7b0672ce7955313a31824d6f3a Mon Sep 17 00:00:00 2001 From: jackychen Date: Thu, 21 May 2015 11:12:02 -0700 Subject: [PATCH] Hardware VP8 encoding: Use QP as metric for resize. Add vp8 frame header parser to get QP from vp8 bitstream. BUG= 4273 R=glaznev@webrtc.org, marpan@google.com, pbos@webrtc.org TBR=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/49259004 Cr-Commit-Position: refs/heads/master@{#9256} --- .../java/jni/androidmediaencoder_jni.cc | 43 ++-- webrtc/modules/video_coding/BUILD.gn | 2 + .../video_coding/codecs/vp8/vp8_impl.cc | 4 +- .../utility/include/quality_scaler.h | 13 +- .../utility/include/vp8_header_parser.h | 75 +++++++ .../video_coding/utility/quality_scaler.cc | 30 +-- .../utility/quality_scaler_unittest.cc | 12 +- .../utility/video_coding_utility.gyp | 4 +- .../video_coding/utility/vp8_header_parser.cc | 188 ++++++++++++++++++ 9 files changed, 305 insertions(+), 66 deletions(-) create mode 100644 webrtc/modules/video_coding/utility/include/vp8_header_parser.h create mode 100644 webrtc/modules/video_coding/utility/vp8_header_parser.cc diff --git a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc index 00e6a381e0..fd496f09a0 100644 --- a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc +++ b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc @@ -35,6 +35,7 @@ #include "webrtc/base/thread.h" #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" #include "webrtc/modules/video_coding/utility/include/quality_scaler.h" +#include "webrtc/modules/video_coding/utility/include/vp8_header_parser.h" #include "webrtc/system_wrappers/interface/logcat_trace_context.h" #include "third_party/libyuv/include/libyuv/convert.h" #include "third_party/libyuv/include/libyuv/convert_from.h" @@ -196,8 +197,6 @@ class MediaCodecVideoEncoder : public webrtc::VideoEncoder, // Global references; must be deleted in Release(). std::vector input_buffers_; scoped_ptr quality_scaler_; - // Target frame size in bytes. - int target_framesize_; // Dynamic resolution change, off by default. bool scale_; }; @@ -280,6 +279,11 @@ int32_t MediaCodecVideoEncoder::InitEncode( size_t /* max_payload_size */) { const int kMinWidth = 320; const int kMinHeight = 180; + // QP is obtained from VP8-bitstream for HW, so the QP corresponds to the + // (internal) range: [0, 127]. And we cannot change QP_max in HW, so it is + // always = 127. Note that in SW, QP is that of the user-level range [0, 63]. + const int kMaxQP = 127; + const int kLowQpThresholdDenominator = 3; if (codec_settings == NULL) { ALOGE("NULL VideoCodec instance"); return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; @@ -290,14 +294,10 @@ int32_t MediaCodecVideoEncoder::InitEncode( ALOGD("InitEncode request"); scale_ = false; - quality_scaler_->Init(0); - quality_scaler_->SetMinResolution(kMinWidth, kMinHeight); - quality_scaler_->ReportFramerate(codec_settings->maxFramerate); - if (codec_settings->maxFramerate > 0) { - target_framesize_ = codec_settings->startBitrate * 1000 / - codec_settings->maxFramerate / 8; - } else { - target_framesize_ = 0; + if (codecType_ == kVideoCodecVP8) { + quality_scaler_->Init(kMaxQP / kLowQpThresholdDenominator); + quality_scaler_->SetMinResolution(kMinWidth, kMinHeight); + quality_scaler_->ReportFramerate(codec_settings->maxFramerate); } return codec_thread_->Invoke( Bind(&MediaCodecVideoEncoder::InitEncodeOnCodecThread, @@ -337,12 +337,8 @@ int32_t MediaCodecVideoEncoder::SetChannelParameters(uint32_t /* packet_loss */, int32_t MediaCodecVideoEncoder::SetRates(uint32_t new_bit_rate, uint32_t frame_rate) { - quality_scaler_->ReportFramerate(frame_rate); - if (frame_rate > 0) { - target_framesize_ = new_bit_rate * 1000 / frame_rate / 8; - } else { - target_framesize_ = 0; - } + if (codecType_ == kVideoCodecVP8) + quality_scaler_->ReportFramerate(frame_rate); return codec_thread_->Invoke( Bind(&MediaCodecVideoEncoder::SetRatesOnCodecThread, this, @@ -716,16 +712,8 @@ bool MediaCodecVideoEncoder::DeliverPendingOutputs(JNIEnv* jni) { last_input_timestamp_ms_ - last_output_timestamp_ms_, frame_encoding_time_ms); - if (payload_size) { - double framesize_deviation = 0.0; - if (target_framesize_ > 0) { - framesize_deviation = - (double)abs((int)payload_size - target_framesize_) / - target_framesize_; - } - quality_scaler_->ReportNormalizedFrameSizeFluctuation( - framesize_deviation); - } + if (payload_size && codecType_ == kVideoCodecVP8) + quality_scaler_->ReportQP(webrtc::vp8::GetQP(payload)); // Calculate and print encoding statistics - every 3 seconds. frames_encoded_++; @@ -872,7 +860,8 @@ int32_t MediaCodecVideoEncoder::NextNaluPosition( } void MediaCodecVideoEncoder::OnDroppedFrame() { - quality_scaler_->ReportDroppedFrame(); + if (codecType_ == kVideoCodecVP8) + quality_scaler_->ReportDroppedFrame(); } MediaCodecVideoEncoderFactory::MediaCodecVideoEncoderFactory() { diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn index 405a173bd1..402502de58 100644 --- a/webrtc/modules/video_coding/BUILD.gn +++ b/webrtc/modules/video_coding/BUILD.gn @@ -96,7 +96,9 @@ source_set("video_coding_utility") { "utility/include/frame_dropper.h", "utility/include/moving_average.h", "utility/include/quality_scaler.h", + "utility/include/vp8_header_parser.h", "utility/quality_scaler.cc", + "utility/vp8_header_parser.cc", ] configs += [ "../..:common_config" ] diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc index 7ab369196a..aa0d163f03 100644 --- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc +++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc @@ -603,7 +603,7 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst, } rps_.Init(); - quality_scaler_.Init(codec_.qpMax); + quality_scaler_.Init(codec_.qpMax / kDefaultLowQpDenominator); quality_scaler_.ReportFramerate(codec_.maxFramerate); return InitAndSetControlSettings(); @@ -1035,7 +1035,7 @@ int VP8EncoderImpl::GetEncodedPartitions( if (encoded_images_[0]._length > 0) { int qp; vpx_codec_control(&encoders_[0], VP8E_GET_LAST_QUANTIZER_64, &qp); - quality_scaler_.ReportNormalizedQP(qp); + quality_scaler_.ReportQP(qp); } else { quality_scaler_.ReportDroppedFrame(); } diff --git a/webrtc/modules/video_coding/utility/include/quality_scaler.h b/webrtc/modules/video_coding/utility/include/quality_scaler.h index 326d887d04..6f2302368c 100644 --- a/webrtc/modules/video_coding/utility/include/quality_scaler.h +++ b/webrtc/modules/video_coding/utility/include/quality_scaler.h @@ -15,6 +15,7 @@ #include "webrtc/modules/video_coding/utility/include/moving_average.h" namespace webrtc { +const int kDefaultLowQpDenominator = 3; class QualityScaler { public: struct Resolution { @@ -23,15 +24,10 @@ class QualityScaler { }; QualityScaler(); - void Init(int max_qp); + void Init(int low_qp_threshold); void SetMinResolution(int min_width, int min_height); void ReportFramerate(int framerate); - - // Report QP for SW encoder, report framesize fluctuation for HW encoder, - // only one of these two functions should be called, framesize fluctuation - // is to be used only if qp isn't available. - void ReportNormalizedQP(int qp); - void ReportNormalizedFrameSizeFluctuation(double framesize_deviation); + void ReportQP(int qp); void ReportDroppedFrame(); void Reset(int framerate, int bitrate, int width, int height); Resolution GetScaledResolution(const I420VideoFrame& frame); @@ -45,10 +41,9 @@ class QualityScaler { I420VideoFrame scaled_frame_; size_t num_samples_; - int target_framesize_; int low_qp_threshold_; MovingAverage framedrop_percent_; - MovingAverage frame_quality_; + MovingAverage average_qp_; int downscale_shift_; int min_width_; diff --git a/webrtc/modules/video_coding/utility/include/vp8_header_parser.h b/webrtc/modules/video_coding/utility/include/vp8_header_parser.h new file mode 100644 index 0000000000..839e0933fc --- /dev/null +++ b/webrtc/modules/video_coding/utility/include/vp8_header_parser.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015 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_VP8_PARSE_HEADER_H_ +#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_VP8_PARSE_HEADER_H_ + +namespace webrtc { + +namespace vp8 { + +enum { + MB_FEATURE_TREE_PROBS = 3, + NUM_MB_SEGMENTS = 4, + NUM_REF_LF_DELTAS = 4, + NUM_MODE_LF_DELTAS = 4, +}; + +typedef struct VP8BitReader VP8BitReader; +struct VP8BitReader { + // Boolean decoder. + uint32_t value_; // Current value. + uint32_t range_; // Current range minus 1. In [127, 254] interval. + int bits_; // Number of valid bits left. + // Read buffer. + const uint8_t* buf_; // Next byte to be read. + const uint8_t* buf_end_; // End of read buffer. + int eof_; // True if input is exhausted. +}; + +const uint8_t kVP8Log2Range[128] = { + 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0 +}; + +// range = ((range - 1) << kVP8Log2Range[range]) + 1 +const uint8_t kVP8NewRange[128] = { + 127, 127, 191, 127, 159, 191, 223, 127, + 143, 159, 175, 191, 207, 223, 239, 127, + 135, 143, 151, 159, 167, 175, 183, 191, + 199, 207, 215, 223, 231, 239, 247, 127, + 131, 135, 139, 143, 147, 151, 155, 159, + 163, 167, 171, 175, 179, 183, 187, 191, + 195, 199, 203, 207, 211, 215, 219, 223, + 227, 231, 235, 239, 243, 247, 251, 127, + 129, 131, 133, 135, 137, 139, 141, 143, + 145, 147, 149, 151, 153, 155, 157, 159, + 161, 163, 165, 167, 169, 171, 173, 175, + 177, 179, 181, 183, 185, 187, 189, 191, + 193, 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, 223, + 225, 227, 229, 231, 233, 235, 237, 239, + 241, 243, 245, 247, 249, 251, 253, 127 +}; + +int GetQP(uint8_t* buf); + +} // namespace vp8 + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_VP8_PARSE_HEADER_H_ diff --git a/webrtc/modules/video_coding/utility/quality_scaler.cc b/webrtc/modules/video_coding/utility/quality_scaler.cc index 38b582b994..50ff58b786 100644 --- a/webrtc/modules/video_coding/utility/quality_scaler.cc +++ b/webrtc/modules/video_coding/utility/quality_scaler.cc @@ -14,17 +14,15 @@ namespace webrtc { static const int kMinFps = 10; static const int kMeasureSeconds = 5; static const int kFramedropPercentThreshold = 60; -static const int kLowQpThresholdDenominator = 3; -static const double kFramesizeFlucThreshold = 0.11; QualityScaler::QualityScaler() : num_samples_(0), low_qp_threshold_(-1), downscale_shift_(0), min_width_(0), min_height_(0) { } -void QualityScaler::Init(int max_qp) { +void QualityScaler::Init(int low_qp_threshold) { ClearSamples(); - low_qp_threshold_ = max_qp / kLowQpThresholdDenominator; + low_qp_threshold_ = low_qp_threshold; } void QualityScaler::SetMinResolution(int min_width, int min_height) { @@ -32,24 +30,15 @@ void QualityScaler::SetMinResolution(int min_width, int min_height) { min_height_ = min_height; } -// TODO(jackychen): target_framesize should be calculated from average bitrate -// in the measured period of time. -// Report framerate(fps) and target_bitrate(kbit/s) to estimate # of samples -// and get target_framesize_. +// Report framerate(fps) to estimate # of samples. void QualityScaler::ReportFramerate(int framerate) { num_samples_ = static_cast( kMeasureSeconds * (framerate < kMinFps ? kMinFps : framerate)); } -void QualityScaler::ReportNormalizedQP(int qp) { +void QualityScaler::ReportQP(int qp) { framedrop_percent_.AddSample(0); - frame_quality_.AddSample(static_cast(qp) / low_qp_threshold_); -} - -void QualityScaler::ReportNormalizedFrameSizeFluctuation( - double framesize_deviation) { - framedrop_percent_.AddSample(0); - frame_quality_.AddSample(framesize_deviation / kFramesizeFlucThreshold); + average_qp_.AddSample(qp); } void QualityScaler::ReportDroppedFrame() { @@ -67,13 +56,12 @@ QualityScaler::Resolution QualityScaler::GetScaledResolution( res.height = frame.height(); // Update scale factor. - int avg_drop; - double avg_quality; + int avg_drop, avg_qp; if (framedrop_percent_.GetAverage(num_samples_, &avg_drop) && avg_drop >= kFramedropPercentThreshold) { AdjustScale(false); - } else if (frame_quality_.GetAverage(num_samples_, &avg_quality) && - avg_quality <= 1.0) { + } else if (average_qp_.GetAverage(num_samples_, &avg_qp) && + avg_qp <= low_qp_threshold_) { AdjustScale(true); } @@ -119,7 +107,7 @@ const I420VideoFrame& QualityScaler::GetScaledFrame( void QualityScaler::ClearSamples() { framedrop_percent_.Reset(); - frame_quality_.Reset(); + average_qp_.Reset(); } void QualityScaler::AdjustScale(bool up) { diff --git a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc index 509b248952..cab7f6a18d 100644 --- a/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc +++ b/webrtc/modules/video_coding/utility/quality_scaler_unittest.cc @@ -31,7 +31,7 @@ class QualityScalerTest : public ::testing::Test { QualityScalerTest() { input_frame_.CreateEmptyFrame( kWidth, kHeight, kWidth, kHalfWidth, kHalfWidth); - qs_.Init(kMaxQp); + qs_.Init(kMaxQp / kDefaultLowQpDenominator); qs_.ReportFramerate(kFramerate); } @@ -40,7 +40,7 @@ class QualityScalerTest : public ::testing::Test { for (int i = 0; i < kFramerate * kNumSeconds; ++i) { switch (scale_direction) { case kScaleUp: - qs_.ReportNormalizedQP(kLowQp); + qs_.ReportQP(kLowQp); break; case kScaleDown: qs_.ReportDroppedFrame(); @@ -93,7 +93,7 @@ TEST_F(QualityScalerTest, DownscalesAfterContinuousFramedrop) { TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { for (int i = 0; i < kFramerate * kNumSeconds / 3; ++i) { - qs_.ReportNormalizedQP(kNormalQp); + qs_.ReportQP(kNormalQp); qs_.ReportDroppedFrame(); qs_.ReportDroppedFrame(); if (qs_.GetScaledResolution(input_frame_).width < input_frame_.width()) @@ -105,7 +105,7 @@ TEST_F(QualityScalerTest, DownscalesAfterTwoThirdsFramedrop) { TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { for (int i = 0; i < kFramerate * kNumSeconds; ++i) { - qs_.ReportNormalizedQP(kNormalQp); + qs_.ReportQP(kNormalQp); ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) << "Unexpected scale on half framedrop."; } @@ -113,7 +113,7 @@ TEST_F(QualityScalerTest, DoesNotDownscaleOnNormalQp) { TEST_F(QualityScalerTest, DoesNotDownscaleAfterHalfFramedrop) { for (int i = 0; i < kFramerate * kNumSeconds / 2; ++i) { - qs_.ReportNormalizedQP(kNormalQp); + qs_.ReportQP(kNormalQp); ASSERT_EQ(input_frame_.width(), qs_.GetScaledResolution(input_frame_).width) << "Unexpected scale on half framedrop."; @@ -153,7 +153,7 @@ void QualityScalerTest::ContinuouslyDownscalesByHalfDimensionsAndBackUp() { // Verify we don't start upscaling after further low use. for (int i = 0; i < kFramerate * kNumSeconds; ++i) { - qs_.ReportNormalizedQP(kLowQp); + qs_.ReportQP(kLowQp); ExpectOriginalFrame(); } } diff --git a/webrtc/modules/video_coding/utility/video_coding_utility.gyp b/webrtc/modules/video_coding/utility/video_coding_utility.gyp index c3649c6152..d5628489a9 100644 --- a/webrtc/modules/video_coding/utility/video_coding_utility.gyp +++ b/webrtc/modules/video_coding/utility/video_coding_utility.gyp @@ -21,8 +21,10 @@ 'frame_dropper.cc', 'include/frame_dropper.h', 'include/moving_average.h', - 'quality_scaler.cc', 'include/quality_scaler.h', + 'include/vp8_header_parser.h', + 'quality_scaler.cc', + 'vp8_header_parser.cc', ], }, ], # targets diff --git a/webrtc/modules/video_coding/utility/vp8_header_parser.cc b/webrtc/modules/video_coding/utility/vp8_header_parser.cc new file mode 100644 index 0000000000..0b0bfa235c --- /dev/null +++ b/webrtc/modules/video_coding/utility/vp8_header_parser.cc @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2015 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 +#include + +#include "webrtc/modules/video_coding/utility/include/vp8_header_parser.h" + +namespace webrtc { + +namespace vp8 { + +static uint32_t BSwap32(uint32_t x) { + return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); +} + +static void VP8LoadFinalBytes(VP8BitReader* const br) { + // Only read 8bits at a time. + if (br->buf_ < br->buf_end_) { + br->bits_ += 8; + br->value_ = static_cast(*br->buf_++) | (br->value_ << 8); + } else if (!br->eof_) { + br->value_ <<= 8; + br->bits_ += 8; + br->eof_ = 1; + } +} + +static void VP8LoadNewBytes(VP8BitReader* const br) { + int BITS = 24; + // Read 'BITS' bits at a time. + if (br->buf_ + sizeof(uint32_t) <= br->buf_end_) { + uint32_t bits; + const uint32_t in_bits = *(const uint32_t*)(br->buf_); + br->buf_ += BITS >> 3; +#if defined(WEBRTC_ARCH_BIG_ENDIAN) + bits = static_cast(in_bits); + if (BITS != 8 * sizeof(uint32_t)) + bits >>= (8 * sizeof(uint32_t) - BITS); +#else + bits = BSwap32(in_bits); + bits >>= 32 - BITS; +#endif + br->value_ = bits | (br->value_ << BITS); + br->bits_ += BITS; + } else { + VP8LoadFinalBytes(br); + } +} + +static void VP8InitBitReader(VP8BitReader* const br, + const uint8_t* const start, + const uint8_t* const end) { + br->range_ = 255 - 1; + br->buf_ = start; + br->buf_end_ = end; + br->value_ = 0; + br->bits_ = -8; // To load the very first 8bits. + br->eof_ = 0; + VP8LoadNewBytes(br); +} + +// Read a bit with proba 'prob'. +static int VP8GetBit(VP8BitReader* const br, int prob) { + uint8_t range = br->range_; + if (br->bits_ < 0) { + VP8LoadNewBytes(br); + } + + const int pos = br->bits_; + const uint8_t split = (range * prob) >> 8; + const uint8_t value = static_cast(br->value_ >> pos); + int bit; + if (value > split) { + range -= split + 1; + br->value_ -= static_cast(split + 1) << pos; + bit = 1; + } else { + range = split; + bit = 0; + } + if (range <= static_cast(0x7e)) { + const int shift = kVP8Log2Range[range]; + range = kVP8NewRange[range]; + br->bits_ -= shift; + } + br->range_ = range; + return bit; +} + +static uint32_t VP8GetValue(VP8BitReader* const br, int bits) { + uint32_t v = 0; + while (bits-- > 0) { + v |= VP8GetBit(br, 0x80) << bits; + } + return v; +} + +static uint32_t VP8Get(VP8BitReader* const br) { + return VP8GetValue(br, 1); +} + +static int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { + const int value = VP8GetValue(br, bits); + return VP8Get(br) ? -value : value; +} + +static void ParseSegmentHeader(VP8BitReader* br) { + int use_segment = VP8Get(br); + if (use_segment) { + int update_map = VP8Get(br); + if (VP8Get(br)) { + int s; + VP8Get(br); + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + VP8Get(br) ? VP8GetSignedValue(br, 7) : 0; + } + for (s = 0; s < NUM_MB_SEGMENTS; ++s) { + VP8Get(br) ? VP8GetSignedValue(br, 6) : 0; + } + } + if (update_map) { + int s; + for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) { + VP8Get(br) ? VP8GetValue(br, 8) : 255; + } + } + } +} + +static void ParseFilterHeader(VP8BitReader* br) { + VP8Get(br); + VP8GetValue(br, 6); + VP8GetValue(br, 3); + int use_lf_delta = VP8Get(br); + if (use_lf_delta) { + if (VP8Get(br)) { + int i; + for (i = 0; i < NUM_REF_LF_DELTAS; ++i) { + if (VP8Get(br)) { + VP8GetSignedValue(br, 6); + } + } + for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) { + if (VP8Get(br)) { + VP8GetSignedValue(br, 6); + } + } + } + } +} + +int GetQP(uint8_t* buf) { + VP8BitReader br; + const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16); + int key_frame = !(bits & 1); + // Size of first partition in bytes. + int partition_length = (bits >> 5); + // Skip past uncompressed header: 10bytes for key, 3bytes for delta frames. + if (key_frame) { + buf += 10; + } else { + buf += 3; + } + VP8InitBitReader(&br, buf, buf + partition_length); + if (key_frame) { + // Color space and pixel type. + VP8Get(&br); + VP8Get(&br); + } + ParseSegmentHeader(&br); + ParseFilterHeader(&br); + // Number of coefficient data partitions. + VP8GetValue(&br, 2); + // Base QP. + const int base_q0 = VP8GetValue(&br, 7); + return base_q0; +} + +} // namespace vp8 + +} // namespace webrtc