blob: aabbecfb5569cc913c5dd956cdf438594e477313 [file] [log] [blame]
// Copyright (c) 2011 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/rectangle_update_decoder.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "remoting/base/decoder.h"
#include "remoting/base/decoder_row_based.h"
#include "remoting/base/decoder_vp8.h"
#include "remoting/base/tracer.h"
#include "remoting/base/util.h"
#include "remoting/client/frame_consumer.h"
#include "remoting/protocol/session_config.h"
using remoting::protocol::ChannelConfig;
using remoting::protocol::SessionConfig;
namespace remoting {
class PartialFrameCleanup : public Task {
public:
PartialFrameCleanup(media::VideoFrame* frame, UpdatedRects* rects,
RectangleUpdateDecoder* decoder)
: frame_(frame), rects_(rects), decoder_(decoder) {
}
virtual void Run() {
delete rects_;
frame_ = NULL;
// There maybe pending request to refresh rectangles.
decoder_->OnFrameConsumed();
decoder_ = NULL;
}
private:
scoped_refptr<media::VideoFrame> frame_;
UpdatedRects* rects_;
scoped_refptr<RectangleUpdateDecoder> decoder_;
};
RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop,
FrameConsumer* consumer)
: message_loop_(message_loop),
consumer_(consumer),
frame_is_new_(false),
frame_is_consuming_(false) {
}
RectangleUpdateDecoder::~RectangleUpdateDecoder() {
}
void RectangleUpdateDecoder::Initialize(const SessionConfig* config) {
initial_screen_size_ = gfx::Size(config->initial_resolution().width,
config->initial_resolution().height);
// Initialize decoder based on the selected codec.
ChannelConfig::Codec codec = config->video_config().codec;
if (codec == ChannelConfig::CODEC_VERBATIM) {
TraceContext::tracer()->PrintString("Creating Verbatim decoder.");
decoder_.reset(DecoderRowBased::CreateVerbatimDecoder());
} else if (codec == ChannelConfig::CODEC_ZIP) {
TraceContext::tracer()->PrintString("Creating Zlib decoder");
decoder_.reset(DecoderRowBased::CreateZlibDecoder());
} else if (codec == ChannelConfig::CODEC_VP8) {
TraceContext::tracer()->PrintString("Creating VP8 decoder");
decoder_.reset(new DecoderVp8());
} else {
NOTREACHED() << "Invalid Encoding found: " << codec;
}
}
void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet,
Task* done) {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this,
&RectangleUpdateDecoder::DecodePacket, packet,
done));
return;
}
base::ScopedTaskRunner done_runner(done);
TraceContext::tracer()->PrintString("Decode Packet called.");
AllocateFrame(packet, done_runner.Release());
}
void RectangleUpdateDecoder::AllocateFrame(const VideoPacket* packet,
Task* done) {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this,
&RectangleUpdateDecoder::AllocateFrame, packet, done));
return;
}
base::ScopedTaskRunner done_runner(done);
TraceContext::tracer()->PrintString("AllocateFrame called.");
// Find the required frame size.
bool has_screen_size = packet->format().has_screen_width() &&
packet->format().has_screen_height();
gfx::Size screen_size(packet->format().screen_width(),
packet->format().screen_height());
if (!has_screen_size)
screen_size = initial_screen_size_;
// Find the current frame size.
gfx::Size frame_size(0, 0);
if (frame_)
frame_size = gfx::Size(static_cast<int>(frame_->width()),
static_cast<int>(frame_->height()));
// Allocate a new frame, if necessary.
if ((!frame_) || (has_screen_size && (screen_size != frame_size))) {
if (frame_) {
TraceContext::tracer()->PrintString("Releasing old frame.");
consumer_->ReleaseFrame(frame_);
frame_ = NULL;
}
TraceContext::tracer()->PrintString("Requesting new frame.");
consumer_->AllocateFrame(media::VideoFrame::RGB32,
screen_size.width(), screen_size.height(),
base::TimeDelta(), base::TimeDelta(),
&frame_,
NewRunnableMethod(this,
&RectangleUpdateDecoder::ProcessPacketData,
packet, done_runner.Release()));
frame_is_new_ = true;
return;
}
ProcessPacketData(packet, done_runner.Release());
}
void RectangleUpdateDecoder::ProcessPacketData(
const VideoPacket* packet, Task* done) {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this,
&RectangleUpdateDecoder::ProcessPacketData, packet,
done));
return;
}
base::ScopedTaskRunner done_runner(done);
if (frame_is_new_) {
decoder_->Reset();
decoder_->Initialize(frame_);
frame_is_new_ = false;
}
if (!decoder_->IsReadyForData()) {
// TODO(ajwong): This whole thing should move into an invalid state.
LOG(ERROR) << "Decoder is unable to process data. Dropping packet.";
return;
}
TraceContext::tracer()->PrintString("Executing Decode.");
if (decoder_->DecodePacket(packet) == Decoder::DECODE_DONE)
SubmitToConsumer();
}
void RectangleUpdateDecoder::SetScaleRatios(double horizontal_ratio,
double vertical_ratio) {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this,
&RectangleUpdateDecoder::SetScaleRatios,
horizontal_ratio,
vertical_ratio));
return;
}
// TODO(hclam): If the scale ratio has changed we should reallocate a
// VideoFrame of different size. However if the scale ratio is always
// smaller than 1.0 we can use the same video frame.
decoder_->SetScaleRatios(horizontal_ratio, vertical_ratio);
}
void RectangleUpdateDecoder::UpdateClipRect(const gfx::Rect& new_clip_rect) {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(
this,
&RectangleUpdateDecoder::UpdateClipRect, new_clip_rect));
return;
}
if (new_clip_rect == clip_rect_ || !decoder_.get())
return;
// Find out the rectangles to show because of clip rect is updated.
if (new_clip_rect.y() < clip_rect_.y()) {
refresh_rects_.push_back(
gfx::Rect(new_clip_rect.x(),
new_clip_rect.y(),
new_clip_rect.width(),
clip_rect_.y() - new_clip_rect.y()));
}
if (new_clip_rect.x() < clip_rect_.x()) {
refresh_rects_.push_back(
gfx::Rect(new_clip_rect.x(),
clip_rect_.y(),
clip_rect_.x() - new_clip_rect.x(),
clip_rect_.height()));
}
if (new_clip_rect.right() > clip_rect_.right()) {
refresh_rects_.push_back(
gfx::Rect(clip_rect_.right(),
clip_rect_.y(),
new_clip_rect.right() - clip_rect_.right(),
new_clip_rect.height()));
}
if (new_clip_rect.bottom() > clip_rect_.bottom()) {
refresh_rects_.push_back(
gfx::Rect(new_clip_rect.x(),
clip_rect_.bottom(),
new_clip_rect.width(),
new_clip_rect.bottom() - clip_rect_.bottom()));
}
clip_rect_ = new_clip_rect;
decoder_->SetClipRect(new_clip_rect);
DoRefresh();
}
void RectangleUpdateDecoder::RefreshFullFrame() {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this, &RectangleUpdateDecoder::RefreshFullFrame));
return;
}
// If a video frame or the decoder is not allocated yet then don't
// save the refresh rectangle to avoid wasted computation.
if (!frame_ || !decoder_.get())
return;
refresh_rects_.push_back(
gfx::Rect(0, 0, static_cast<int>(frame_->width()),
static_cast<int>(frame_->height())));
DoRefresh();
}
void RectangleUpdateDecoder::SubmitToConsumer() {
// A frame is not allocated yet, we can reach here because of a refresh
// request.
if (!frame_)
return;
UpdatedRects* dirty_rects = new UpdatedRects();
decoder_->GetUpdatedRects(dirty_rects);
frame_is_consuming_ = true;
consumer_->OnPartialFrameOutput(
frame_, dirty_rects,
new PartialFrameCleanup(frame_, dirty_rects, this));
}
void RectangleUpdateDecoder::DoRefresh() {
DCHECK_EQ(message_loop_, MessageLoop::current());
if (refresh_rects_.empty())
return;
decoder_->RefreshRects(refresh_rects_);
refresh_rects_.clear();
SubmitToConsumer();
}
void RectangleUpdateDecoder::OnFrameConsumed() {
if (message_loop_ != MessageLoop::current()) {
message_loop_->PostTask(
FROM_HERE,
NewTracedMethod(this, &RectangleUpdateDecoder::OnFrameConsumed));
return;
}
frame_is_consuming_ = false;
DoRefresh();
}
} // namespace remoting