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/session_config.h" |
16 | 17 |
17 using media::AutoTaskRunner; | 18 using media::AutoTaskRunner; |
| 19 using remoting::protocol::ChannelConfig; |
| 20 using remoting::protocol::SessionConfig; |
18 | 21 |
19 namespace remoting { | 22 namespace remoting { |
20 | 23 |
21 namespace { | 24 namespace { |
22 | 25 |
23 class PartialFrameCleanup : public Task { | 26 class PartialFrameCleanup : public Task { |
24 public: | 27 public: |
25 PartialFrameCleanup(media::VideoFrame* frame, UpdatedRects* rects) | 28 PartialFrameCleanup(media::VideoFrame* frame, UpdatedRects* rects) |
26 : frame_(frame), rects_(rects) { | 29 : frame_(frame), rects_(rects) { |
27 } | 30 } |
(...skipping 12 matching lines...) Expand all Loading... |
40 | 43 |
41 RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop, | 44 RectangleUpdateDecoder::RectangleUpdateDecoder(MessageLoop* message_loop, |
42 FrameConsumer* consumer) | 45 FrameConsumer* consumer) |
43 : message_loop_(message_loop), | 46 : message_loop_(message_loop), |
44 consumer_(consumer) { | 47 consumer_(consumer) { |
45 } | 48 } |
46 | 49 |
47 RectangleUpdateDecoder::~RectangleUpdateDecoder() { | 50 RectangleUpdateDecoder::~RectangleUpdateDecoder() { |
48 } | 51 } |
49 | 52 |
50 void RectangleUpdateDecoder::DecodePacket(const VideoPacket& packet, | 53 void RectangleUpdateDecoder::Initialize(const SessionConfig* config) { |
| 54 screen_size_ = gfx::Size(config->initial_resolution().width, |
| 55 config->initial_resolution().height); |
| 56 |
| 57 // Initialize decoder based on the selected codec. |
| 58 ChannelConfig::Codec codec = config->video_config().codec; |
| 59 if (codec == ChannelConfig::CODEC_VERBATIM) { |
| 60 TraceContext::tracer()->PrintString("Creating Verbatim decoder."); |
| 61 decoder_.reset(DecoderRowBased::CreateVerbatimDecoder()); |
| 62 } else if (codec == ChannelConfig::CODEC_ZIP) { |
| 63 TraceContext::tracer()->PrintString("Creating Zlib decoder"); |
| 64 decoder_.reset(DecoderRowBased::CreateZlibDecoder()); |
| 65 // TODO(sergeyu): Enable VP8 on ARM builds. |
| 66 #if !defined(ARCH_CPU_ARM_FAMILY) |
| 67 } else if (codec == ChannelConfig::CODEC_VP8) { |
| 68 TraceContext::tracer()->PrintString("Creating VP8 decoder"); |
| 69 decoder_.reset(new DecoderVp8()); |
| 70 #endif |
| 71 } else { |
| 72 NOTREACHED() << "Invalid Encoding found: " << codec; |
| 73 } |
| 74 } |
| 75 |
| 76 void RectangleUpdateDecoder::DecodePacket(const VideoPacket* packet, |
51 Task* done) { | 77 Task* done) { |
52 if (message_loop_ != MessageLoop::current()) { | 78 if (message_loop_ != MessageLoop::current()) { |
53 message_loop_->PostTask( | 79 message_loop_->PostTask( |
54 FROM_HERE, | 80 FROM_HERE, |
55 NewTracedMethod(this, | 81 NewTracedMethod(this, |
56 &RectangleUpdateDecoder::DecodePacket, packet, | 82 &RectangleUpdateDecoder::DecodePacket, packet, |
57 done)); | 83 done)); |
58 return; | 84 return; |
59 } | 85 } |
60 AutoTaskRunner done_runner(done); | 86 AutoTaskRunner done_runner(done); |
61 | 87 |
62 TraceContext::tracer()->PrintString("Decode Packet called."); | 88 TraceContext::tracer()->PrintString("Decode Packet called."); |
63 | 89 |
64 if (!IsValidPacket(packet)) { | 90 if (!decoder_->IsReadyForData()) { |
65 LOG(ERROR) << "Received invalid packet."; | 91 InitializeDecoder( |
66 return; | 92 NewTracedMethod(this, |
67 } | 93 &RectangleUpdateDecoder::ProcessPacketData, |
68 | 94 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 { | 95 } else { |
79 process_packet_data->Run(); | 96 ProcessPacketData(packet, done_runner.release()); |
80 delete process_packet_data; | |
81 } | 97 } |
82 } | 98 } |
83 | 99 |
84 void RectangleUpdateDecoder::ProcessPacketData( | 100 void RectangleUpdateDecoder::ProcessPacketData( |
85 const VideoPacket& packet, Task* done) { | 101 const VideoPacket* packet, Task* done) { |
86 AutoTaskRunner done_runner(done); | 102 AutoTaskRunner done_runner(done); |
87 | 103 |
88 if (!decoder_->IsReadyForData()) { | 104 if (!decoder_->IsReadyForData()) { |
89 // TODO(ajwong): This whole thing should move into an invalid state. | 105 // TODO(ajwong): This whole thing should move into an invalid state. |
90 LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; | 106 LOG(ERROR) << "Decoder is unable to process data. Dropping packet."; |
91 return; | 107 return; |
92 } | 108 } |
93 | 109 |
94 TraceContext::tracer()->PrintString("Executing Decode."); | 110 TraceContext::tracer()->PrintString("Executing Decode."); |
95 decoder_->DecodeBytes(packet.data()); | |
96 | 111 |
97 if (packet.flags() | VideoPacket::LAST_PACKET) { | 112 Decoder::DecodeResult result = decoder_->DecodePacket(packet); |
98 decoder_->Reset(); | |
99 | 113 |
| 114 if (result == Decoder::DECODE_DONE) { |
100 UpdatedRects* rects = new UpdatedRects(); | 115 UpdatedRects* rects = new UpdatedRects(); |
101 | 116 decoder_->GetUpdatedRects(rects); |
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, | 117 consumer_->OnPartialFrameOutput(frame_, rects, |
107 new PartialFrameCleanup(frame_, rects)); | 118 new PartialFrameCleanup(frame_, rects)); |
108 } | 119 } |
109 } | 120 } |
110 | 121 |
111 // static | 122 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()) { | 123 if (message_loop_ != MessageLoop::current()) { |
146 message_loop_->PostTask( | 124 message_loop_->PostTask( |
147 FROM_HERE, | 125 FROM_HERE, |
148 NewTracedMethod(this, | 126 NewTracedMethod(this, |
149 &RectangleUpdateDecoder::InitializeDecoder, | 127 &RectangleUpdateDecoder::InitializeDecoder, done)); |
150 format, done)); | |
151 return; | 128 return; |
152 } | 129 } |
153 AutoTaskRunner done_runner(done); | 130 AutoTaskRunner done_runner(done); |
154 | 131 |
155 // Check if we need to request a new frame. | 132 // Check if we need to request a new frame. |
156 if (!frame_ || | 133 if (!frame_ || |
157 frame_->width() < static_cast<size_t>(format.width()) || | 134 frame_->width() != static_cast<size_t>(screen_size_.width()) || |
158 frame_->height() < static_cast<size_t>(format.height())) { | 135 frame_->height() != static_cast<size_t>(screen_size_.height())) { |
159 if (frame_) { | 136 if (frame_) { |
160 TraceContext::tracer()->PrintString("Releasing old frame."); | 137 TraceContext::tracer()->PrintString("Releasing old frame."); |
161 consumer_->ReleaseFrame(frame_); | 138 consumer_->ReleaseFrame(frame_); |
162 frame_ = NULL; | 139 frame_ = NULL; |
163 } | 140 } |
164 TraceContext::tracer()->PrintString("Requesting new frame."); | 141 TraceContext::tracer()->PrintString("Requesting new frame."); |
165 consumer_->AllocateFrame(media::VideoFrame::RGB32, | 142 consumer_->AllocateFrame(media::VideoFrame::RGB32, |
166 format.width(), format.height(), | 143 screen_size_.width(), screen_size_.height(), |
167 base::TimeDelta(), base::TimeDelta(), | 144 base::TimeDelta(), base::TimeDelta(), |
168 &frame_, | 145 &frame_, |
169 NewTracedMethod( | 146 NewTracedMethod( |
170 this, | 147 this, |
171 &RectangleUpdateDecoder::InitializeDecoder, | 148 &RectangleUpdateDecoder::InitializeDecoder, |
172 format, | |
173 done_runner.release())); | 149 done_runner.release())); |
174 return; | 150 return; |
175 } | 151 } |
176 | 152 |
177 // TODO(ajwong): We need to handle the allocator failing to create a frame | 153 // TODO(ajwong): We need to handle the allocator failing to create a frame |
178 // and properly disable this class. | 154 // and properly disable this class. |
179 CHECK(frame_); | 155 CHECK(frame_); |
180 | 156 |
181 if (decoder_.get()) { | 157 decoder_->Reset(); |
182 // TODO(ajwong): We need to handle changing decoders midstream correctly | 158 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 | 159 |
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"); | 160 TraceContext::tracer()->PrintString("Decoder is Initialized"); |
221 } | 161 } |
222 | 162 |
223 } // namespace remoting | 163 } // namespace remoting |
OLD | NEW |