OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/software_video_renderer.h" | 5 #include "remoting/client/software_video_renderer.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/location.h" | 12 #include "base/location.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/single_thread_task_runner.h" | 14 #include "base/single_thread_task_runner.h" |
| 15 #include "base/task_runner_util.h" |
15 #include "remoting/base/util.h" | 16 #include "remoting/base/util.h" |
16 #include "remoting/client/frame_consumer.h" | 17 #include "remoting/client/frame_consumer.h" |
17 #include "remoting/codec/video_decoder.h" | 18 #include "remoting/codec/video_decoder.h" |
18 #include "remoting/codec/video_decoder_verbatim.h" | 19 #include "remoting/codec/video_decoder_verbatim.h" |
19 #include "remoting/codec/video_decoder_vpx.h" | 20 #include "remoting/codec/video_decoder_vpx.h" |
| 21 #include "remoting/proto/video.pb.h" |
20 #include "remoting/protocol/session_config.h" | 22 #include "remoting/protocol/session_config.h" |
21 #include "third_party/libyuv/include/libyuv/convert_argb.h" | 23 #include "third_party/libyuv/include/libyuv/convert_argb.h" |
22 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 24 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
23 | 25 |
24 using base::Passed; | |
25 using remoting::protocol::ChannelConfig; | 26 using remoting::protocol::ChannelConfig; |
26 using remoting::protocol::SessionConfig; | 27 using remoting::protocol::SessionConfig; |
27 | 28 |
28 namespace remoting { | 29 namespace remoting { |
29 | 30 |
| 31 namespace { |
| 32 |
30 // This class wraps a VideoDecoder and byte-swaps the pixels for compatibility | 33 // This class wraps a VideoDecoder and byte-swaps the pixels for compatibility |
31 // with the android.graphics.Bitmap class. | 34 // with the android.graphics.Bitmap class. |
32 // TODO(lambroslambrou): Refactor so that the VideoDecoder produces data | 35 // TODO(lambroslambrou): Refactor so that the VideoDecoder produces data |
33 // in the right byte-order, instead of swapping it here. | 36 // in the right byte-order, instead of swapping it here. |
34 class RgbToBgrVideoDecoderFilter : public VideoDecoder { | 37 class RgbToBgrVideoDecoderFilter : public VideoDecoder { |
35 public: | 38 public: |
36 RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent) | 39 RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent) |
37 : parent_(parent.Pass()) { | 40 : parent_(parent.Pass()) {} |
38 } | |
39 | |
40 void Initialize(const webrtc::DesktopSize& screen_size) override { | |
41 parent_->Initialize(screen_size); | |
42 } | |
43 | 41 |
44 bool DecodePacket(const VideoPacket& packet) override { | 42 bool DecodePacket(const VideoPacket& packet) override { |
45 return parent_->DecodePacket(packet); | 43 return parent_->DecodePacket(packet); |
46 } | 44 } |
47 | 45 |
48 void Invalidate(const webrtc::DesktopSize& view_size, | 46 void Invalidate(const webrtc::DesktopSize& view_size, |
49 const webrtc::DesktopRegion& region) override { | 47 const webrtc::DesktopRegion& region) override { |
50 return parent_->Invalidate(view_size, region); | 48 return parent_->Invalidate(view_size, region); |
51 } | 49 } |
52 | 50 |
53 void RenderFrame(const webrtc::DesktopSize& view_size, | 51 void RenderFrame(const webrtc::DesktopSize& view_size, |
54 const webrtc::DesktopRect& clip_area, | 52 const webrtc::DesktopRect& clip_area, |
55 uint8* image_buffer, | 53 uint8* image_buffer, |
56 int image_stride, | 54 int image_stride, |
57 webrtc::DesktopRegion* output_region) override { | 55 webrtc::DesktopRegion* output_region) override { |
58 parent_->RenderFrame(view_size, clip_area, image_buffer, image_stride, | 56 parent_->RenderFrame(view_size, clip_area, image_buffer, image_stride, |
59 output_region); | 57 output_region); |
60 | 58 |
61 for (webrtc::DesktopRegion::Iterator i(*output_region); !i.IsAtEnd(); | 59 for (webrtc::DesktopRegion::Iterator i(*output_region); !i.IsAtEnd(); |
62 i.Advance()) { | 60 i.Advance()) { |
63 webrtc::DesktopRect rect = i.rect(); | 61 webrtc::DesktopRect rect = i.rect(); |
64 uint8* pixels = image_buffer + (rect.top() * image_stride) + | 62 uint8* pixels = image_buffer + (rect.top() * image_stride) + |
65 (rect.left() * kBytesPerPixel); | 63 (rect.left() * kBytesPerPixel); |
66 libyuv::ABGRToARGB(pixels, image_stride, pixels, image_stride, | 64 libyuv::ABGRToARGB(pixels, image_stride, pixels, image_stride, |
67 rect.width(), rect.height()); | 65 rect.width(), rect.height()); |
68 } | 66 } |
69 } | 67 } |
70 | 68 |
71 const webrtc::DesktopRegion* GetImageShape() override { | 69 const webrtc::DesktopRegion* GetImageShape() override { |
72 return parent_->GetImageShape(); | 70 return parent_->GetImageShape(); |
73 } | 71 } |
74 | 72 |
75 private: | 73 private: |
76 scoped_ptr<VideoDecoder> parent_; | 74 scoped_ptr<VideoDecoder> parent_; |
77 }; | 75 }; |
78 | 76 |
79 class SoftwareVideoRenderer::Core { | 77 scoped_ptr<webrtc::DesktopFrame> DoDecodeFrame( |
80 public: | 78 VideoDecoder* decoder, |
81 Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | 79 scoped_ptr<VideoPacket> packet, |
82 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner, | 80 scoped_ptr<webrtc::DesktopFrame> frame) { |
83 scoped_ptr<FrameConsumerProxy> consumer); | 81 if (!decoder->DecodePacket(*packet)) |
84 ~Core(); | 82 frame.reset(); |
85 | 83 |
86 void OnSessionConfig(const protocol::SessionConfig& config); | 84 decoder->RenderFrame( |
87 void DrawBuffer(webrtc::DesktopFrame* buffer); | 85 frame->size(), webrtc::DesktopRect::MakeSize(frame->size()), |
88 void InvalidateRegion(const webrtc::DesktopRegion& region); | 86 frame->data(), frame->stride(), frame->mutable_updated_region()); |
89 void RequestReturnBuffers(const base::Closure& done); | |
90 void SetOutputSizeAndClip( | |
91 const webrtc::DesktopSize& view_size, | |
92 const webrtc::DesktopRect& clip_area); | |
93 | 87 |
94 // Decodes the contents of |packet|. DecodePacket may keep a reference to | 88 const webrtc::DesktopRegion* shape = decoder->GetImageShape(); |
95 // |packet| so the |packet| must remain alive and valid until |done| is | 89 if (shape) |
96 // executed. | 90 frame->set_shape(new webrtc::DesktopRegion(*shape)); |
97 void DecodePacket(scoped_ptr<VideoPacket> packet, const base::Closure& done); | |
98 | 91 |
99 private: | 92 return frame.Pass(); |
100 // Paints the invalidated region to the next available buffer and returns it | 93 } |
101 // to the consumer. | |
102 void SchedulePaint(); | |
103 void DoPaint(); | |
104 | 94 |
105 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; | 95 } // namespace |
106 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner_; | |
107 scoped_ptr<FrameConsumerProxy> consumer_; | |
108 scoped_ptr<VideoDecoder> decoder_; | |
109 | 96 |
110 // Remote screen size in pixels. | 97 SoftwareVideoRenderer::SoftwareVideoRenderer( |
111 webrtc::DesktopSize source_size_; | |
112 | |
113 // Vertical and horizontal DPI of the remote screen. | |
114 webrtc::DesktopVector source_dpi_; | |
115 | |
116 // The current dimensions of the frame consumer view. | |
117 webrtc::DesktopSize view_size_; | |
118 webrtc::DesktopRect clip_area_; | |
119 | |
120 // The drawing buffers supplied by the frame consumer. | |
121 std::list<webrtc::DesktopFrame*> buffers_; | |
122 | |
123 // Flag used to coalesce runs of SchedulePaint()s into a single DoPaint(). | |
124 bool paint_scheduled_; | |
125 | |
126 base::WeakPtrFactory<Core> weak_factory_; | |
127 }; | |
128 | |
129 SoftwareVideoRenderer::Core::Core( | |
130 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
131 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner, | 98 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner, |
132 scoped_ptr<FrameConsumerProxy> consumer) | 99 FrameConsumer* consumer) |
133 : main_task_runner_(main_task_runner), | 100 : decode_task_runner_(decode_task_runner), |
134 decode_task_runner_(decode_task_runner), | 101 consumer_(consumer), |
135 consumer_(consumer.Pass()), | |
136 paint_scheduled_(false), | |
137 weak_factory_(this) {} | 102 weak_factory_(this) {} |
138 | 103 |
139 SoftwareVideoRenderer::Core::~Core() { | 104 SoftwareVideoRenderer::~SoftwareVideoRenderer() { |
| 105 if (decoder_) |
| 106 decode_task_runner_->DeleteSoon(FROM_HERE, decoder_.release()); |
140 } | 107 } |
141 | 108 |
142 void SoftwareVideoRenderer::Core::OnSessionConfig(const SessionConfig& config) { | 109 void SoftwareVideoRenderer::OnSessionConfig( |
143 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | 110 const protocol::SessionConfig& config) { |
| 111 DCHECK(thread_checker_.CalledOnValidThread()); |
144 | 112 |
145 // Initialize decoder based on the selected codec. | 113 // Initialize decoder based on the selected codec. |
146 ChannelConfig::Codec codec = config.video_config().codec; | 114 ChannelConfig::Codec codec = config.video_config().codec; |
147 if (codec == ChannelConfig::CODEC_VERBATIM) { | 115 if (codec == ChannelConfig::CODEC_VERBATIM) { |
148 decoder_.reset(new VideoDecoderVerbatim()); | 116 decoder_.reset(new VideoDecoderVerbatim()); |
149 } else if (codec == ChannelConfig::CODEC_VP8) { | 117 } else if (codec == ChannelConfig::CODEC_VP8) { |
150 decoder_ = VideoDecoderVpx::CreateForVP8(); | 118 decoder_ = VideoDecoderVpx::CreateForVP8(); |
151 } else if (codec == ChannelConfig::CODEC_VP9) { | 119 } else if (codec == ChannelConfig::CODEC_VP9) { |
152 decoder_ = VideoDecoderVpx::CreateForVP9(); | 120 decoder_ = VideoDecoderVpx::CreateForVP9(); |
153 } else { | 121 } else { |
154 NOTREACHED() << "Invalid Encoding found: " << codec; | 122 NOTREACHED() << "Invalid Encoding found: " << codec; |
155 } | 123 } |
156 | 124 |
157 if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) { | 125 if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) { |
158 scoped_ptr<VideoDecoder> wrapper( | 126 scoped_ptr<VideoDecoder> wrapper( |
159 new RgbToBgrVideoDecoderFilter(decoder_.Pass())); | 127 new RgbToBgrVideoDecoderFilter(decoder_.Pass())); |
160 decoder_ = wrapper.Pass(); | 128 decoder_ = wrapper.Pass(); |
161 } | 129 } |
162 } | 130 } |
163 | 131 |
164 void SoftwareVideoRenderer::Core::DecodePacket(scoped_ptr<VideoPacket> packet, | |
165 const base::Closure& done) { | |
166 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
167 | |
168 bool decoder_needs_reset = false; | |
169 bool notify_size_or_dpi_change = false; | |
170 | |
171 // If the packet includes screen size or DPI information, store them. | |
172 if (packet->format().has_screen_width() && | |
173 packet->format().has_screen_height()) { | |
174 webrtc::DesktopSize source_size(packet->format().screen_width(), | |
175 packet->format().screen_height()); | |
176 if (!source_size_.equals(source_size)) { | |
177 source_size_ = source_size; | |
178 decoder_needs_reset = true; | |
179 notify_size_or_dpi_change = true; | |
180 } | |
181 } | |
182 if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) { | |
183 webrtc::DesktopVector source_dpi(packet->format().x_dpi(), | |
184 packet->format().y_dpi()); | |
185 if (!source_dpi.equals(source_dpi_)) { | |
186 source_dpi_ = source_dpi; | |
187 notify_size_or_dpi_change = true; | |
188 } | |
189 } | |
190 | |
191 // If we've never seen a screen size, ignore the packet. | |
192 if (source_size_.is_empty()) { | |
193 main_task_runner_->PostTask(FROM_HERE, base::Bind(done)); | |
194 return; | |
195 } | |
196 | |
197 if (decoder_needs_reset) | |
198 decoder_->Initialize(source_size_); | |
199 if (notify_size_or_dpi_change) | |
200 consumer_->SetSourceSize(source_size_, source_dpi_); | |
201 | |
202 if (decoder_->DecodePacket(*packet.get())) { | |
203 SchedulePaint(); | |
204 } else { | |
205 LOG(ERROR) << "DecodePacket() failed."; | |
206 } | |
207 | |
208 main_task_runner_->PostTask(FROM_HERE, base::Bind(done)); | |
209 } | |
210 | |
211 void SoftwareVideoRenderer::Core::SchedulePaint() { | |
212 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
213 if (paint_scheduled_) | |
214 return; | |
215 paint_scheduled_ = true; | |
216 decode_task_runner_->PostTask( | |
217 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::DoPaint, | |
218 weak_factory_.GetWeakPtr())); | |
219 } | |
220 | |
221 void SoftwareVideoRenderer::Core::DoPaint() { | |
222 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
223 DCHECK(paint_scheduled_); | |
224 paint_scheduled_ = false; | |
225 | |
226 // If the view size is empty or we have no output buffers ready, return. | |
227 if (buffers_.empty() || view_size_.is_empty()) | |
228 return; | |
229 | |
230 // If no Decoder is initialized, or the host dimensions are empty, return. | |
231 if (!decoder_.get() || source_size_.is_empty()) | |
232 return; | |
233 | |
234 // Draw the invalidated region to the buffer. | |
235 webrtc::DesktopFrame* buffer = buffers_.front(); | |
236 webrtc::DesktopRegion output_region; | |
237 decoder_->RenderFrame(view_size_, clip_area_, | |
238 buffer->data(), buffer->stride(), &output_region); | |
239 | |
240 // Notify the consumer that painting is done. | |
241 if (!output_region.is_empty()) { | |
242 buffers_.pop_front(); | |
243 consumer_->ApplyBuffer(view_size_, clip_area_, buffer, output_region, | |
244 decoder_->GetImageShape()); | |
245 } | |
246 } | |
247 | |
248 void SoftwareVideoRenderer::Core::RequestReturnBuffers( | |
249 const base::Closure& done) { | |
250 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
251 | |
252 while (!buffers_.empty()) { | |
253 consumer_->ReturnBuffer(buffers_.front()); | |
254 buffers_.pop_front(); | |
255 } | |
256 | |
257 if (!done.is_null()) | |
258 done.Run(); | |
259 } | |
260 | |
261 void SoftwareVideoRenderer::Core::DrawBuffer(webrtc::DesktopFrame* buffer) { | |
262 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
263 DCHECK(clip_area_.width() <= buffer->size().width() && | |
264 clip_area_.height() <= buffer->size().height()); | |
265 | |
266 buffers_.push_back(buffer); | |
267 SchedulePaint(); | |
268 } | |
269 | |
270 void SoftwareVideoRenderer::Core::InvalidateRegion( | |
271 const webrtc::DesktopRegion& region) { | |
272 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
273 | |
274 if (decoder_.get()) { | |
275 decoder_->Invalidate(view_size_, region); | |
276 SchedulePaint(); | |
277 } | |
278 } | |
279 | |
280 void SoftwareVideoRenderer::Core::SetOutputSizeAndClip( | |
281 const webrtc::DesktopSize& view_size, | |
282 const webrtc::DesktopRect& clip_area) { | |
283 DCHECK(decode_task_runner_->BelongsToCurrentThread()); | |
284 | |
285 // The whole frame needs to be repainted if the scaling factor has changed. | |
286 if (!view_size_.equals(view_size) && decoder_.get()) { | |
287 webrtc::DesktopRegion region; | |
288 region.AddRect(webrtc::DesktopRect::MakeSize(view_size)); | |
289 decoder_->Invalidate(view_size, region); | |
290 } | |
291 | |
292 if (!view_size_.equals(view_size) || | |
293 !clip_area_.equals(clip_area)) { | |
294 view_size_ = view_size; | |
295 clip_area_ = clip_area; | |
296 | |
297 // Return buffers that are smaller than needed to the consumer for | |
298 // reuse/reallocation. | |
299 std::list<webrtc::DesktopFrame*>::iterator i = buffers_.begin(); | |
300 while (i != buffers_.end()) { | |
301 if ((*i)->size().width() < clip_area_.width() || | |
302 (*i)->size().height() < clip_area_.height()) { | |
303 consumer_->ReturnBuffer(*i); | |
304 i = buffers_.erase(i); | |
305 } else { | |
306 ++i; | |
307 } | |
308 } | |
309 | |
310 SchedulePaint(); | |
311 } | |
312 } | |
313 | |
314 SoftwareVideoRenderer::SoftwareVideoRenderer( | |
315 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, | |
316 scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner, | |
317 scoped_ptr<FrameConsumerProxy> consumer) | |
318 : decode_task_runner_(decode_task_runner), | |
319 core_(new Core(main_task_runner, decode_task_runner, consumer.Pass())), | |
320 weak_factory_(this) { | |
321 DCHECK(CalledOnValidThread()); | |
322 } | |
323 | |
324 SoftwareVideoRenderer::~SoftwareVideoRenderer() { | |
325 DCHECK(CalledOnValidThread()); | |
326 bool result = decode_task_runner_->DeleteSoon(FROM_HERE, core_.release()); | |
327 DCHECK(result); | |
328 } | |
329 | |
330 void SoftwareVideoRenderer::OnSessionConfig( | |
331 const protocol::SessionConfig& config) { | |
332 DCHECK(CalledOnValidThread()); | |
333 decode_task_runner_->PostTask( | |
334 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::OnSessionConfig, | |
335 base::Unretained(core_.get()), config)); | |
336 } | |
337 | |
338 ChromotingStats* SoftwareVideoRenderer::GetStats() { | 132 ChromotingStats* SoftwareVideoRenderer::GetStats() { |
339 DCHECK(CalledOnValidThread()); | 133 DCHECK(thread_checker_.CalledOnValidThread()); |
340 return &stats_; | 134 return &stats_; |
341 } | 135 } |
342 | 136 |
343 protocol::VideoStub* SoftwareVideoRenderer::GetVideoStub() { | 137 protocol::VideoStub* SoftwareVideoRenderer::GetVideoStub() { |
| 138 DCHECK(thread_checker_.CalledOnValidThread()); |
344 return this; | 139 return this; |
345 } | 140 } |
346 | 141 |
347 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet, | 142 void SoftwareVideoRenderer::ProcessVideoPacket(scoped_ptr<VideoPacket> packet, |
348 const base::Closure& done) { | 143 const base::Closure& done) { |
349 DCHECK(CalledOnValidThread()); | 144 DCHECK(thread_checker_.CalledOnValidThread()); |
| 145 |
| 146 base::ScopedClosureRunner done_runner(done); |
350 | 147 |
351 stats_.RecordVideoPacketStats(*packet); | 148 stats_.RecordVideoPacketStats(*packet); |
352 | 149 |
353 // If the video packet is empty then drop it. Empty packets are used to | 150 // If the video packet is empty then drop it. Empty packets are used to |
354 // maintain activity on the network. | 151 // maintain activity on the network. |
355 if (!packet->has_data() || packet->data().size() == 0) { | 152 if (!packet->has_data() || packet->data().size() == 0) { |
356 decode_task_runner_->PostTask(FROM_HERE, done); | |
357 return; | 153 return; |
358 } | 154 } |
359 | 155 |
360 // Measure the latency between the last packet being received and presented. | 156 if (packet->format().has_screen_width() && |
361 base::Time decode_start = base::Time::Now(); | 157 packet->format().has_screen_height()) { |
| 158 current_size_.set(packet->format().screen_width(), |
| 159 packet->format().screen_height()); |
| 160 } |
362 | 161 |
363 base::Closure decode_done = base::Bind(&SoftwareVideoRenderer::OnPacketDone, | 162 if (current_size_.is_empty()) { |
364 weak_factory_.GetWeakPtr(), | 163 LOG(ERROR) << "Received VideoPacket with unknown size."; |
365 decode_start, done); | 164 return; |
| 165 } |
366 | 166 |
367 decode_task_runner_->PostTask(FROM_HERE, base::Bind( | 167 scoped_ptr<webrtc::DesktopFrame> frame = |
368 &SoftwareVideoRenderer::Core::DecodePacket, | 168 consumer_->AllocateFrame(current_size_); |
369 base::Unretained(core_.get()), base::Passed(&packet), decode_done)); | 169 base::PostTaskAndReplyWithResult( |
| 170 decode_task_runner_.get(), FROM_HERE, |
| 171 base::Bind(&DoDecodeFrame, decoder_.get(), base::Passed(&packet), |
| 172 base::Passed(&frame)), |
| 173 base::Bind(&SoftwareVideoRenderer::RenderFrame, |
| 174 weak_factory_.GetWeakPtr(), base::TimeTicks::Now(), |
| 175 done_runner.Release())); |
370 } | 176 } |
371 | 177 |
372 void SoftwareVideoRenderer::DrawBuffer(webrtc::DesktopFrame* buffer) { | 178 void SoftwareVideoRenderer::RenderFrame( |
373 decode_task_runner_->PostTask( | 179 base::TimeTicks decode_start_time, |
374 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::DrawBuffer, | 180 const base::Closure& done, |
375 base::Unretained(core_.get()), buffer)); | 181 scoped_ptr<webrtc::DesktopFrame> frame) { |
| 182 DCHECK(thread_checker_.CalledOnValidThread()); |
| 183 |
| 184 stats_.RecordDecodeTime( |
| 185 (base::TimeTicks::Now() - decode_start_time).InMilliseconds()); |
| 186 |
| 187 if (!frame) { |
| 188 if (!done.is_null()) |
| 189 done.Run(); |
| 190 return; |
| 191 } |
| 192 |
| 193 consumer_->DrawFrame( |
| 194 frame.Pass(), |
| 195 base::Bind(&SoftwareVideoRenderer::OnFrameRendered, |
| 196 weak_factory_.GetWeakPtr(), base::TimeTicks::Now(), done)); |
376 } | 197 } |
377 | 198 |
378 void SoftwareVideoRenderer::InvalidateRegion( | 199 void SoftwareVideoRenderer::OnFrameRendered(base::TimeTicks paint_start_time, |
379 const webrtc::DesktopRegion& region) { | 200 const base::Closure& done) { |
380 decode_task_runner_->PostTask( | 201 DCHECK(thread_checker_.CalledOnValidThread()); |
381 FROM_HERE, base::Bind(&SoftwareVideoRenderer::Core::InvalidateRegion, | |
382 base::Unretained(core_.get()), region)); | |
383 } | |
384 | 202 |
385 void SoftwareVideoRenderer::RequestReturnBuffers(const base::Closure& done) { | 203 stats_.RecordPaintTime( |
386 decode_task_runner_->PostTask( | 204 (base::TimeTicks::Now() - paint_start_time).InMilliseconds()); |
387 FROM_HERE, | |
388 base::Bind(&SoftwareVideoRenderer::Core::RequestReturnBuffers, | |
389 base::Unretained(core_.get()), done)); | |
390 } | |
391 | 205 |
392 void SoftwareVideoRenderer::SetOutputSizeAndClip( | 206 if (!done.is_null()) |
393 const webrtc::DesktopSize& view_size, | 207 done.Run(); |
394 const webrtc::DesktopRect& clip_area) { | |
395 decode_task_runner_->PostTask( | |
396 FROM_HERE, | |
397 base::Bind(&SoftwareVideoRenderer::Core::SetOutputSizeAndClip, | |
398 base::Unretained(core_.get()), view_size, clip_area)); | |
399 } | |
400 | |
401 void SoftwareVideoRenderer::OnPacketDone(base::Time decode_start, | |
402 const base::Closure& done) { | |
403 DCHECK(CalledOnValidThread()); | |
404 | |
405 // Record the latency between the packet being received and presented. | |
406 base::TimeDelta decode_time = base::Time::Now() - decode_start; | |
407 stats_.RecordDecodeTime(decode_time.InMilliseconds()); | |
408 | |
409 decode_task_runner_->PostTask(FROM_HERE, done); | |
410 } | 208 } |
411 | 209 |
412 } // namespace remoting | 210 } // namespace remoting |
OLD | NEW |