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 : decode_task_runner_(decode_task_runner
),
77 weak_factory_(this) {}
79 SoftwareVideoRenderer::~SoftwareVideoRenderer() {
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();
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());
112 protocol::VideoStub
* SoftwareVideoRenderer::GetVideoStub() {
113 DCHECK(thread_checker_
.CalledOnValidThread());
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) {
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.";
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());
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());
188 stats_
.RecordPaintTime(
189 (base::TimeTicks::Now() - paint_start_time
).InMilliseconds());
195 } // namespace remoting