Handle account removal correctly on all platforms.
[chromium-blink-merge.git] / remoting / client / software_video_renderer.cc
bloba7236c8fd0ef76ffc77645fde9ec3e379bed8a9b
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/client/software_video_renderer.h"
7 #include <list>
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/single_thread_task_runner.h"
15 #include "remoting/base/util.h"
16 #include "remoting/client/frame_consumer.h"
17 #include "remoting/codec/video_decoder.h"
18 #include "remoting/codec/video_decoder_verbatim.h"
19 #if !defined(MEDIA_DISABLE_LIBVPX)
20 #include "remoting/codec/video_decoder_vpx.h"
21 #endif // !defined(MEDIA_DISABLE_LIBVPX)
22 #include "remoting/protocol/session_config.h"
23 #include "third_party/libyuv/include/libyuv/convert_argb.h"
24 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
26 using base::Passed;
27 using remoting::protocol::ChannelConfig;
28 using remoting::protocol::SessionConfig;
30 namespace remoting {
32 // This class wraps a VideoDecoder and byte-swaps the pixels for compatibility
33 // with the android.graphics.Bitmap class.
34 // TODO(lambroslambrou): Refactor so that the VideoDecoder produces data
35 // in the right byte-order, instead of swapping it here.
36 class RgbToBgrVideoDecoderFilter : public VideoDecoder {
37 public:
38 RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent)
39 : parent_(parent.Pass()) {
42 virtual void Initialize(const webrtc::DesktopSize& screen_size) OVERRIDE {
43 parent_->Initialize(screen_size);
46 virtual bool DecodePacket(const VideoPacket& packet) OVERRIDE {
47 return parent_->DecodePacket(packet);
50 virtual void Invalidate(const webrtc::DesktopSize& view_size,
51 const webrtc::DesktopRegion& region) OVERRIDE {
52 return parent_->Invalidate(view_size, region);
55 virtual void RenderFrame(const webrtc::DesktopSize& view_size,
56 const webrtc::DesktopRect& clip_area,
57 uint8* image_buffer,
58 int image_stride,
59 webrtc::DesktopRegion* output_region) OVERRIDE {
60 parent_->RenderFrame(view_size, clip_area, image_buffer, image_stride,
61 output_region);
63 for (webrtc::DesktopRegion::Iterator i(*output_region); !i.IsAtEnd();
64 i.Advance()) {
65 webrtc::DesktopRect rect = i.rect();
66 uint8* pixels = image_buffer + (rect.top() * image_stride) +
67 (rect.left() * kBytesPerPixel);
68 libyuv::ABGRToARGB(pixels, image_stride, pixels, image_stride,
69 rect.width(), rect.height());
73 virtual const webrtc::DesktopRegion* GetImageShape() OVERRIDE {
74 return parent_->GetImageShape();
77 private:
78 scoped_ptr<VideoDecoder> parent_;
81 class SoftwareVideoRenderer::Core {
82 public:
83 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
84 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
85 scoped_refptr<FrameConsumerProxy> consumer);
86 ~Core();
88 void Initialize(const protocol::SessionConfig& config);
89 void DrawBuffer(webrtc::DesktopFrame* buffer);
90 void InvalidateRegion(const webrtc::DesktopRegion& region);
91 void RequestReturnBuffers(const base::Closure& done);
92 void SetOutputSizeAndClip(
93 const webrtc::DesktopSize& view_size,
94 const webrtc::DesktopRect& clip_area);
96 // Decodes the contents of |packet|. DecodePacket may keep a reference to
97 // |packet| so the |packet| must remain alive and valid until |done| is
98 // executed.
99 void DecodePacket(scoped_ptr<VideoPacket> packet, const base::Closure& done);
101 private:
102 // Paints the invalidated region to the next available buffer and returns it
103 // to the consumer.
104 void SchedulePaint();
105 void DoPaint();
107 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
108 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner_;
109 scoped_refptr<FrameConsumerProxy> consumer_;
110 scoped_ptr<VideoDecoder> decoder_;
112 // Remote screen size in pixels.
113 webrtc::DesktopSize source_size_;
115 // Vertical and horizontal DPI of the remote screen.
116 webrtc::DesktopVector source_dpi_;
118 // The current dimensions of the frame consumer view.
119 webrtc::DesktopSize view_size_;
120 webrtc::DesktopRect clip_area_;
122 // The drawing buffers supplied by the frame consumer.
123 std::list<webrtc::DesktopFrame*> buffers_;
125 // Flag used to coalesce runs of SchedulePaint()s into a single DoPaint().
126 bool paint_scheduled_;
128 base::WeakPtrFactory<Core> weak_factory_;
131 SoftwareVideoRenderer::Core::Core(
132 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
133 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
134 scoped_refptr<FrameConsumerProxy> consumer)
135 : main_task_runner_(main_task_runner),
136 decode_task_runner_(decode_task_runner),
137 consumer_(consumer),
138 paint_scheduled_(false),
139 weak_factory_(this) {
142 SoftwareVideoRenderer::Core::~Core() {
145 void SoftwareVideoRenderer::Core::Initialize(const SessionConfig& config) {
146 DCHECK(decode_task_runner_->BelongsToCurrentThread());
148 // Initialize decoder based on the selected codec.
149 ChannelConfig::Codec codec = config.video_config().codec;
150 if (codec == ChannelConfig::CODEC_VERBATIM) {
151 decoder_.reset(new VideoDecoderVerbatim());
152 #if !defined(MEDIA_DISABLE_LIBVPX)
153 } else if (codec == ChannelConfig::CODEC_VP8) {
154 decoder_ = VideoDecoderVpx::CreateForVP8();
155 } else if (codec == ChannelConfig::CODEC_VP9) {
156 decoder_ = VideoDecoderVpx::CreateForVP9();
157 } else {
158 #endif // !defined(MEDIA_DISABLE_LIBVPX)
159 NOTREACHED() << "Invalid Encoding found: " << codec;
162 if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) {
163 scoped_ptr<VideoDecoder> wrapper(
164 new RgbToBgrVideoDecoderFilter(decoder_.Pass()));
165 decoder_ = wrapper.Pass();
169 void SoftwareVideoRenderer::Core::DecodePacket(scoped_ptr<VideoPacket> packet,
170 const base::Closure& done) {
171 DCHECK(decode_task_runner_->BelongsToCurrentThread());
173 bool decoder_needs_reset = false;
174 bool notify_size_or_dpi_change = false;
176 // If the packet includes screen size or DPI information, store them.
177 if (packet->format().has_screen_width() &&
178 packet->format().has_screen_height()) {
179 webrtc::DesktopSize source_size(packet->format().screen_width(),
180 packet->format().screen_height());
181 if (!source_size_.equals(source_size)) {
182 source_size_ = source_size;
183 decoder_needs_reset = true;
184 notify_size_or_dpi_change = true;
187 if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) {
188 webrtc::DesktopVector source_dpi(packet->format().x_dpi(),
189 packet->format().y_dpi());
190 if (!source_dpi.equals(source_dpi_)) {
191 source_dpi_ = source_dpi;
192 notify_size_or_dpi_change = true;
196 // If we've never seen a screen size, ignore the packet.
197 if (source_size_.is_empty()) {
198 main_task_runner_->PostTask(FROM_HERE, base::Bind(done));
199 return;
202 if (decoder_needs_reset)
203 decoder_->Initialize(source_size_);
204 if (notify_size_or_dpi_change)
205 consumer_->SetSourceSize(source_size_, source_dpi_);
207 if (decoder_->DecodePacket(*packet.get())) {
208 SchedulePaint();
209 } else {
210 LOG(ERROR) << "DecodePacket() failed.";
213 main_task_runner_->PostTask(FROM_HERE, base::Bind(done));
216 void SoftwareVideoRenderer::Core::SchedulePaint() {
217 DCHECK(decode_task_runner_->BelongsToCurrentThread());
218 if (paint_scheduled_)
219 return;
220 paint_scheduled_ = true;
221 decode_task_runner_->PostTask(
222 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::DoPaint,
223 weak_factory_.GetWeakPtr()));
226 void SoftwareVideoRenderer::Core::DoPaint() {
227 DCHECK(decode_task_runner_->BelongsToCurrentThread());
228 DCHECK(paint_scheduled_);
229 paint_scheduled_ = false;
231 // If the view size is empty or we have no output buffers ready, return.
232 if (buffers_.empty() || view_size_.is_empty())
233 return;
235 // If no Decoder is initialized, or the host dimensions are empty, return.
236 if (!decoder_.get() || source_size_.is_empty())
237 return;
239 // Draw the invalidated region to the buffer.
240 webrtc::DesktopFrame* buffer = buffers_.front();
241 webrtc::DesktopRegion output_region;
242 decoder_->RenderFrame(view_size_, clip_area_,
243 buffer->data(), buffer->stride(), &output_region);
245 // Notify the consumer that painting is done.
246 if (!output_region.is_empty()) {
247 buffers_.pop_front();
248 consumer_->ApplyBuffer(view_size_, clip_area_, buffer, output_region,
249 *decoder_->GetImageShape());
253 void SoftwareVideoRenderer::Core::RequestReturnBuffers(
254 const base::Closure& done) {
255 DCHECK(decode_task_runner_->BelongsToCurrentThread());
257 while (!buffers_.empty()) {
258 consumer_->ReturnBuffer(buffers_.front());
259 buffers_.pop_front();
262 if (!done.is_null())
263 done.Run();
266 void SoftwareVideoRenderer::Core::DrawBuffer(webrtc::DesktopFrame* buffer) {
267 DCHECK(decode_task_runner_->BelongsToCurrentThread());
268 DCHECK(clip_area_.width() <= buffer->size().width() &&
269 clip_area_.height() <= buffer->size().height());
271 buffers_.push_back(buffer);
272 SchedulePaint();
275 void SoftwareVideoRenderer::Core::InvalidateRegion(
276 const webrtc::DesktopRegion& region) {
277 DCHECK(decode_task_runner_->BelongsToCurrentThread());
279 if (decoder_.get()) {
280 decoder_->Invalidate(view_size_, region);
281 SchedulePaint();
285 void SoftwareVideoRenderer::Core::SetOutputSizeAndClip(
286 const webrtc::DesktopSize& view_size,
287 const webrtc::DesktopRect& clip_area) {
288 DCHECK(decode_task_runner_->BelongsToCurrentThread());
290 // The whole frame needs to be repainted if the scaling factor has changed.
291 if (!view_size_.equals(view_size) && decoder_.get()) {
292 webrtc::DesktopRegion region;
293 region.AddRect(webrtc::DesktopRect::MakeSize(view_size));
294 decoder_->Invalidate(view_size, region);
297 if (!view_size_.equals(view_size) ||
298 !clip_area_.equals(clip_area)) {
299 view_size_ = view_size;
300 clip_area_ = clip_area;
302 // Return buffers that are smaller than needed to the consumer for
303 // reuse/reallocation.
304 std::list<webrtc::DesktopFrame*>::iterator i = buffers_.begin();
305 while (i != buffers_.end()) {
306 if ((*i)->size().width() < clip_area_.width() ||
307 (*i)->size().height() < clip_area_.height()) {
308 consumer_->ReturnBuffer(*i);
309 i = buffers_.erase(i);
310 } else {
311 ++i;
315 SchedulePaint();
319 SoftwareVideoRenderer::SoftwareVideoRenderer(
320 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
321 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
322 scoped_refptr<FrameConsumerProxy> consumer)
323 : decode_task_runner_(decode_task_runner),
324 core_(new Core(main_task_runner, decode_task_runner, consumer)),
325 latest_sequence_number_(0),
326 weak_factory_(this) {
327 DCHECK(CalledOnValidThread());
330 SoftwareVideoRenderer::~SoftwareVideoRenderer() {
331 DCHECK(CalledOnValidThread());
332 decode_task_runner_->DeleteSoon(FROM_HERE, core_.release());
335 void SoftwareVideoRenderer::Initialize(
336 const protocol::SessionConfig& config) {
337 DCHECK(CalledOnValidThread());
338 decode_task_runner_->PostTask(
339 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::Initialize,
340 base::Unretained(core_.get()), config));
343 ChromotingStats* SoftwareVideoRenderer::GetStats() {
344 DCHECK(CalledOnValidThread());
345 return &stats_;
348 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
349 const base::Closure& done) {
350 DCHECK(CalledOnValidThread());
352 // If the video packet is empty then drop it. Empty packets are used to
353 // maintain activity on the network.
354 if (!packet->has_data() || packet->data().size() == 0) {
355 done.Run();
356 return;
359 // Add one frame to the counter.
360 stats_.video_frame_rate()->Record(1);
362 // Record other statistics received from host.
363 stats_.video_bandwidth()->Record(packet->data().size());
364 if (packet->has_capture_time_ms())
365 stats_.video_capture_ms()->Record(packet->capture_time_ms());
366 if (packet->has_encode_time_ms())
367 stats_.video_encode_ms()->Record(packet->encode_time_ms());
368 if (packet->has_client_sequence_number() &&
369 packet->client_sequence_number() > latest_sequence_number_) {
370 latest_sequence_number_ = packet->client_sequence_number();
371 base::TimeDelta round_trip_latency =
372 base::Time::Now() -
373 base::Time::FromInternalValue(packet->client_sequence_number());
374 stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
377 // Measure the latency between the last packet being received and presented.
378 base::Time decode_start = base::Time::Now();
380 base::Closure decode_done = base::Bind(&SoftwareVideoRenderer::OnPacketDone,
381 weak_factory_.GetWeakPtr(),
382 decode_start, done);
384 decode_task_runner_->PostTask(FROM_HERE, base::Bind(
385 &SoftwareVideoRenderer::Core::DecodePacket,
386 base::Unretained(core_.get()), base::Passed(&packet), decode_done));
389 void SoftwareVideoRenderer::DrawBuffer(webrtc::DesktopFrame* buffer) {
390 decode_task_runner_->PostTask(
391 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::DrawBuffer,
392 base::Unretained(core_.get()), buffer));
395 void SoftwareVideoRenderer::InvalidateRegion(
396 const webrtc::DesktopRegion& region) {
397 decode_task_runner_->PostTask(
398 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::InvalidateRegion,
399 base::Unretained(core_.get()), region));
402 void SoftwareVideoRenderer::RequestReturnBuffers(const base::Closure& done) {
403 decode_task_runner_->PostTask(
404 FROM_HERE,
405 base::Bind(&SoftwareVideoRenderer::Core::RequestReturnBuffers,
406 base::Unretained(core_.get()), done));
409 void SoftwareVideoRenderer::SetOutputSizeAndClip(
410 const webrtc::DesktopSize& view_size,
411 const webrtc::DesktopRect& clip_area) {
412 decode_task_runner_->PostTask(
413 FROM_HERE,
414 base::Bind(&SoftwareVideoRenderer::Core::SetOutputSizeAndClip,
415 base::Unretained(core_.get()), view_size, clip_area));
418 void SoftwareVideoRenderer::OnPacketDone(base::Time decode_start,
419 const base::Closure& done) {
420 DCHECK(CalledOnValidThread());
422 // Record the latency between the packet being received and presented.
423 stats_.video_decode_ms()->Record(
424 (base::Time::Now() - decode_start).InMilliseconds());
426 done.Run();
429 } // namespace remoting