Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / client / software_video_renderer.cc
blobb52db3eacff23b9e8629a6b667ea9c99528eb0a7
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 protocol::PerformanceTracker* perf_tracker)
76 : decode_task_runner_(decode_task_runner),
77 consumer_(consumer),
78 perf_tracker_(perf_tracker),
79 weak_factory_(this) {}
81 SoftwareVideoRenderer::~SoftwareVideoRenderer() {
82 if (decoder_)
83 decode_task_runner_->DeleteSoon(FROM_HERE, decoder_.release());
86 void SoftwareVideoRenderer::OnSessionConfig(
87 const protocol::SessionConfig& config) {
88 DCHECK(thread_checker_.CalledOnValidThread());
90 // Initialize decoder based on the selected codec.
91 ChannelConfig::Codec codec = config.video_config().codec;
92 if (codec == ChannelConfig::CODEC_VERBATIM) {
93 decoder_.reset(new VideoDecoderVerbatim());
94 } else if (codec == ChannelConfig::CODEC_VP8) {
95 decoder_ = VideoDecoderVpx::CreateForVP8();
96 } else if (codec == ChannelConfig::CODEC_VP9) {
97 decoder_ = VideoDecoderVpx::CreateForVP9();
98 } else {
99 NOTREACHED() << "Invalid Encoding found: " << codec;
102 if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) {
103 scoped_ptr<VideoDecoder> wrapper(
104 new RgbToBgrVideoDecoderFilter(decoder_.Pass()));
105 decoder_ = wrapper.Pass();
109 protocol::VideoStub* SoftwareVideoRenderer::GetVideoStub() {
110 DCHECK(thread_checker_.CalledOnValidThread());
111 return this;
114 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
115 const base::Closure& done) {
116 DCHECK(thread_checker_.CalledOnValidThread());
118 base::ScopedClosureRunner done_runner(done);
120 if (perf_tracker_)
121 perf_tracker_->RecordVideoPacketStats(*packet);
123 // If the video packet is empty then drop it. Empty packets are used to
124 // maintain activity on the network.
125 if (!packet->has_data() || packet->data().size() == 0) {
126 return;
129 if (packet->format().has_screen_width() &&
130 packet->format().has_screen_height()) {
131 source_size_.set(packet->format().screen_width(),
132 packet->format().screen_height());
135 if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) {
136 webrtc::DesktopVector source_dpi(packet->format().x_dpi(),
137 packet->format().y_dpi());
138 if (!source_dpi.equals(source_dpi_)) {
139 source_dpi_ = source_dpi;
143 if (source_size_.is_empty()) {
144 LOG(ERROR) << "Received VideoPacket with unknown size.";
145 return;
148 scoped_ptr<webrtc::DesktopFrame> frame =
149 consumer_->AllocateFrame(source_size_);
150 frame->set_dpi(source_dpi_);
152 base::PostTaskAndReplyWithResult(
153 decode_task_runner_.get(), FROM_HERE,
154 base::Bind(&DoDecodeFrame, decoder_.get(), base::Passed(&packet),
155 base::Passed(&frame)),
156 base::Bind(&SoftwareVideoRenderer::RenderFrame,
157 weak_factory_.GetWeakPtr(), base::TimeTicks::Now(),
158 done_runner.Release()));
161 void SoftwareVideoRenderer::RenderFrame(
162 base::TimeTicks decode_start_time,
163 const base::Closure& done,
164 scoped_ptr<webrtc::DesktopFrame> frame) {
165 DCHECK(thread_checker_.CalledOnValidThread());
167 if (perf_tracker_) {
168 perf_tracker_->RecordDecodeTime(
169 (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 if (perf_tracker_) {
189 perf_tracker_->RecordPaintTime(
190 (base::TimeTicks::Now() - paint_start_time).InMilliseconds());
193 if (!done.is_null())
194 done.Run();
197 } // namespace remoting