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"
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
;
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
{
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
))
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());
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()))
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
),
78 perf_tracker_(perf_tracker
),
79 weak_factory_(this) {}
81 SoftwareVideoRenderer::~SoftwareVideoRenderer() {
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();
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());
114 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr
<VideoPacket
> packet
,
115 const base::Closure
& done
) {
116 DCHECK(thread_checker_
.CalledOnValidThread());
118 base::ScopedClosureRunner
done_runner(done
);
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) {
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.";
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());
168 perf_tracker_
->RecordDecodeTime(
169 (base::TimeTicks::Now() - decode_start_time
).InMilliseconds());
178 consumer_
->DrawFrame(
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());
189 perf_tracker_
->RecordPaintTime(
190 (base::TimeTicks::Now() - paint_start_time
).InMilliseconds());
197 } // namespace remoting