| Index: webrtc/video/video_quality_test.cc | 
| diff --git a/webrtc/video/video_quality_test.cc b/webrtc/video/video_quality_test.cc | 
| index 057a8a2ab71aa2d2be32b1e19e65e31860cf3138..3f5f37ebd3d9125bd9d1624ff868cf8cf4f3f154 100644 | 
| --- a/webrtc/video/video_quality_test.cc | 
| +++ b/webrtc/video/video_quality_test.cc | 
| @@ -64,11 +64,14 @@ class VideoAnalyzer : public PacketReceiver, | 
| graph_data_output_file_(graph_data_output_file), | 
| graph_title_(graph_title), | 
| ssrc_to_analyze_(ssrc_to_analyze), | 
| +        pre_encode_proxy_(this), | 
| encode_timing_proxy_(this), | 
| frames_to_process_(duration_frames), | 
| frames_recorded_(0), | 
| frames_processed_(0), | 
| dropped_frames_(0), | 
| +        dropped_frames_before_first_encode_(0), | 
| +        dropped_frames_before_rendering_(0), | 
| last_render_time_(0), | 
| rtp_timestamp_delta_(0), | 
| avg_psnr_threshold_(avg_psnr_threshold), | 
| @@ -144,18 +147,26 @@ class VideoAnalyzer : public PacketReceiver, | 
| void IncomingCapturedFrame(const VideoFrame& video_frame) override { | 
| VideoFrame copy = video_frame; | 
| copy.set_timestamp(copy.ntp_time_ms() * 90); | 
| - | 
| { | 
| rtc::CritScope lock(&crit_); | 
| -      if (!first_send_timestamp_ && rtp_timestamp_delta_ == 0) | 
| -        first_send_timestamp_ = rtc::Optional<uint32_t>(copy.timestamp()); | 
| - | 
| frames_.push_back(copy); | 
| } | 
|  | 
| input_->IncomingCapturedFrame(video_frame); | 
| } | 
|  | 
| +  void PreEncodeOnFrame(const VideoFrame& video_frame) { | 
| +    rtc::CritScope lock(&crit_); | 
| +    if (!first_send_timestamp_ && rtp_timestamp_delta_ == 0) { | 
| +      while (frames_.front().timestamp() != video_frame.timestamp()) { | 
| +        ++dropped_frames_before_first_encode_; | 
| +        frames_.pop_front(); | 
| +        RTC_CHECK(!frames_.empty()); | 
| +      } | 
| +      first_send_timestamp_ = rtc::Optional<uint32_t>(video_frame.timestamp()); | 
| +    } | 
| +  } | 
| + | 
| bool SendRtp(const uint8_t* packet, | 
| size_t length, | 
| const PacketOptions& options) override { | 
| @@ -204,9 +215,18 @@ class VideoAnalyzer : public PacketReceiver, | 
| wrap_handler_.Unwrap(video_frame.timestamp() - rtp_timestamp_delta_); | 
|  | 
| while (wrap_handler_.Unwrap(frames_.front().timestamp()) < send_timestamp) { | 
| +      if (last_rendered_frame_.IsZeroSize()) { | 
| +        // No previous frame rendered, this one was dropped after sending but | 
| +        // before rendering. | 
| +        ++dropped_frames_before_rendering_; | 
| +        frames_.pop_front(); | 
| +        RTC_CHECK(!frames_.empty()); | 
| +        continue; | 
| +      } | 
| AddFrameComparison(frames_.front(), last_rendered_frame_, true, | 
| render_time_ms); | 
| frames_.pop_front(); | 
| +      RTC_DCHECK(!frames_.empty()); | 
| } | 
|  | 
| VideoFrame reference_frame = frames_.front(); | 
| @@ -268,6 +288,9 @@ class VideoAnalyzer : public PacketReceiver, | 
| stats_polling_thread_.Stop(); | 
| } | 
|  | 
| +  rtc::VideoSinkInterface<VideoFrame>* pre_encode_proxy() { | 
| +    return &pre_encode_proxy_; | 
| +  } | 
| EncodedFrameObserver* encode_timing_proxy() { return &encode_timing_proxy_; } | 
|  | 
| VideoCaptureInput* input_; | 
| @@ -351,11 +374,26 @@ class VideoAnalyzer : public PacketReceiver, | 
| VideoAnalyzer* const parent_; | 
| }; | 
|  | 
| +  // This class receives the send-side OnFrame callback and is provided to not | 
| +  // conflict with the receiver-side renderer callback. | 
| +  class PreEncodeProxy : public rtc::VideoSinkInterface<VideoFrame> { | 
| +   public: | 
| +    explicit PreEncodeProxy(VideoAnalyzer* parent) : parent_(parent) {} | 
| + | 
| +    void OnFrame(const VideoFrame& video_frame) override { | 
| +      parent_->PreEncodeOnFrame(video_frame); | 
| +    } | 
| + | 
| +   private: | 
| +    VideoAnalyzer* const parent_; | 
| +  }; | 
| + | 
| void AddFrameComparison(const VideoFrame& reference, | 
| const VideoFrame& render, | 
| bool dropped, | 
| int64_t render_time_ms) | 
| EXCLUSIVE_LOCKS_REQUIRED(crit_) { | 
| +    RTC_DCHECK(!render.IsZeroSize()); | 
| int64_t reference_timestamp = wrap_handler_.Unwrap(reference.timestamp()); | 
| int64_t send_time_ms = send_times_[reference_timestamp]; | 
| send_times_.erase(reference_timestamp); | 
| @@ -488,8 +526,6 @@ class VideoAnalyzer : public PacketReceiver, | 
| PrintResult("psnr", psnr_, " dB"); | 
| PrintResult("ssim", ssim_, " score"); | 
| PrintResult("sender_time", sender_time_, " ms"); | 
| -    printf("RESULT dropped_frames: %s = %d frames\n", test_label_.c_str(), | 
| -           dropped_frames_); | 
| PrintResult("receiver_time", receiver_time_, " ms"); | 
| PrintResult("total_delay_incl_network", end_to_end_, " ms"); | 
| PrintResult("time_between_rendered_frames", rendered_delta_, " ms"); | 
| @@ -499,6 +535,13 @@ class VideoAnalyzer : public PacketReceiver, | 
| PrintResult("encode_usage_percent", encode_usage_percent, " percent"); | 
| PrintResult("media_bitrate", media_bitrate_bps, " bps"); | 
|  | 
| +    printf("RESULT dropped_frames: %s = %d frames\n", test_label_.c_str(), | 
| +           dropped_frames_); | 
| +    printf("RESULT dropped_frames_before_first_encode: %s = %d frames\n", | 
| +           test_label_.c_str(), dropped_frames_before_first_encode_); | 
| +    printf("RESULT dropped_frames_before_rendering: %s = %d frames\n", | 
| +           test_label_.c_str(), dropped_frames_before_rendering_); | 
| + | 
| EXPECT_GT(psnr_.Mean(), avg_psnr_threshold_); | 
| EXPECT_GT(ssim_.Mean(), avg_ssim_threshold_); | 
| } | 
| @@ -594,6 +637,7 @@ class VideoAnalyzer : public PacketReceiver, | 
| FILE* const graph_data_output_file_; | 
| const std::string graph_title_; | 
| const uint32_t ssrc_to_analyze_; | 
| +  PreEncodeProxy pre_encode_proxy_; | 
| OnEncodeTimingProxy encode_timing_proxy_; | 
| std::vector<Sample> samples_ GUARDED_BY(comparison_lock_); | 
| std::map<int64_t, int> samples_encode_time_ms_ GUARDED_BY(comparison_lock_); | 
| @@ -613,6 +657,8 @@ class VideoAnalyzer : public PacketReceiver, | 
| int frames_recorded_; | 
| int frames_processed_; | 
| int dropped_frames_; | 
| +  int dropped_frames_before_first_encode_; | 
| +  int dropped_frames_before_rendering_; | 
| int64_t last_render_time_; | 
| uint32_t rtp_timestamp_delta_; | 
|  | 
| @@ -1006,6 +1052,7 @@ void VideoQualityTest::RunWithAnalyzer(const Params& params) { | 
|  | 
| SetupCommon(&analyzer, &recv_transport); | 
| video_receive_configs_[params_.ss.selected_stream].renderer = &analyzer; | 
| +  video_send_config_.pre_encode_callback = analyzer.pre_encode_proxy(); | 
| for (auto& config : video_receive_configs_) | 
| config.pre_decode_callback = &analyzer; | 
| RTC_DCHECK(!video_send_config_.post_encode_callback); | 
|  |