Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / remoting / client / software_video_renderer.cc
blobb1ce27b6deb67e17359b85631f7683e5fc76e31c
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 "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/task_runner_util.h"
14 #include "remoting/base/util.h"
15 #include "remoting/client/frame_consumer.h"
16 #include "remoting/codec/video_decoder.h"
17 #include "remoting/codec/video_decoder_verbatim.h"
18 #include "remoting/codec/video_decoder_vpx.h"
19 #include "remoting/proto/video.pb.h"
20 #include "remoting/protocol/session_config.h"
21 #include "third_party/libyuv/include/libyuv/convert_argb.h"
22 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
24 using remoting::protocol::ChannelConfig;
25 using remoting::protocol::SessionConfig;
27 namespace remoting {
29 namespace {
31 // This class wraps a VideoDecoder and byte-swaps the pixels for compatibility
32 // with the android.graphics.Bitmap class.
33 // TODO(lambroslambrou): Refactor so that the VideoDecoder produces data
34 // in the right byte-order, instead of swapping it here.
35 class RgbToBgrVideoDecoderFilter : public VideoDecoder {
36 public:
37 RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent)
38 : parent_(parent.Pass()) {
41 bool DecodePacket(const VideoPacket& packet,
42 webrtc::DesktopFrame* frame) override {
43 if (!parent_->DecodePacket(packet, frame))
44 return false;
45 for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
46 !i.IsAtEnd(); i.Advance()) {
47 webrtc::DesktopRect rect = i.rect();
48 uint8_t* pixels = frame->data() + (rect.top() * frame->stride()) +
49 (rect.left() * webrtc::DesktopFrame::kBytesPerPixel);
50 libyuv::ABGRToARGB(pixels, frame->stride(), pixels, frame->stride(),
51 rect.width(), rect.height());
54 return true;
57 private:
58 scoped_ptr<VideoDecoder> parent_;
61 scoped_ptr<webrtc::DesktopFrame> DoDecodeFrame(
62 VideoDecoder* decoder,
63 scoped_ptr<VideoPacket> packet,
64 scoped_ptr<webrtc::DesktopFrame> frame) {
65 if (!decoder->DecodePacket(*packet, frame.get()))
66 frame.reset();
67 return frame.Pass();
70 } // namespace
72 SoftwareVideoRenderer::SoftwareVideoRenderer(
73 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
74 FrameConsumer* consumer)
75 : decode_task_runner_(decode_task_runner),
76 consumer_(consumer),
77 weak_factory_(this) {}
79 SoftwareVideoRenderer::~SoftwareVideoRenderer() {
80 if (decoder_)
81 decode_task_runner_->DeleteSoon(FROM_HERE, decoder_.release());
84 void SoftwareVideoRenderer::OnSessionConfig(
85 const protocol::SessionConfig& config) {
86 DCHECK(thread_checker_.CalledOnValidThread());
88 // Initialize decoder based on the selected codec.
89 ChannelConfig::Codec codec = config.video_config().codec;
90 if (codec == ChannelConfig::CODEC_VERBATIM) {
91 decoder_.reset(new VideoDecoderVerbatim());
92 } else if (codec == ChannelConfig::CODEC_VP8) {
93 decoder_ = VideoDecoderVpx::CreateForVP8();
94 } else if (codec == ChannelConfig::CODEC_VP9) {
95 decoder_ = VideoDecoderVpx::CreateForVP9();
96 } else {
97 NOTREACHED() << "Invalid Encoding found: " << codec;
100 if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) {
101 scoped_ptr<VideoDecoder> wrapper(
102 new RgbToBgrVideoDecoderFilter(decoder_.Pass()));
103 decoder_ = wrapper.Pass();
107 ChromotingStats* SoftwareVideoRenderer::GetStats() {
108 DCHECK(thread_checker_.CalledOnValidThread());
109 return &stats_;
112 protocol::VideoStub* SoftwareVideoRenderer::GetVideoStub() {
113 DCHECK(thread_checker_.CalledOnValidThread());
114 return this;
117 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
118 const base::Closure& done) {
119 DCHECK(thread_checker_.CalledOnValidThread());
121 base::ScopedClosureRunner done_runner(done);
123 stats_.RecordVideoPacketStats(*packet);
125 // If the video packet is empty then drop it. Empty packets are used to
126 // maintain activity on the network.
127 if (!packet->has_data() || packet->data().size() == 0) {
128 return;
131 if (packet->format().has_screen_width() &&
132 packet->format().has_screen_height()) {
133 source_size_.set(packet->format().screen_width(),
134 packet->format().screen_height());
137 if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) {
138 webrtc::DesktopVector source_dpi(packet->format().x_dpi(),
139 packet->format().y_dpi());
140 if (!source_dpi.equals(source_dpi_)) {
141 source_dpi_ = source_dpi;
145 if (source_size_.is_empty()) {
146 LOG(ERROR) << "Received VideoPacket with unknown size.";
147 return;
150 scoped_ptr<webrtc::DesktopFrame> frame =
151 consumer_->AllocateFrame(source_size_);
152 frame->set_dpi(source_dpi_);
154 base::PostTaskAndReplyWithResult(
155 decode_task_runner_.get(), FROM_HERE,
156 base::Bind(&DoDecodeFrame, decoder_.get(), base::Passed(&packet),
157 base::Passed(&frame)),
158 base::Bind(&SoftwareVideoRenderer::RenderFrame,
159 weak_factory_.GetWeakPtr(), base::TimeTicks::Now(),
160 done_runner.Release()));
163 void SoftwareVideoRenderer::RenderFrame(
164 base::TimeTicks decode_start_time,
165 const base::Closure& done,
166 scoped_ptr<webrtc::DesktopFrame> frame) {
167 DCHECK(thread_checker_.CalledOnValidThread());
169 stats_.RecordDecodeTime(
170 (base::TimeTicks::Now() - decode_start_time).InMilliseconds());
172 if (!frame) {
173 if (!done.is_null())
174 done.Run();
175 return;
178 consumer_->DrawFrame(
179 frame.Pass(),
180 base::Bind(&SoftwareVideoRenderer::OnFrameRendered,
181 weak_factory_.GetWeakPtr(), base::TimeTicks::Now(), done));
184 void SoftwareVideoRenderer::OnFrameRendered(base::TimeTicks paint_start_time,
185 const base::Closure& done) {
186 DCHECK(thread_checker_.CalledOnValidThread());
188 stats_.RecordPaintTime(
189 (base::TimeTicks::Now() - paint_start_time).InMilliseconds());
191 if (!done.is_null())
192 done.Run();
195 } // namespace remoting