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