blob: 2fa0fb249090dd18a7656d036ce0745a4f74a69f [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/client/software_video_renderer.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner_util.h"
#include "remoting/base/util.h"
#include "remoting/codec/video_decoder.h"
#include "remoting/codec/video_decoder_verbatim.h"
#include "remoting/codec/video_decoder_vpx.h"
#include "remoting/proto/video.pb.h"
#include "remoting/protocol/frame_consumer.h"
#include "remoting/protocol/session_config.h"
#include "third_party/libyuv/include/libyuv/convert_argb.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
using remoting::protocol::ChannelConfig;
using remoting::protocol::SessionConfig;
namespace remoting {
namespace {
// This class wraps a VideoDecoder and byte-swaps the pixels for compatibility
// with the android.graphics.Bitmap class.
// TODO(lambroslambrou): Refactor so that the VideoDecoder produces data
// in the right byte-order, instead of swapping it here.
class RgbToBgrVideoDecoderFilter : public VideoDecoder {
public:
RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent)
: parent_(std::move(parent)) {}
bool DecodePacket(const VideoPacket& packet,
webrtc::DesktopFrame* frame) override {
if (!parent_->DecodePacket(packet, frame))
return false;
for (webrtc::DesktopRegion::Iterator i(frame->updated_region());
!i.IsAtEnd(); i.Advance()) {
webrtc::DesktopRect rect = i.rect();
uint8_t* pixels = frame->data() + (rect.top() * frame->stride()) +
(rect.left() * webrtc::DesktopFrame::kBytesPerPixel);
libyuv::ABGRToARGB(pixels, frame->stride(), pixels, frame->stride(),
rect.width(), rect.height());
}
return true;
}
private:
scoped_ptr<VideoDecoder> parent_;
};
scoped_ptr<webrtc::DesktopFrame> DoDecodeFrame(
VideoDecoder* decoder,
scoped_ptr<VideoPacket> packet,
scoped_ptr<webrtc::DesktopFrame> frame) {
if (!decoder->DecodePacket(*packet, frame.get()))
frame.reset();
return frame;
}
} // namespace
SoftwareVideoRenderer::SoftwareVideoRenderer(
scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
protocol::FrameConsumer* consumer,
protocol::PerformanceTracker* perf_tracker)
: decode_task_runner_(decode_task_runner),
consumer_(consumer),
perf_tracker_(perf_tracker),
weak_factory_(this) {}
SoftwareVideoRenderer::~SoftwareVideoRenderer() {
if (decoder_)
decode_task_runner_->DeleteSoon(FROM_HERE, decoder_.release());
}
void SoftwareVideoRenderer::OnSessionConfig(
const protocol::SessionConfig& config) {
DCHECK(thread_checker_.CalledOnValidThread());
// Initialize decoder based on the selected codec.
ChannelConfig::Codec codec = config.video_config().codec;
if (codec == ChannelConfig::CODEC_VERBATIM) {
decoder_.reset(new VideoDecoderVerbatim());
} else if (codec == ChannelConfig::CODEC_VP8) {
decoder_ = VideoDecoderVpx::CreateForVP8();
} else if (codec == ChannelConfig::CODEC_VP9) {
decoder_ = VideoDecoderVpx::CreateForVP9();
} else {
NOTREACHED() << "Invalid Encoding found: " << codec;
}
if (consumer_->GetPixelFormat() == protocol::FrameConsumer::FORMAT_RGBA) {
decoder_ =
make_scoped_ptr(new RgbToBgrVideoDecoderFilter(std::move(decoder_)));
}
}
protocol::VideoStub* SoftwareVideoRenderer::GetVideoStub() {
DCHECK(thread_checker_.CalledOnValidThread());
return this;
}
protocol::FrameConsumer* SoftwareVideoRenderer::GetFrameConsumer() {
return consumer_;
}
void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
const base::Closure& done) {
DCHECK(thread_checker_.CalledOnValidThread());
base::ScopedClosureRunner done_runner(done);
if (perf_tracker_)
perf_tracker_->RecordVideoPacketStats(*packet);
// If the video packet is empty then drop it. Empty packets are used to
// maintain activity on the network.
if (!packet->has_data() || packet->data().size() == 0) {
return;
}
if (packet->format().has_screen_width() &&
packet->format().has_screen_height()) {
source_size_.set(packet->format().screen_width(),
packet->format().screen_height());
}
if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) {
webrtc::DesktopVector source_dpi(packet->format().x_dpi(),
packet->format().y_dpi());
if (!source_dpi.equals(source_dpi_)) {
source_dpi_ = source_dpi;
}
}
if (source_size_.is_empty()) {
LOG(ERROR) << "Received VideoPacket with unknown size.";
return;
}
scoped_ptr<webrtc::DesktopFrame> frame =
consumer_->AllocateFrame(source_size_);
frame->set_dpi(source_dpi_);
int32_t frame_id = packet->frame_id();
base::PostTaskAndReplyWithResult(
decode_task_runner_.get(), FROM_HERE,
base::Bind(&DoDecodeFrame, decoder_.get(), base::Passed(&packet),
base::Passed(&frame)),
base::Bind(&SoftwareVideoRenderer::RenderFrame,
weak_factory_.GetWeakPtr(), frame_id, done_runner.Release()));
}
void SoftwareVideoRenderer::RenderFrame(
int32_t frame_id,
const base::Closure& done,
scoped_ptr<webrtc::DesktopFrame> frame) {
DCHECK(thread_checker_.CalledOnValidThread());
if (perf_tracker_)
perf_tracker_->OnFrameDecoded(frame_id);
if (!frame) {
if (!done.is_null())
done.Run();
return;
}
consumer_->DrawFrame(std::move(frame),
base::Bind(&SoftwareVideoRenderer::OnFrameRendered,
weak_factory_.GetWeakPtr(), frame_id, done));
}
void SoftwareVideoRenderer::OnFrameRendered(int32_t frame_id,
const base::Closure& done) {
DCHECK(thread_checker_.CalledOnValidThread());
if (perf_tracker_)
perf_tracker_->OnFramePainted(frame_id);
if (!done.is_null())
done.Run();
}
} // namespace remoting