OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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. |
| 4 |
| 5 #include "remoting/client/rectangle_update_decoder.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/message_loop.h" |
| 9 #include "media/base/callback.h" |
| 10 #include "remoting/base/decoder.h" |
| 11 #include "remoting/base/decoder_verbatim.h" |
| 12 #include "remoting/base/decoder_zlib.h" |
| 13 #include "remoting/base/protocol/chromotocol.pb.h" |
| 14 #include "remoting/base/tracer.h" |
| 15 #include "remoting/client/frame_consumer.h" |
| 16 |
| 17 using media::AutoTaskRunner; |
| 18 |
| 19 namespace remoting { |
| 20 |
| 21 namespace { |
| 22 |
| 23 class PartialFrameCleanup : public Task { |
| 24 public: |
| 25 PartialFrameCleanup(media::VideoFrame* frame, UpdatedRects* rects) |
| 26 : frame_(frame), rects_(rects) { |
| 27 } |
| 28 |
| 29 virtual void Run() { |
| 30 delete rects_; |
| 31 frame_ = NULL; |
| 32 } |
| 33 |
| 34 private: |
| 35 scoped_refptr<media::VideoFrame> frame_; |
| 36 UpdatedRects* rects_; |
| 37 }; |
| 38 |
| 39 } // namespace |
| 40 |
| 41 RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop, |
| 42 FrameConsumer* consumer) |
| 43 : message_loop_(message_loop), |
| 44 consumer_(consumer) { |
| 45 } |
| 46 |
| 47 RectangleUpdateDecoder::~RectangleUpdateDecoder() { |
| 48 } |
| 49 |
| 50 void RectangleUpdateDecoder::DecodePacket(const RectangleUpdatePacket& packet, |
| 51 Task* done) { |
| 52 if (message_loop_ != MessageLoop::current()) { |
| 53 message_loop_->PostTask( |
| 54 FROM_HERE, |
| 55 NewTracedMethod(this, |
| 56 &RectangleUpdateDecoder::DecodePacket, packet, |
| 57 done)); |
| 58 return; |
| 59 } |
| 60 AutoTaskRunner done_runner(done); |
| 61 |
| 62 TraceContext::tracer()->PrintString("Decode Packet called."); |
| 63 |
| 64 if (!IsValidPacket(packet)) { |
| 65 LOG(ERROR) << "Received invalid packet."; |
| 66 return; |
| 67 } |
| 68 |
| 69 Task* process_packet_data = |
| 70 NewTracedMethod(this, |
| 71 &RectangleUpdateDecoder::ProcessPacketData, |
| 72 packet, done_runner.release()); |
| 73 |
| 74 if (packet.flags() | RectangleUpdatePacket::FIRST_PACKET) { |
| 75 const RectangleFormat& format = packet.format(); |
| 76 |
| 77 InitializeDecoder(format, process_packet_data); |
| 78 } else { |
| 79 process_packet_data->Run(); |
| 80 delete process_packet_data; |
| 81 } |
| 82 } |
| 83 |
| 84 void RectangleUpdateDecoder::ProcessPacketData( |
| 85 const RectangleUpdatePacket& packet, |
| 86 Task* done) { |
| 87 AutoTaskRunner done_runner(done); |
| 88 |
| 89 if (!decoder_->IsReadyForData()) { |
| 90 // TODO(ajwong): This whole thing should move into an invalid state. |
| 91 LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; |
| 92 return; |
| 93 } |
| 94 |
| 95 TraceContext::tracer()->PrintString("Executing Decode."); |
| 96 decoder_->DecodeBytes(packet.encoded_rect()); |
| 97 |
| 98 if (packet.flags() | RectangleUpdatePacket::LAST_PACKET) { |
| 99 decoder_->Reset(); |
| 100 |
| 101 UpdatedRects* rects = new UpdatedRects(); |
| 102 |
| 103 // Empty out the list of current updated rects so the decoder can keep |
| 104 // writing new ones while these are processed. |
| 105 rects->swap(updated_rects_); |
| 106 |
| 107 consumer_->OnPartialFrameOutput(frame_, rects, |
| 108 new PartialFrameCleanup(frame_, rects)); |
| 109 } |
| 110 } |
| 111 |
| 112 // static |
| 113 bool RectangleUpdateDecoder::IsValidPacket( |
| 114 const RectangleUpdatePacket& packet) { |
| 115 if (!packet.IsInitialized()) { |
| 116 LOG(WARNING) << "Protobuf consistency checks fail."; |
| 117 return false; |
| 118 } |
| 119 |
| 120 // First packet must have a format. |
| 121 if (packet.flags() | RectangleUpdatePacket::FIRST_PACKET) { |
| 122 if (!packet.has_format()) { |
| 123 LOG(WARNING) << "First packet must have format."; |
| 124 return false; |
| 125 } |
| 126 |
| 127 // TODO(ajwong): Verify that we don't need to whitelist encodings. |
| 128 const RectangleFormat& format = packet.format(); |
| 129 if (!format.has_encoding() || |
| 130 format.encoding() == EncodingInvalid) { |
| 131 LOG(WARNING) << "Invalid encoding specified."; |
| 132 return false; |
| 133 } |
| 134 } |
| 135 |
| 136 // We shouldn't generate null packets. |
| 137 if (!packet.has_encoded_rect()) { |
| 138 LOG(WARNING) << "Packet w/o an encoded rectangle received."; |
| 139 return false; |
| 140 } |
| 141 |
| 142 return true; |
| 143 } |
| 144 |
| 145 void RectangleUpdateDecoder::InitializeDecoder(const RectangleFormat& format, |
| 146 Task* done) { |
| 147 if (message_loop_ != MessageLoop::current()) { |
| 148 message_loop_->PostTask( |
| 149 FROM_HERE, |
| 150 NewTracedMethod(this, |
| 151 &RectangleUpdateDecoder::InitializeDecoder, |
| 152 format, done)); |
| 153 return; |
| 154 } |
| 155 AutoTaskRunner done_runner(done); |
| 156 |
| 157 // Check if we need to request a new frame. |
| 158 if (!frame_ || |
| 159 frame_->width() < static_cast<size_t>(format.width()) || |
| 160 frame_->height() < static_cast<size_t>(format.height())) { |
| 161 if (frame_) { |
| 162 TraceContext::tracer()->PrintString("Releasing old frame."); |
| 163 consumer_->ReleaseFrame(frame_); |
| 164 frame_ = NULL; |
| 165 } |
| 166 TraceContext::tracer()->PrintString("Requesting new frame."); |
| 167 consumer_->AllocateFrame(media::VideoFrame::RGB32, |
| 168 format.width(), format.height(), |
| 169 base::TimeDelta(), base::TimeDelta(), |
| 170 &frame_, |
| 171 NewTracedMethod( |
| 172 this, |
| 173 &RectangleUpdateDecoder::InitializeDecoder, |
| 174 format, |
| 175 done_runner.release())); |
| 176 return; |
| 177 } |
| 178 |
| 179 // TODO(ajwong): We need to handle the allocator failing to create a frame |
| 180 // and properly disable this class. |
| 181 CHECK(frame_); |
| 182 |
| 183 if (decoder_.get()) { |
| 184 // TODO(ajwong): We need to handle changing decoders midstream correctly |
| 185 // since someone may feed us a corrupt stream, or manually create a |
| 186 // stream that changes decoders. At the very leask, we should not |
| 187 // crash the process. |
| 188 // |
| 189 // For now though, assume that only one encoding is used throughout. |
| 190 // |
| 191 // Note, this may be as simple as just deleting the current decoder. |
| 192 // However, we need to verify the flushing semantics of the decoder first. |
| 193 CHECK(decoder_->Encoding() == format.encoding()); |
| 194 } else { |
| 195 // Initialize a new decoder based on this message encoding. |
| 196 if (format.encoding() == EncodingNone) { |
| 197 TraceContext::tracer()->PrintString("Creating Verbatim decoder."); |
| 198 decoder_.reset(new DecoderVerbatim()); |
| 199 } else if (format.encoding() == EncodingZlib) { |
| 200 TraceContext::tracer()->PrintString("Creating Zlib decoder"); |
| 201 decoder_.reset(new DecoderZlib()); |
| 202 } else { |
| 203 NOTREACHED() << "Invalid Encoding found: " << format.encoding(); |
| 204 } |
| 205 } |
| 206 |
| 207 // TODO(ajwong): This can happen in the face of corrupt input data. Figure |
| 208 // out the right behavior and make this more resilient. |
| 209 CHECK(updated_rects_.empty()); |
| 210 |
| 211 gfx::Rect rectangle_size(format.x(), format.y(), |
| 212 format.width(), format.height()); |
| 213 updated_rects_.push_back(rectangle_size); |
| 214 decoder_->Initialize(frame_, rectangle_size); |
| 215 TraceContext::tracer()->PrintString("Decoder is Initialized"); |
| 216 } |
| 217 |
| 218 } // namespace remoting |
OLD | NEW |