blob: 02399896ea7573d26d138977f8aab8fdb96cf1de [file] [log] [blame]
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +01001/*
2 * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10#include "test/scenario/quality_stats.h"
11
12#include <utility>
13
14#include "common_video/libyuv/include/webrtc_libyuv.h"
15#include "rtc_base/checks.h"
16#include "rtc_base/event.h"
17
18namespace webrtc {
19namespace test {
20
21VideoQualityAnalyzer::VideoQualityAnalyzer(
Sebastian Jansson52de8b02019-01-16 17:25:44 +010022 std::unique_ptr<RtcEventLogOutput> writer,
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010023 std::function<void(const VideoFrameQualityInfo&)> frame_info_handler)
Sebastian Jansson52de8b02019-01-16 17:25:44 +010024 : writer_(std::move(writer)), task_queue_("VideoAnalyzer") {
25 if (writer_) {
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010026 PrintHeaders();
27 frame_info_handlers_.push_back(
28 [this](const VideoFrameQualityInfo& info) { PrintFrameInfo(info); });
29 }
30 if (frame_info_handler)
31 frame_info_handlers_.push_back(frame_info_handler);
32}
33
34VideoQualityAnalyzer::~VideoQualityAnalyzer() {
Sebastian Jansson123f3452019-03-13 11:22:52 +010035 task_queue_.SendTask([] {});
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010036}
37
38void VideoQualityAnalyzer::OnCapturedFrame(const VideoFrame& frame) {
39 VideoFrame copy = frame;
Mirko Bonadei80a86872019-02-04 15:01:43 +010040 task_queue_.PostTask([this, copy]() mutable {
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010041 captured_frames_.push_back(std::move(copy));
42 });
43}
44
45void VideoQualityAnalyzer::OnDecodedFrame(const VideoFrame& frame) {
46 VideoFrame decoded = frame;
47 RTC_CHECK(frame.ntp_time_ms());
48 RTC_CHECK(frame.timestamp());
49 task_queue_.PostTask([this, decoded] {
Sebastian Jansson4d2367a2019-02-20 14:18:35 +010050 // TODO(srte): Add detection and handling of lost frames.
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010051 RTC_CHECK(!captured_frames_.empty());
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010052 VideoFrame captured = std::move(captured_frames_.front());
53 captured_frames_.pop_front();
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010054 VideoFrameQualityInfo decoded_info =
55 VideoFrameQualityInfo{Timestamp::us(captured.timestamp_us()),
56 Timestamp::ms(decoded.timestamp() / 90.0),
57 Timestamp::ms(decoded.render_time_ms()),
58 decoded.width(),
59 decoded.height(),
60 I420PSNR(&captured, &decoded)};
61 for (auto& handler : frame_info_handlers_)
62 handler(decoded_info);
63 });
64}
65
66bool VideoQualityAnalyzer::Active() const {
67 return !frame_info_handlers_.empty();
68}
69
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010070void VideoQualityAnalyzer::PrintHeaders() {
Sebastian Jansson52de8b02019-01-16 17:25:44 +010071 writer_->Write("capt recv_capt render width height psnr\n");
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010072}
73
74void VideoQualityAnalyzer::PrintFrameInfo(const VideoFrameQualityInfo& sample) {
Sebastian Jansson52de8b02019-01-16 17:25:44 +010075 LogWriteFormat(writer_.get(), "%.3f %.3f %.3f %i %i %.3f\n",
76 sample.capture_time.seconds<double>(),
77 sample.received_capture_time.seconds<double>(),
78 sample.render_time.seconds<double>(), sample.width,
79 sample.height, sample.psnr);
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010080}
81
82void VideoQualityStats::HandleFrameInfo(VideoFrameQualityInfo sample) {
83 total++;
84 if (sample.render_time.IsInfinite()) {
85 ++lost;
86 } else {
87 ++valid;
88 end_to_end_seconds.AddSample(
89 (sample.render_time - sample.capture_time).seconds<double>());
90 psnr.AddSample(sample.psnr);
91 }
92}
93
94ForwardingCapturedFrameTap::ForwardingCapturedFrameTap(
Sebastian Janssonaa01f272019-01-30 11:28:59 +010095 Clock* clock,
Sebastian Jansson9a4f38e2018-12-19 13:14:41 +010096 VideoQualityAnalyzer* analyzer,
97 rtc::VideoSourceInterface<VideoFrame>* source)
98 : clock_(clock), analyzer_(analyzer), source_(source) {}
99
100ForwardingCapturedFrameTap::~ForwardingCapturedFrameTap() {}
101
102void ForwardingCapturedFrameTap::OnFrame(const VideoFrame& frame) {
103 RTC_CHECK(sink_);
104 VideoFrame copy = frame;
105 if (frame.ntp_time_ms() == 0)
106 copy.set_ntp_time_ms(clock_->CurrentNtpInMilliseconds());
107 copy.set_timestamp(copy.ntp_time_ms() * 90);
108 analyzer_->OnCapturedFrame(copy);
109 sink_->OnFrame(copy);
110}
111void ForwardingCapturedFrameTap::OnDiscardedFrame() {
112 RTC_CHECK(sink_);
113 discarded_count_++;
114 sink_->OnDiscardedFrame();
115}
116
117void ForwardingCapturedFrameTap::AddOrUpdateSink(
118 VideoSinkInterface<VideoFrame>* sink,
119 const rtc::VideoSinkWants& wants) {
120 sink_ = sink;
121 source_->AddOrUpdateSink(this, wants);
122}
123void ForwardingCapturedFrameTap::RemoveSink(
124 VideoSinkInterface<VideoFrame>* sink) {
125 source_->RemoveSink(this);
126 sink_ = nullptr;
127}
128
129DecodedFrameTap::DecodedFrameTap(VideoQualityAnalyzer* analyzer)
130 : analyzer_(analyzer) {}
131
132void DecodedFrameTap::OnFrame(const VideoFrame& frame) {
133 analyzer_->OnDecodedFrame(frame);
134}
135
136} // namespace test
137} // namespace webrtc