Add stats for average QP per frame for VP8 (for received video streams):

"WebRTC.Video.Decoded.VP8.Qp"

BUG=chromium:512752

Review URL: https://codereview.webrtc.org/1340623002

Cr-Commit-Position: refs/heads/master@{#10349}
diff --git a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
index d2ec552..3bbf682 100644
--- a/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
+++ b/talk/app/webrtc/java/jni/androidmediaencoder_jni.cc
@@ -795,8 +795,11 @@
         header.fragmentationLength[0] = image->_length;
         header.fragmentationPlType[0] = 0;
         header.fragmentationTimeDiff[0] = 0;
-        if (scale_)
-          quality_scaler_.ReportQP(webrtc::vp8::GetQP(payload));
+        if (scale_) {
+          int qp;
+          if (webrtc::vp8::GetQp(payload, payload_size, &qp))
+            quality_scaler_.ReportQP(qp);
+        }
       } else if (codecType_ == kVideoCodecH264) {
         if (scale_) {
           h264_bitstream_parser_.ParseBitstream(payload, payload_size);
diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn
index e05ab85..7b3449c 100644
--- a/webrtc/modules/video_coding/BUILD.gn
+++ b/webrtc/modules/video_coding/BUILD.gn
@@ -96,8 +96,10 @@
     "utility/frame_dropper.cc",
     "utility/include/frame_dropper.h",
     "utility/include/moving_average.h",
+    "utility/include/qp_parser.h",
     "utility/include/quality_scaler.h",
     "utility/include/vp8_header_parser.h",
+    "utility/qp_parser.cc",
     "utility/quality_scaler.cc",
     "utility/vp8_header_parser.cc",
   ]
diff --git a/webrtc/modules/video_coding/main/interface/video_coding.h b/webrtc/modules/video_coding/main/interface/video_coding.h
index 7fd2627..889be69 100644
--- a/webrtc/modules/video_coding/main/interface/video_coding.h
+++ b/webrtc/modules/video_coding/main/interface/video_coding.h
@@ -132,7 +132,7 @@
     //                                to have. Usually MTU - overhead.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterSendCodec(const VideoCodec* sendCodec,
                                             uint32_t numberOfCores,
                                             uint32_t maxPayloadSize) = 0;
@@ -154,7 +154,7 @@
     //      - currentSendCodec : Address where the sendCodec will be written.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     //
     // NOTE: The returned codec information is not guaranteed to be current when
     // the call returns.  This method acquires a lock that is aligned with
@@ -180,7 +180,7 @@
     //      - payloadType     : The payload type bound which this encoder is bound to.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterExternalEncoder(VideoEncoder* externalEncoder,
                                             uint8_t payloadType,
                                             bool internalSource = false) = 0;
@@ -232,7 +232,7 @@
     //                                scenario
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t SetReceiveChannelParameters(int64_t rtt) = 0;
 
     // Register a transport callback which will be called to deliver the encoded data and
@@ -242,7 +242,7 @@
     //      - transport  : The callback object to register.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterTransportCallback(VCMPacketizationCallback* transport) = 0;
 
     // Register video output information callback which will be called to deliver information
@@ -253,7 +253,7 @@
     //      - outputInformation  : The callback object to register.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterSendStatisticsCallback(
                                      VCMSendStatisticsCallback* sendStats) = 0;
 
@@ -264,7 +264,7 @@
     //      - protection  : The callback object to register.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterProtectionCallback(VCMProtectionCallback* protection) = 0;
 
     // Enable or disable a video protection method.
@@ -275,7 +275,7 @@
     //                           it should be disabled.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t SetVideoProtection(VCMVideoProtection videoProtection,
                                        bool enable) = 0;
 
@@ -289,7 +289,7 @@
     //      - codecSpecificInfo : Extra codec information, e.g., pre-parsed in-band signaling.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t AddVideoFrame(
         const VideoFrame& videoFrame,
         const VideoContentMetrics* contentMetrics = NULL,
@@ -298,7 +298,7 @@
     // Next frame encoded should be an intra frame (keyframe).
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t IntraFrameRequest(int stream_index) = 0;
 
     // Frame Dropper enable. Can be used to disable the frame dropping when the encoder
@@ -310,7 +310,7 @@
     //      - enable            : True to enable the setting, false to disable it.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t EnableFrameDropper(bool enable) = 0;
 
 
@@ -329,7 +329,7 @@
     //                            to be decoded until the first key frame has been decoded.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterReceiveCodec(const VideoCodec* receiveCodec,
                                          int32_t numberOfCores,
                                          bool requireKeyFrame = false) = 0;
@@ -346,7 +346,7 @@
     //                                 object can make sure to render at a given time in ms.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterExternalDecoder(VideoDecoder* externalDecoder,
                                             uint8_t payloadType,
                                             bool internalRenderTiming) = 0;
@@ -360,7 +360,7 @@
     //                                 De-register with a NULL pointer.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterReceiveCallback(VCMReceiveCallback* receiveCallback) = 0;
 
     // Register a receive statistics callback which will be called to deliver information
@@ -371,7 +371,7 @@
     //      - receiveStats  : The callback object to register.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterReceiveStatisticsCallback(
                                VCMReceiveStatisticsCallback* receiveStats) = 0;
 
@@ -383,7 +383,7 @@
     //      - decoderTiming  : The callback object to register.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterDecoderTimingCallback(
         VCMDecoderTimingCallback* decoderTiming) = 0;
 
@@ -396,7 +396,7 @@
     //                                 De-register with a NULL pointer.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t RegisterFrameTypeCallback(
                                   VCMFrameTypeCallback* frameTypeCallback) = 0;
 
@@ -407,7 +407,7 @@
     //              - callback      : The callback to be registered in the VCM.
     //
     // Return value     : VCM_OK,     on success.
-    //                    <0,              on error.
+    //                    <0,         on error.
     virtual int32_t RegisterPacketRequestCallback(
                                         VCMPacketRequestCallback* callback) = 0;
 
@@ -416,7 +416,7 @@
     // Should be called as often as possible to get the most out of the decoder.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t Decode(uint16_t maxWaitTimeMs = 200) = 0;
 
     // Registers a callback which conveys the size of the render buffer.
@@ -426,7 +426,7 @@
     // Reset the decoder state to the initial state.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t ResetDecoder() = 0;
 
     // API to get the codec which is currently used for decoding by the module.
@@ -435,7 +435,7 @@
     //      - currentReceiveCodec      : Settings for the codec to be registered.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t ReceiveCodec(VideoCodec* currentReceiveCodec) const = 0;
 
     // API to get the codec type currently used for decoding by the module.
@@ -454,7 +454,7 @@
     //      - rtpInfo              : The parsed header.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t IncomingPacket(const uint8_t* incomingPayload,
                                    size_t payloadLength,
                                    const WebRtcRTPHeader& rtpInfo) = 0;
@@ -467,7 +467,7 @@
     //      - minPlayoutDelayMs   : Additional delay in ms.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t SetMinimumPlayoutDelay(uint32_t minPlayoutDelayMs) = 0;
 
     // Set the time required by the renderer to render a frame.
@@ -476,7 +476,7 @@
     //      - timeMS        : The time in ms required by the renderer to render a frame.
     //
     // Return value      : VCM_OK, on success.
-    //                     < 0,         on error.
+    //                     < 0,    on error.
     virtual int32_t SetRenderDelay(uint32_t timeMS) = 0;
 
     // The total delay desired by the VCM. Can be less than the minimum
diff --git a/webrtc/modules/video_coding/main/source/video_coding_impl.h b/webrtc/modules/video_coding/main/source/video_coding_impl.h
index 6813666..2d9dc06 100644
--- a/webrtc/modules/video_coding/main/source/video_coding_impl.h
+++ b/webrtc/modules/video_coding/main/source/video_coding_impl.h
@@ -25,6 +25,7 @@
 #include "webrtc/modules/video_coding/main/source/media_optimization.h"
 #include "webrtc/modules/video_coding/main/source/receiver.h"
 #include "webrtc/modules/video_coding/main/source/timing.h"
+#include "webrtc/modules/video_coding/utility/include/qp_parser.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
@@ -237,6 +238,7 @@
   VCMProcessTimer _receiveStatsTimer;
   VCMProcessTimer _retransmissionTimer;
   VCMProcessTimer _keyRequestTimer;
+  QpParser qp_parser_;
 };
 
 }  // namespace vcm
diff --git a/webrtc/modules/video_coding/main/source/video_receiver.cc b/webrtc/modules/video_coding/main/source/video_receiver.cc
index 76d2c9f..2e23b5e 100644
--- a/webrtc/modules/video_coding/main/source/video_receiver.cc
+++ b/webrtc/modules/video_coding/main/source/video_receiver.cc
@@ -302,7 +302,12 @@
 
     if (pre_decode_image_callback_) {
       EncodedImage encoded_image(frame->EncodedImage());
-      pre_decode_image_callback_->Encoded(encoded_image, NULL, NULL);
+      int qp = -1;
+      if (qp_parser_.GetQp(*frame, &qp)) {
+        encoded_image.qp_ = qp;
+      }
+      pre_decode_image_callback_->Encoded(
+          encoded_image, frame->CodecSpecific(), NULL);
     }
 
 #ifdef DEBUG_DECODER_BIT_STREAM
diff --git a/webrtc/modules/video_coding/utility/include/qp_parser.h b/webrtc/modules/video_coding/utility/include/qp_parser.h
new file mode 100644
index 0000000..805b37b
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/include/qp_parser.h
@@ -0,0 +1,30 @@
+/*
+ *  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_QP_PARSER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_QP_PARSER_H_
+
+#include "webrtc/modules/video_coding/main/source/encoded_frame.h"
+
+namespace webrtc {
+
+class QpParser {
+ public:
+  QpParser() {}
+  ~QpParser() {}
+
+  // Parses an encoded |frame| and extracts the |qp|.
+  // Returns true on success, false otherwise.
+  bool GetQp(const VCMEncodedFrame& frame, int* qp);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_UTILITY_QP_PARSER_H_
diff --git a/webrtc/modules/video_coding/utility/include/vp8_header_parser.h b/webrtc/modules/video_coding/utility/include/vp8_header_parser.h
index 839e093..88796ec 100644
--- a/webrtc/modules/video_coding/utility/include/vp8_header_parser.h
+++ b/webrtc/modules/video_coding/utility/include/vp8_header_parser.h
@@ -66,7 +66,9 @@
   241, 243, 245, 247, 249, 251, 253, 127
 };
 
-int GetQP(uint8_t* buf);
+// Gets the QP, QP range: [0, 127].
+// Returns true on success, false otherwise.
+bool GetQp(const uint8_t* buf, size_t length, int* qp);
 
 }  // namespace vp8
 
diff --git a/webrtc/modules/video_coding/utility/qp_parser.cc b/webrtc/modules/video_coding/utility/qp_parser.cc
new file mode 100644
index 0000000..62ce313
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/qp_parser.cc
@@ -0,0 +1,28 @@
+/*
+ *  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 "webrtc/modules/video_coding/utility/include/qp_parser.h"
+
+#include "webrtc/common_types.h"
+#include "webrtc/modules/video_coding/utility/include/vp8_header_parser.h"
+
+namespace webrtc {
+
+bool QpParser::GetQp(const VCMEncodedFrame& frame, int* qp) {
+  switch (frame.CodecSpecific()->codecType) {
+    case kVideoCodecVP8:
+      // QP range: [0, 127].
+      return vp8::GetQp(frame.Buffer(), frame.Length(), qp);
+    default:
+      return false;
+  }
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/video_coding/utility/video_coding_utility.gyp b/webrtc/modules/video_coding/utility/video_coding_utility.gyp
index d562848..f0764bb 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',
+        'include/qp_parser.h',
         'include/quality_scaler.h',
         'include/vp8_header_parser.h',
+        'qp_parser.cc',
         'quality_scaler.cc',
         'vp8_header_parser.cc',
       ],
diff --git a/webrtc/modules/video_coding/utility/vp8_header_parser.cc b/webrtc/modules/video_coding/utility/vp8_header_parser.cc
index 0b0bfa2..9fcd6e4 100644
--- a/webrtc/modules/video_coding/utility/vp8_header_parser.cc
+++ b/webrtc/modules/video_coding/utility/vp8_header_parser.cc
@@ -12,9 +12,15 @@
 
 #include "webrtc/modules/video_coding/utility/include/vp8_header_parser.h"
 
+#include "webrtc/system_wrappers/interface/logging.h"
+
 namespace webrtc {
 
 namespace vp8 {
+namespace {
+const size_t kCommonPayloadHeaderLength = 3;
+const size_t kKeyPayloadHeaderLength = 10;
+}  // namespace
 
 static uint32_t BSwap32(uint32_t x) {
   return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24);
@@ -156,18 +162,26 @@
   }
 }
 
-int GetQP(uint8_t* buf) {
+bool GetQp(const uint8_t* buf, size_t length, int* qp) {
+  if (length < kCommonPayloadHeaderLength) {
+    LOG(LS_WARNING) << "Failed to get QP, invalid length.";
+    return false;
+  }
   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.
+  uint32_t partition_length = (bits >> 5);
+  size_t header_length = kCommonPayloadHeaderLength;
   if (key_frame) {
-    buf += 10;
-  } else {
-    buf += 3;
+    header_length = kKeyPayloadHeaderLength;
   }
+  if (header_length + partition_length > length) {
+    LOG(LS_WARNING) << "Failed to get QP, invalid length: " << length;
+    return false;
+  }
+  buf += header_length;
+
   VP8InitBitReader(&br, buf, buf + partition_length);
   if (key_frame) {
     // Color space and pixel type.
@@ -180,7 +194,12 @@
   VP8GetValue(&br, 2);
   // Base QP.
   const int base_q0 = VP8GetValue(&br, 7);
-  return base_q0;
+  if (br.eof_ == 1) {
+    LOG(LS_WARNING) << "Failed to get QP, end of file reached.";
+    return false;
+  }
+  *qp = base_q0;
+  return true;
 }
 
 }  // namespace vp8
diff --git a/webrtc/system_wrappers/interface/metrics.h b/webrtc/system_wrappers/interface/metrics.h
index cb641c0..7ebe3bd 100644
--- a/webrtc/system_wrappers/interface/metrics.h
+++ b/webrtc/system_wrappers/interface/metrics.h
@@ -72,6 +72,9 @@
 #define RTC_HISTOGRAM_COUNTS_100(name, sample) RTC_HISTOGRAM_COUNTS( \
     name, sample, 1, 100, 50)
 
+#define RTC_HISTOGRAM_COUNTS_200(name, sample) RTC_HISTOGRAM_COUNTS( \
+    name, sample, 1, 200, 50)
+
 #define RTC_HISTOGRAM_COUNTS_1000(name, sample) RTC_HISTOGRAM_COUNTS( \
     name, sample, 1, 1000, 50)
 
diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
index 6b79e9c..5b456a8 100644
--- a/webrtc/video/receive_statistics_proxy.cc
+++ b/webrtc/video/receive_statistics_proxy.cc
@@ -13,6 +13,7 @@
 #include <cmath>
 
 #include "webrtc/base/checks.h"
+#include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/metrics.h"
@@ -53,6 +54,10 @@
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedWidthInPixels", width);
     RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.ReceivedHeightInPixels", height);
   }
+  int qp = qp_counters_.vp8.Avg(kMinRequiredSamples);
+  if (qp != -1)
+    RTC_HISTOGRAM_COUNTS_200("WebRTC.Video.Decoded.Vp8.Qp", qp);
+
   // TODO(asapersson): DecoderTiming() is call periodically (each 1000ms) and
   // not per frame. Change decode time to include every frame.
   const int kMinRequiredDecodeSamples = 5;
@@ -180,6 +185,17 @@
   stats_.discarded_packets = discarded_packets;
 }
 
+void ReceiveStatisticsProxy::OnPreDecode(
+    const EncodedImage& encoded_image,
+    const CodecSpecificInfo* codec_specific_info) {
+  if (codec_specific_info == nullptr || encoded_image.qp_ == -1) {
+    return;
+  }
+  if (codec_specific_info->codecType == kVideoCodecVP8) {
+    qp_counters_.vp8.Add(encoded_image.qp_);
+  }
+}
+
 void ReceiveStatisticsProxy::SampleCounter::Add(int sample) {
   sum += sample;
   ++num_samples;
diff --git a/webrtc/video/receive_statistics_proxy.h b/webrtc/video/receive_statistics_proxy.h
index 6e4fd3a..b6741f9 100644
--- a/webrtc/video/receive_statistics_proxy.h
+++ b/webrtc/video/receive_statistics_proxy.h
@@ -30,6 +30,7 @@
 class Clock;
 class ViECodec;
 class ViEDecoderObserver;
+struct CodecSpecificInfo;
 
 class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
                                public RtcpStatisticsCallback,
@@ -54,6 +55,9 @@
                        int render_delay_ms,
                        int64_t rtt_ms);
 
+  void OnPreDecode(const EncodedImage& encoded_image,
+                   const CodecSpecificInfo* codec_specific_info);
+
   // Overrides VCMReceiveStatisticsCallback.
   void OnReceiveRatesUpdated(uint32_t bitRate, uint32_t frameRate) override;
   void OnFrameCountsUpdated(const FrameCounts& frame_counts) override;
@@ -82,6 +86,9 @@
     int sum;
     int num_samples;
   };
+  struct QpCounters {
+    SampleCounter vp8;
+  };
 
   void UpdateHistograms() EXCLUSIVE_LOCKS_REQUIRED(crit_);
 
@@ -98,6 +105,7 @@
   SampleCounter decode_time_counter_ GUARDED_BY(crit_);
   SampleCounter delay_counter_ GUARDED_BY(crit_);
   ReportBlockStats report_block_stats_ GUARDED_BY(crit_);
+  QpCounters qp_counters_;  // Only accessed on the decoding thread.
 };
 
 }  // namespace webrtc
diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc
index bbcc66a..e892989 100644
--- a/webrtc/video/video_receive_stream.cc
+++ b/webrtc/video/video_receive_stream.cc
@@ -19,7 +19,6 @@
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/video/receive_statistics_proxy.h"
-#include "webrtc/video_encoder.h"
 #include "webrtc/video_engine/call_stats.h"
 #include "webrtc/video_receive_stream.h"
 
@@ -277,8 +276,7 @@
   incoming_video_stream_->SetExternalCallback(this);
   vie_channel_->SetIncomingVideoStream(incoming_video_stream_.get());
 
-  if (config.pre_decode_callback)
-    vie_channel_->RegisterPreDecodeImageCallback(&encoded_frame_proxy_);
+  vie_channel_->RegisterPreDecodeImageCallback(this);
   vie_channel_->RegisterPreRenderCallback(this);
 }
 
@@ -364,6 +362,21 @@
   return 0;
 }
 
+// TODO(asapersson): Consider moving callback from video_encoder.h or
+// creating a different callback.
+int32_t VideoReceiveStream::Encoded(
+    const EncodedImage& encoded_image,
+    const CodecSpecificInfo* codec_specific_info,
+    const RTPFragmentationHeader* fragmentation) {
+  stats_proxy_->OnPreDecode(encoded_image, codec_specific_info);
+  if (config_.pre_decode_callback) {
+    // TODO(asapersson): Remove EncodedFrameCallbackAdapter.
+    encoded_frame_proxy_.Encoded(
+        encoded_image, codec_specific_info, fragmentation);
+  }
+  return 0;
+}
+
 void VideoReceiveStream::SignalNetworkState(NetworkState state) {
   vie_channel_->SetRTCPMode(state == kNetworkUp ? config_.rtp.rtcp_mode
                                                 : RtcpMode::kOff);
diff --git a/webrtc/video/video_receive_stream.h b/webrtc/video/video_receive_stream.h
index 011788d..fbea604 100644
--- a/webrtc/video/video_receive_stream.h
+++ b/webrtc/video/video_receive_stream.h
@@ -22,6 +22,7 @@
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/video/encoded_frame_callback_adapter.h"
 #include "webrtc/video/receive_statistics_proxy.h"
+#include "webrtc/video_encoder.h"
 #include "webrtc/video_engine/vie_channel.h"
 #include "webrtc/video_engine/vie_channel_group.h"
 #include "webrtc/video_engine/vie_encoder.h"
@@ -35,7 +36,8 @@
 
 class VideoReceiveStream : public webrtc::VideoReceiveStream,
                            public I420FrameCallback,
-                           public VideoRenderCallback {
+                           public VideoRenderCallback,
+                           public EncodedImageCallback {
  public:
   VideoReceiveStream(int num_cpu_cores,
                      ChannelGroup* channel_group,
@@ -63,6 +65,11 @@
   int RenderFrame(const uint32_t /*stream_id*/,
                   const VideoFrame& video_frame) override;
 
+  // Overrides EncodedImageCallback.
+  int32_t Encoded(const EncodedImage& encoded_image,
+                  const CodecSpecificInfo* codec_specific_info,
+                  const RTPFragmentationHeader* fragmentation) override;
+
   const Config& config() const { return config_; }
 
   void SetSyncChannel(VoiceEngine* voice_engine, int audio_channel_id);
diff --git a/webrtc/video_frame.h b/webrtc/video_frame.h
index 821bfc2..ecc23c9 100644
--- a/webrtc/video_frame.h
+++ b/webrtc/video_frame.h
@@ -199,6 +199,7 @@
   size_t _size;
   bool _completeFrame = false;
   AdaptReason adapt_reason_;
+  int qp_ = -1;  // Quantizer value.
 };
 
 }  // namespace webrtc