Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(406)

Side by Side Diff: remoting/protocol/webrtc_video_renderer_adapter.cc

Issue 2200273003: Enable video stats reporting when using WebRTC (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address feedback Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/protocol/webrtc_video_renderer_adapter.h" 5 #include "remoting/protocol/webrtc_video_renderer_adapter.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/callback.h" 11 #include "base/callback.h"
12 #include "base/location.h" 12 #include "base/location.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "base/single_thread_task_runner.h" 14 #include "base/single_thread_task_runner.h"
15 #include "base/task_runner_util.h" 15 #include "base/task_runner_util.h"
16 #include "base/threading/thread_task_runner_handle.h" 16 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/threading/worker_pool.h" 17 #include "base/threading/worker_pool.h"
18 #include "remoting/protocol/client_video_stats_dispatcher.h"
18 #include "remoting/protocol/frame_consumer.h" 19 #include "remoting/protocol/frame_consumer.h"
19 #include "remoting/protocol/frame_stats.h" 20 #include "remoting/protocol/frame_stats.h"
20 #include "remoting/protocol/video_renderer.h" 21 #include "remoting/protocol/video_renderer.h"
21 #include "third_party/libyuv/include/libyuv/convert_argb.h" 22 #include "remoting/protocol/webrtc_transport.h"
22 #include "third_party/libyuv/include/libyuv/convert_from.h" 23 #include "third_party/libyuv/include/libyuv/convert_from.h"
23 #include "third_party/libyuv/include/libyuv/video_common.h"
24 #include "third_party/webrtc/media/base/videoframe.h" 24 #include "third_party/webrtc/media/base/videoframe.h"
25 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 25 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
26 26
27 namespace remoting { 27 namespace remoting {
28 namespace protocol { 28 namespace protocol {
29 29
30 namespace { 30 namespace {
31 31
32 // Maximum number of ClientFrameStats instances to keep.
33 const int kMaxQueuedStats = 200;
34
32 std::unique_ptr<webrtc::DesktopFrame> ConvertYuvToRgb( 35 std::unique_ptr<webrtc::DesktopFrame> ConvertYuvToRgb(
33 scoped_refptr<webrtc::VideoFrameBuffer> yuv_frame, 36 scoped_refptr<webrtc::VideoFrameBuffer> yuv_frame,
34 std::unique_ptr<webrtc::DesktopFrame> rgb_frame, 37 std::unique_ptr<webrtc::DesktopFrame> rgb_frame,
35 FrameConsumer::PixelFormat pixel_format) { 38 FrameConsumer::PixelFormat pixel_format) {
36 DCHECK(rgb_frame->size().equals( 39 DCHECK(rgb_frame->size().equals(
37 webrtc::DesktopSize(yuv_frame->width(), yuv_frame->height()))); 40 webrtc::DesktopSize(yuv_frame->width(), yuv_frame->height())));
38 auto yuv_to_rgb_function = (pixel_format == FrameConsumer::FORMAT_BGRA) 41 auto yuv_to_rgb_function = (pixel_format == FrameConsumer::FORMAT_BGRA)
39 ? &libyuv::I420ToARGB 42 ? &libyuv::I420ToARGB
40 : &libyuv::I420ToABGR; 43 : &libyuv::I420ToABGR;
41 yuv_to_rgb_function(yuv_frame->DataY(), yuv_frame->StrideY(), 44 yuv_to_rgb_function(yuv_frame->DataY(), yuv_frame->StrideY(),
42 yuv_frame->DataU(), yuv_frame->StrideU(), 45 yuv_frame->DataU(), yuv_frame->StrideU(),
43 yuv_frame->DataV(), yuv_frame->StrideV(), 46 yuv_frame->DataV(), yuv_frame->StrideV(),
44 rgb_frame->data(), rgb_frame->stride(), 47 rgb_frame->data(), rgb_frame->stride(),
45 yuv_frame->width(), yuv_frame->height()); 48 yuv_frame->width(), yuv_frame->height());
46 49
47 rgb_frame->mutable_updated_region()->AddRect( 50 rgb_frame->mutable_updated_region()->AddRect(
48 webrtc::DesktopRect::MakeSize(rgb_frame->size())); 51 webrtc::DesktopRect::MakeSize(rgb_frame->size()));
49 return rgb_frame; 52 return rgb_frame;
50 } 53 }
51 54
52 } // namespace 55 } // namespace
53 56
54 WebrtcVideoRendererAdapter::WebrtcVideoRendererAdapter( 57 WebrtcVideoRendererAdapter::WebrtcVideoRendererAdapter(
55 scoped_refptr<webrtc::MediaStreamInterface> media_stream, 58 const std::string& label,
56 VideoRenderer* video_renderer) 59 VideoRenderer* video_renderer)
57 : media_stream_(std::move(media_stream)), 60 : label_(label),
58 video_renderer_(video_renderer), 61 video_renderer_(video_renderer),
59 task_runner_(base::ThreadTaskRunnerHandle::Get()), 62 task_runner_(base::ThreadTaskRunnerHandle::Get()),
60 weak_factory_(this) { 63 weak_factory_(this) {}
64
65 WebrtcVideoRendererAdapter::~WebrtcVideoRendererAdapter() {
66 DCHECK(task_runner_->BelongsToCurrentThread());
67
68 webrtc::VideoTrackVector video_tracks = media_stream_->GetVideoTracks();
69 DCHECK(!video_tracks.empty());
70 video_tracks[0]->RemoveSink(this);
71 }
72
73 void WebrtcVideoRendererAdapter::SetMediaStream(
74 scoped_refptr<webrtc::MediaStreamInterface> media_stream) {
75 DCHECK_EQ(media_stream->label(), label());
76
77 media_stream_ = std::move(media_stream);
78
61 webrtc::VideoTrackVector video_tracks = media_stream_->GetVideoTracks(); 79 webrtc::VideoTrackVector video_tracks = media_stream_->GetVideoTracks();
62 if (video_tracks.empty()) { 80 if (video_tracks.empty()) {
63 LOG(ERROR) << "Received media stream with no video tracks."; 81 LOG(ERROR) << "Received media stream with no video tracks.";
64 return; 82 return;
65 } 83 }
66 84
67 if (video_tracks.size() > 1U) { 85 if (video_tracks.size() > 1U) {
68 LOG(WARNING) << "Received media stream with multiple video tracks."; 86 LOG(WARNING) << "Received media stream with multiple video tracks.";
69 } 87 }
70 88
71 video_tracks[0]->AddOrUpdateSink(this, rtc::VideoSinkWants()); 89 video_tracks[0]->AddOrUpdateSink(this, rtc::VideoSinkWants());
72 } 90 }
73 91
74 WebrtcVideoRendererAdapter::~WebrtcVideoRendererAdapter() { 92 void WebrtcVideoRendererAdapter::SetVideoStatsChannel(
75 DCHECK(task_runner_->BelongsToCurrentThread()); 93 std::unique_ptr<MessagePipe> message_pipe) {
76 94 // Expect that the host also creates video_stats data channel.
77 webrtc::VideoTrackVector video_tracks = media_stream_->GetVideoTracks(); 95 video_stats_dispatcher_.reset(new ClientVideoStatsDispatcher(label_, this));
78 DCHECK(!video_tracks.empty()); 96 video_stats_dispatcher_->Init(std::move(message_pipe), this);
79 video_tracks[0]->RemoveSink(this);
80 } 97 }
81 98
82 void WebrtcVideoRendererAdapter::OnFrame(const cricket::VideoFrame& frame) { 99 void WebrtcVideoRendererAdapter::OnFrame(const cricket::VideoFrame& frame) {
83 if (static_cast<uint64_t>(frame.timestamp_us()) >= rtc::TimeMicros()) { 100 if (static_cast<uint64_t>(frame.timestamp_us()) >= rtc::TimeMicros()) {
84 // The host sets playout delay to 0, so all incoming frames are expected to 101 // The host sets playout delay to 0, so all incoming frames are expected to
85 // be rendered as so as they are received. 102 // be rendered as so as they are received.
86 LOG(WARNING) << "Received frame with playout delay greater than 0."; 103 LOG(WARNING) << "Received frame with playout delay greater than 0.";
87 } 104 }
88 105
106 task_runner_->PostTask(
107 FROM_HERE,
108 base::Bind(&WebrtcVideoRendererAdapter::HandleFrameOnMainThread,
109 weak_factory_.GetWeakPtr(), frame.transport_frame_id(),
110 base::TimeTicks::Now(),
111 scoped_refptr<webrtc::VideoFrameBuffer>(
112 frame.video_frame_buffer().get())));
113 }
114
115 void WebrtcVideoRendererAdapter::OnVideoFrameStats(
116 uint32_t frame_id,
117 const HostFrameStats& host_stats) {
118 DCHECK(task_runner_->BelongsToCurrentThread());
119
120 // Drop all ClientFrameStats for frames before |frame_id|. Stats messages are
121 // expected to be received in the same order as the corresponding video
122 // frames, so we are not going to receive HostFrameStats for the frames before
123 // |frame_id|. This may happen only if for some reason the host doesn't
124 // generate stats message for all video frames.
125 while (!client_stats_queue_.empty() &&
126 client_stats_queue_.front().first != frame_id) {
127 client_stats_queue_.pop_front();
128 }
129
130 // If there are no ClientFrameStats in the queue then queue HostFrameStats
131 // to be processed in FrameRendered().
132 if (client_stats_queue_.empty()) {
133 if (host_stats_queue_.size() > kMaxQueuedStats) {
134 LOG(ERROR) << "video_stats channel is out of sync with the video stream. "
135 "Performance stats will not be reported.";
136 video_stats_dispatcher_.reset();
137 return;
138 }
139 host_stats_queue_.push_back(std::make_pair(frame_id, host_stats));
140 return;
141 }
142
143 // The correspond frame has been received and now we have both HostFrameStats
144 // and ClientFrameStats. Report the stats to FrameStatsConsumer.
145 DCHECK_EQ(client_stats_queue_.front().first, frame_id);
146 FrameStats frame_stats;
147 frame_stats.client_stats = client_stats_queue_.front().second;
148 client_stats_queue_.pop_front();
149 frame_stats.host_stats = host_stats;
150 video_renderer_->GetFrameStatsConsumer()->OnVideoFrameStats(frame_stats);
151 }
152
153 void WebrtcVideoRendererAdapter::OnChannelInitialized(
154 ChannelDispatcherBase* channel_dispatcher) {}
155
156 void WebrtcVideoRendererAdapter::OnChannelClosed(
157 ChannelDispatcherBase* channel_dispatcher) {
158 LOG(WARNING) << "video_stats channel was closed by the host.";
159 }
160
161 void WebrtcVideoRendererAdapter::HandleFrameOnMainThread(
162 uint32_t frame_id,
163 base::TimeTicks time_received,
164 scoped_refptr<webrtc::VideoFrameBuffer> frame) {
165 DCHECK(task_runner_->BelongsToCurrentThread());
166
89 std::unique_ptr<ClientFrameStats> stats(new ClientFrameStats()); 167 std::unique_ptr<ClientFrameStats> stats(new ClientFrameStats());
90 // TODO(sergeyu): |time_received| is not reported correctly here because the 168 // TODO(sergeyu): |time_received| is not reported correctly here because the
91 // frame is already decoded at this point. 169 // frame is already decoded at this point.
92 stats->time_received = base::TimeTicks::Now(); 170 stats->time_received = time_received;
93
94 task_runner_->PostTask(
95 FROM_HERE,
96 base::Bind(&WebrtcVideoRendererAdapter::HandleFrameOnMainThread,
97 weak_factory_.GetWeakPtr(), base::Passed(&stats),
98 scoped_refptr<webrtc::VideoFrameBuffer>(
99 frame.video_frame_buffer().get())));
100 }
101
102 void WebrtcVideoRendererAdapter::HandleFrameOnMainThread(
103 std::unique_ptr<ClientFrameStats> stats,
104 scoped_refptr<webrtc::VideoFrameBuffer> frame) {
105 DCHECK(task_runner_->BelongsToCurrentThread());
106 171
107 std::unique_ptr<webrtc::DesktopFrame> rgb_frame = 172 std::unique_ptr<webrtc::DesktopFrame> rgb_frame =
108 video_renderer_->GetFrameConsumer()->AllocateFrame( 173 video_renderer_->GetFrameConsumer()->AllocateFrame(
109 webrtc::DesktopSize(frame->width(), frame->height())); 174 webrtc::DesktopSize(frame->width(), frame->height()));
110 175
111 base::PostTaskAndReplyWithResult( 176 base::PostTaskAndReplyWithResult(
112 base::WorkerPool::GetTaskRunner(false).get(), FROM_HERE, 177 base::WorkerPool::GetTaskRunner(false).get(), FROM_HERE,
113 base::Bind(&ConvertYuvToRgb, base::Passed(&frame), 178 base::Bind(&ConvertYuvToRgb, base::Passed(&frame),
114 base::Passed(&rgb_frame), 179 base::Passed(&rgb_frame),
115 video_renderer_->GetFrameConsumer()->GetPixelFormat()), 180 video_renderer_->GetFrameConsumer()->GetPixelFormat()),
116 base::Bind(&WebrtcVideoRendererAdapter::DrawFrame, 181 base::Bind(&WebrtcVideoRendererAdapter::DrawFrame,
117 weak_factory_.GetWeakPtr(), base::Passed(&stats))); 182 weak_factory_.GetWeakPtr(), frame_id, base::Passed(&stats)));
118 } 183 }
119 184
120 void WebrtcVideoRendererAdapter::DrawFrame( 185 void WebrtcVideoRendererAdapter::DrawFrame(
186 uint32_t frame_id,
121 std::unique_ptr<ClientFrameStats> stats, 187 std::unique_ptr<ClientFrameStats> stats,
122 std::unique_ptr<webrtc::DesktopFrame> frame) { 188 std::unique_ptr<webrtc::DesktopFrame> frame) {
123 DCHECK(task_runner_->BelongsToCurrentThread()); 189 DCHECK(task_runner_->BelongsToCurrentThread());
124 stats->time_decoded = base::TimeTicks::Now(); 190 stats->time_decoded = base::TimeTicks::Now();
125 video_renderer_->GetFrameConsumer()->DrawFrame( 191 video_renderer_->GetFrameConsumer()->DrawFrame(
126 std::move(frame), 192 std::move(frame),
127 base::Bind(&WebrtcVideoRendererAdapter::FrameRendered, 193 base::Bind(&WebrtcVideoRendererAdapter::FrameRendered,
128 weak_factory_.GetWeakPtr(), base::Passed(&stats))); 194 weak_factory_.GetWeakPtr(), frame_id, base::Passed(&stats)));
129 } 195 }
130 196
131 void WebrtcVideoRendererAdapter::FrameRendered( 197 void WebrtcVideoRendererAdapter::FrameRendered(
132 std::unique_ptr<ClientFrameStats> stats) { 198 uint32_t frame_id,
133 // TODO(sergeyu): Report stats here 199 std::unique_ptr<ClientFrameStats> client_stats) {
200 DCHECK(task_runner_->BelongsToCurrentThread());
201
202 if (!video_stats_dispatcher_ || !video_stats_dispatcher_->is_connected())
203 return;
204
205 client_stats->time_rendered = base::TimeTicks::Now();
206
207 // Drop all HostFrameStats for frames before |frame_id|. Stats messages are
208 // expected to be received in the same order as the corresponding video
209 // frames. This may happen only if the host generates HostFrameStats without
210 // the corresponding frame.
211 while (!host_stats_queue_.empty() &&
212 host_stats_queue_.front().first != frame_id) {
213 LOG(WARNING) << "Host sent VideoStats message for a frame that was never "
214 "received.";
215 host_stats_queue_.pop_front();
216 }
217
218 // If HostFrameStats hasn't been received for |frame_id| then queue
219 // ClientFrameStats to be processed in OnVideoFrameStats().
220 if (host_stats_queue_.empty()) {
221 if (host_stats_queue_.size() > kMaxQueuedStats) {
Irfan 2016/08/10 20:58:49 This should be client_stats_queue ?
Sergey Ulanov 2016/08/10 22:29:56 Done.
222 LOG(ERROR) << "video_stats channel is out of sync with the video "
223 "stream. Performance stats will not be reported.";
224 video_stats_dispatcher_.reset();
225 return;
226 }
227 client_stats_queue_.push_back(std::make_pair(frame_id, *client_stats));
228 return;
229 }
230
231 // The correspond HostFrameStats has been received already and now we have
232 // both HostFrameStats and ClientFrameStats. Report the stats to
233 // FrameStatsConsumer.
234 DCHECK_EQ(host_stats_queue_.front().first, frame_id);
235 FrameStats frame_stats;
236 frame_stats.host_stats = host_stats_queue_.front().second;
237 frame_stats.client_stats = *client_stats;
238 host_stats_queue_.pop_front();
239 video_renderer_->GetFrameStatsConsumer()->OnVideoFrameStats(frame_stats);
134 } 240 }
135 241
136 } // namespace protocol 242 } // namespace protocol
137 } // namespace remoting 243 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698