Index: remoting/client/rectangle_update_decoder.cc |
diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9d34e2a8e663b76db689c7287dc5ad62d1f057b1 |
--- /dev/null |
+++ b/remoting/client/rectangle_update_decoder.cc |
@@ -0,0 +1,218 @@ |
+// 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.IsInitialized()) { |
+ 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; |
+ } |
+ |
+ // TODO(ajwong): Verify that we don't need to whitelist encodings. |
+ 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: " << format.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 |