diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-27 20:40:30 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-27 20:40:30 +0000 |
commit | 2073bd0b00962a89513618d3b7034c28f346ba3f (patch) | |
tree | beabcf3978911efcb7fc9d1c66690fe958c41d14 /remoting/client/rectangle_update_decoder.cc | |
parent | 44473f80c0be3e8b565d59201ce1b1a9338399b1 (diff) | |
download | chromium_src-2073bd0b00962a89513618d3b7034c28f346ba3f.zip chromium_src-2073bd0b00962a89513618d3b7034c28f346ba3f.tar.gz chromium_src-2073bd0b00962a89513618d3b7034c28f346ba3f.tar.bz2 |
Add in a new FrameConsumer interface, Decode API, and a RectangleUpdateDecoder abstraction.
This should allow a decoder that can still request the view to allocate frames without being owned by the view itself. This allows for cleaner threading semantics and reduced coupling of classes. The new decoder API keeps the decoder from being aware of the network packet types tightening up the API layering.
BUG=52833
TEST=None. This code isn't used yet.
Review URL: http://codereview.chromium.org/3335012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60703 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client/rectangle_update_decoder.cc')
-rw-r--r-- | remoting/client/rectangle_update_decoder.cc | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc new file mode 100644 index 0000000..2b35b83 --- /dev/null +++ b/remoting/client/rectangle_update_decoder.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2010 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 "media/base/callback.h" +#include "remoting/base/decoder.h" +#include "remoting/base/decoder_verbatim.h" +#include "remoting/base/decoder_zlib.h" +#include "remoting/base/protocol/chromotocol.pb.h" +#include "remoting/base/tracer.h" +#include "remoting/client/frame_consumer.h" + +using media::AutoTaskRunner; + +namespace remoting { + +namespace { + +class PartialFrameCleanup : public Task { + public: + PartialFrameCleanup(media::VideoFrame* frame, UpdatedRects* rects) + : frame_(frame), rects_(rects) { + } + + virtual void Run() { + delete rects_; + frame_ = NULL; + } + + private: + scoped_refptr<media::VideoFrame> frame_; + UpdatedRects* rects_; +}; + +} // namespace + +RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop, + FrameConsumer* consumer) + : message_loop_(message_loop), + consumer_(consumer) { +} + +RectangleUpdateDecoder::~RectangleUpdateDecoder() { +} + +void RectangleUpdateDecoder::DecodePacket(const RectangleUpdatePacket& packet, + Task* done) { + if (message_loop_ != MessageLoop::current()) { + message_loop_->PostTask( + FROM_HERE, + NewTracedMethod(this, + &RectangleUpdateDecoder::DecodePacket, packet, + done)); + return; + } + AutoTaskRunner done_runner(done); + + TraceContext::tracer()->PrintString("Decode Packet called."); + + if (!IsValidPacket(packet)) { + LOG(ERROR) << "Received invalid packet."; + return; + } + + Task* process_packet_data = + NewTracedMethod(this, + &RectangleUpdateDecoder::ProcessPacketData, + packet, done_runner.release()); + + if (packet.flags() | RectangleUpdatePacket::FIRST_PACKET) { + const RectangleFormat& format = packet.format(); + + InitializeDecoder(format, process_packet_data); + } else { + process_packet_data->Run(); + delete process_packet_data; + } +} + +void RectangleUpdateDecoder::ProcessPacketData( + const RectangleUpdatePacket& packet, + Task* done) { + AutoTaskRunner done_runner(done); + + 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."); + decoder_->DecodeBytes(packet.encoded_rect()); + + if (packet.flags() | RectangleUpdatePacket::LAST_PACKET) { + decoder_->Reset(); + + UpdatedRects* rects = new UpdatedRects(); + + // Empty out the list of current updated rects so the decoder can keep + // writing new ones while these are processed. + rects->swap(updated_rects_); + + consumer_->OnPartialFrameOutput(frame_, rects, + new PartialFrameCleanup(frame_, rects)); + } +} + +// static +bool RectangleUpdateDecoder::IsValidPacket( + const RectangleUpdatePacket& packet) { + if (!packet.IsValid()) { + LOG(WARNING) << "Protobuf consistency checks fail."; + return false; + } + + // First packet must have a format. + if (packet.flags() | RectangleUpdatePacket::FIRST_PACKET) { + if (!packet.has_format()) { + LOG(WARNING) << "First packet must have format."; + return false; + } + + const RectangleFormat& format = packet.format(); + if (!format.has_encoding() || + format.encoding() == EncodingInvalid) { + LOG(WARNING) << "Invalid encoding specified."; + return false; + } + } + + // We shouldn't generate null packets. + if (!packet.has_encoded_rect()) { + LOG(WARNING) << "Packet w/o an encoded rectangle received."; + return false; + } + + return true; +} + +void RectangleUpdateDecoder::InitializeDecoder(const RectangleFormat& format, + Task* done) { + if (message_loop_ != MessageLoop::current()) { + message_loop_->PostTask( + FROM_HERE, + NewTracedMethod(this, + &RectangleUpdateDecoder::InitializeDecoder, + format, done)); + return; + } + AutoTaskRunner done_runner(done); + + // Check if we need to request a new frame. + if (!frame_ || + frame_->width() < static_cast<size_t>(format.width()) || + frame_->height() < static_cast<size_t>(format.height())) { + if (frame_) { + TraceContext::tracer()->PrintString("Releasing old frame."); + consumer_->ReleaseFrame(frame_); + frame_ = NULL; + } + TraceContext::tracer()->PrintString("Requesting new frame."); + consumer_->AllocateFrame(media::VideoFrame::RGB32, + format.width(), format.height(), + base::TimeDelta(), base::TimeDelta(), + &frame_, + NewTracedMethod( + this, + &RectangleUpdateDecoder::InitializeDecoder, + format, + done_runner.release())); + return; + } + + // TODO(ajwong): We need to handle the allocator failing to create a frame + // and properly disable this class. + CHECK(frame_); + + if (decoder_.get()) { + // TODO(ajwong): We need to handle changing decoders midstream correctly + // since someone may feed us a corrupt stream, or manually create a + // stream that changes decoders. At the very leask, we should not + // crash the process. + // + // For now though, assume that only one encoding is used throughout. + // + // Note, this may be as simple as just deleting the current decoder. + // However, we need to verify the flushing semantics of the decoder first. + CHECK(decoder_->Encoding() == format.encoding()); + } else { + // Initialize a new decoder based on this message encoding. + if (format.encoding() == EncodingNone) { + TraceContext::tracer()->PrintString("Creating Verbatim decoder."); + decoder_.reset(new DecoderVerbatim()); + } else if (format.encoding() == EncodingZlib) { + TraceContext::tracer()->PrintString("Creating Zlib decoder"); + decoder_.reset(new DecoderZlib()); + } else { + NOTREACHED << "Invalid Encoding found: " << found.encoding(); + } + } + + // TODO(ajwong): This can happen in the face of corrupt input data. Figure + // out the right behavior and make this more resilient. + CHECK(updated_rects_.empty()); + + gfx::Rect rectangle_size(format.x(), format.y(), + format.width(), format.height()); + updated_rects_.push_back(rectangle_size); + decoder_->Initialize(frame_, rectangle_size); + TraceContext::tracer()->PrintString("Decoder is Initialized"); +} + +} // namespace remoting |