Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: ppapi/proxy/video_decoder_resource.cc

Issue 270213004: Implement Pepper PPB_VideoDecoder interface. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "ppapi/proxy/video_decoder_resource.h"
6
7 #include "base/bind.h"
8 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
9 #include "gpu/command_buffer/client/gles2_implementation.h"
10 #include "ipc/ipc_message.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/c/ppb_opengles2.h"
13 #include "ppapi/proxy/plugin_dispatcher.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
16 #include "ppapi/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h"
18 #include "ppapi/shared_impl/proxy_lock.h"
19 #include "ppapi/shared_impl/resource_tracker.h"
20 #include "ppapi/thunk/enter.h"
21
22 using ppapi::thunk::EnterResourceNoLock;
23 using ppapi::thunk::PPB_Graphics3D_API;
24 using ppapi::thunk::PPB_VideoDecoder_API;
25
26 namespace ppapi {
27 namespace proxy {
28
29 namespace {
30
31 // These constants should be kept in sync with PepperVideoDecoderHost.
32 // Maximum number of concurrent decodes which can be pending.
33 const uint32_t kMaximumPendingDecodes = 8;
34 // Minimum size of shared-memory buffers we allocate (100 KB). Make them large
35 // since we try to reuse them.
36 const uint32_t kMinimumBitstreamBufferSize = 100 << 10;
37 // Maximum size of shared-memory buffers we allocate (4 MB). This should be
38 // enough for even 4K video at reasonable compression levels.
39 const uint32_t kMaximumBitstreamBufferSize = 4 << 20;
40
41 } // namespace
42
43 VideoDecoderResource::ShmBuffer::ShmBuffer(
44 scoped_ptr<base::SharedMemory> shm_ptr,
45 uint32_t size,
46 uint32_t shm_id)
47 : shm(shm_ptr.Pass()), size(size), addr(NULL), shm_id(shm_id) {
48 if (shm->Map(size))
dmichael (off chromium) 2014/05/20 22:40:38 Given this is what size means, do you need the siz
bbudge 2014/05/22 18:34:55 Done.
49 addr = shm->memory();
50 DCHECK(addr);
51 }
52
53 VideoDecoderResource::ShmBuffer::~ShmBuffer() {
54 }
55
56 VideoDecoderResource::Texture::Texture(uint32_t texture_target,
57 const PP_Size& size)
58 : texture_target(texture_target), size(size) {
59 }
60
61 VideoDecoderResource::Texture::~Texture() {
62 }
63
64 VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id)
65 : decode_id(decode_id), texture_id(texture_id) {
66 }
67
68 VideoDecoderResource::Picture::~Picture() {
69 }
70
71 VideoDecoderResource::VideoDecoderResource(Connection connection,
72 PP_Instance instance)
73 : PluginResource(connection, instance),
74 next_decode_id_(0),
75 decode_size_(0),
76 decode_buffer_(NULL),
77 get_shm_buffer_pending_(false),
78 pending_shm_id_(0),
79 get_picture_(NULL),
80 gles2_impl_(NULL),
81 initialized_(false),
82 testing_(false),
83 // Set |decoder_last_error_| to PP_OK after successful initialization.
84 // This makes error checking a little more concise, since we can check
85 // that the decoder has been initialized and hasn't returned an error by
86 // just testing |decoder_last_error_|.
87 decoder_last_error_(PP_ERROR_FAILED) {
88 // Clear the decode_ids_ array.
89 memset(decode_ids_, 0, arraysize(decode_ids_));
90 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create());
91 }
92
93 VideoDecoderResource::~VideoDecoderResource() {
94 // Destroy any textures which haven't been dismissed.
95 TextureMap::iterator it = textures_.begin();
96 for (; it != textures_.end(); ++it)
97 DeleteGLTexture(it->first);
98 }
99
100 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() {
101 return this;
102 }
103
104 int32_t VideoDecoderResource::Initialize(
105 PP_Resource graphics_context,
106 PP_VideoProfile profile,
107 PP_Bool allow_software_fallback,
108 scoped_refptr<TrackedCallback> callback) {
109 if (initialized_)
110 return PP_ERROR_FAILED;
111 if (profile < 0 || profile > PP_VIDEOPROFILE_MAX)
112 return PP_ERROR_BADARGUMENT;
113 if (initialize_callback_)
114 return PP_ERROR_INPROGRESS;
115 if (!graphics_context)
116 return PP_ERROR_BADRESOURCE;
117
118 // Create a new Graphics3D resource that can create texture resources to share
119 // with the plugin. We can't use the plugin's Graphics3D, since we create
120 // textures on a proxy thread, and would interfere with the plugin.
121 thunk::EnterResourceCreationNoLock enter_create(pp_instance());
122 if (enter_create.failed())
123 return PP_ERROR_FAILED;
124 int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE};
125 HostResource host_resource;
126 if (!testing_) {
127 graphics3d_ =
128 ScopedPPResource(ScopedPPResource::PassRef(),
129 enter_create.functions()->CreateGraphics3D(
130 pp_instance(), graphics_context, attrib_list));
131 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(),
132 true);
133 if (enter_graphics.failed())
134 return PP_ERROR_BADRESOURCE;
135
136 PPB_Graphics3D_Shared* ppb_graphics3d_shared =
137 static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object());
138 gles2_impl_ = ppb_graphics3d_shared->gles2_impl();
139 host_resource = ppb_graphics3d_shared->host_resource();
140 }
141
142 initialize_callback_ = callback;
143
144 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>(
145 RENDERER,
146 PpapiHostMsg_VideoDecoder_Initialize(
147 host_resource, profile, PP_ToBool(allow_software_fallback)),
148 base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this));
149
150 return PP_OK_COMPLETIONPENDING;
151 }
152
153 int32_t VideoDecoderResource::Decode(uint32_t decode_id,
154 uint32_t size,
155 const void* buffer,
156 scoped_refptr<TrackedCallback> callback) {
157 if (decoder_last_error_)
158 return decoder_last_error_;
159 if (flush_callback_ || reset_callback_)
160 return PP_ERROR_FAILED;
161 if (decode_callback_)
162 return PP_ERROR_INPROGRESS;
163 if (size > kMaximumBitstreamBufferSize)
164 return PP_ERROR_NOMEMORY;
165
166 // Save decode_id in a ring buffer. The ring buffer is large enough to store
167 // decode_id for the maximim picture delay.
dmichael (off chromium) 2014/05/20 22:40:38 maximim->maximum
bbudge 2014/05/22 18:34:55 Done.
168 decode_ids_[next_decode_id_] = decode_id;
dmichael (off chromium) 2014/05/20 22:40:38 So, decode_id is from the plugin and can be anythi
bbudge 2014/05/22 18:34:55 The resource and host both maintain a count of dec
169 next_decode_id_ = (next_decode_id_ + 1) % kMaximumPictureLatency;
170
171 return TryDecode(size, buffer, callback);
172 }
173
174 int32_t VideoDecoderResource::GetPicture(
175 PP_VideoPicture* picture,
176 scoped_refptr<TrackedCallback> callback) {
177 if (decoder_last_error_)
178 return decoder_last_error_;
179 if (reset_callback_)
180 return PP_ERROR_FAILED;
181 if (get_picture_callback_)
182 return PP_ERROR_INPROGRESS;
183
184 // If the next picture is ready, return it synchronously.
185 if (!received_pictures_.empty()) {
186 WriteNextPicture(picture);
187 return PP_OK;
188 }
189
190 get_picture_callback_ = callback;
191 get_picture_ = picture;
192 return PP_OK_COMPLETIONPENDING;
193 }
194
195 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) {
196 if (decoder_last_error_)
197 return;
198 if (reset_callback_)
199 return;
200
201 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id));
202 }
203
204 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) {
205 if (decoder_last_error_)
206 return decoder_last_error_;
207 if (reset_callback_)
208 return PP_ERROR_FAILED;
209 if (flush_callback_)
210 return PP_ERROR_INPROGRESS;
211 flush_callback_ = callback;
212
213 Call<PpapiPluginMsg_VideoDecoder_FlushReply>(
214 RENDERER,
215 PpapiHostMsg_VideoDecoder_Flush(),
216 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this));
217
218 return PP_OK_COMPLETIONPENDING;
219 }
220
221 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) {
222 if (decoder_last_error_)
223 return decoder_last_error_;
224 if (flush_callback_)
225 return PP_ERROR_FAILED;
226 if (reset_callback_)
227 return PP_ERROR_INPROGRESS;
228 reset_callback_ = callback;
229
230 // Cause any pending Decode or GetPicture callbacks to abort immediately.
231 // Reentrancy isn't a problem, since all calls fail until Reset completes.
232 if (TrackedCallback::IsPending(decode_callback_))
233 decode_callback_->Abort();
234 decode_callback_ = NULL;
235 if (TrackedCallback::IsPending(get_picture_callback_))
236 get_picture_callback_->Abort();
237 get_picture_callback_ = NULL;
238 Call<PpapiPluginMsg_VideoDecoder_ResetReply>(
239 RENDERER,
240 PpapiHostMsg_VideoDecoder_Reset(),
241 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this));
242
243 return PP_OK_COMPLETIONPENDING;
244 }
245
246 void VideoDecoderResource::OnReplyReceived(
247 const ResourceMessageReplyParams& params,
248 const IPC::Message& msg) {
249 PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg)
250 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
251 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures)
252 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
253 PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady)
254 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
255 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture)
256 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
257 PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError)
258 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
259 PluginResource::OnReplyReceived(params, msg))
260 PPAPI_END_MESSAGE_MAP()
261 }
262
263 void VideoDecoderResource::SetForTest() {
264 testing_ = true;
265 }
266
267 void VideoDecoderResource::OnPluginMsgRequestTextures(
268 const ResourceMessageReplyParams& params,
269 uint32_t num_textures,
270 const PP_Size& size,
271 uint32_t texture_target) {
272 DCHECK(num_textures);
273 std::vector<uint32_t> texture_ids(num_textures);
274 if (gles2_impl_) {
275 gles2_impl_->GenTextures(num_textures, &texture_ids.front());
276 for (uint32_t i = 0; i < num_textures; ++i) {
277 gles2_impl_->ActiveTexture(GL_TEXTURE0);
278 gles2_impl_->BindTexture(texture_target, texture_ids[i]);
279 gles2_impl_->TexParameteri(
280 texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
281 gles2_impl_->TexParameteri(
282 texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
283 gles2_impl_->TexParameterf(
284 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
285 gles2_impl_->TexParameterf(
286 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
287
288 if (texture_target == GL_TEXTURE_2D) {
289 gles2_impl_->TexImage2D(texture_target,
290 0,
291 GL_RGBA,
292 size.width,
293 size.height,
294 0,
295 GL_RGBA,
296 GL_UNSIGNED_BYTE,
297 NULL);
298 }
299
300 textures_.insert(
301 std::make_pair(texture_ids[i], Texture(texture_target, size)));
302 }
303 gles2_impl_->Flush();
304 } else if (testing_) {
305 // Create some fake texture ids so we can test picture handling.
306 for (uint32_t i = 0; i < num_textures; ++i) {
307 texture_ids[i] = i + 1;
308 textures_.insert(
309 std::make_pair(texture_ids[i], Texture(texture_target, size)));
310 }
311 }
312
313 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids));
314 }
315
316 void VideoDecoderResource::OnPluginMsgPictureReady(
317 const ResourceMessageReplyParams& params,
318 uint32_t decode_id,
319 uint32_t texture_id) {
320 received_pictures_.push(Picture(decode_id, texture_id));
321 // Prepare to accept another call to GetPicture in the callback.
322 scoped_refptr<TrackedCallback> callback;
323 callback.swap(get_picture_callback_);
324 PP_VideoPicture* picture = get_picture_;
325 get_picture_ = NULL;
326 if (TrackedCallback::IsPending(callback)) {
327 WriteNextPicture(picture);
328 callback->Run(PP_OK);
329 }
330 }
331
332 void VideoDecoderResource::OnPluginMsgDismissPicture(
333 const ResourceMessageReplyParams& params,
334 uint32_t texture_id) {
335 DeleteGLTexture(texture_id);
336 textures_.erase(texture_id);
337 }
338
339 void VideoDecoderResource::OnPluginMsgNotifyError(
340 const ResourceMessageReplyParams& params,
341 int32_t error) {
342 decoder_last_error_ = error;
343 // Cause any pending Decode or GetPicture callbacks to run immediately.
344 // Reentrancy isn't a problem, since the resource is unusable now.
345 if (TrackedCallback::IsPending(decode_callback_))
346 decode_callback_->Run(decoder_last_error_);
347 decode_callback_ = NULL;
348 if (TrackedCallback::IsPending(get_picture_callback_))
349 get_picture_callback_->Run(decoder_last_error_);
350 get_picture_callback_ = NULL;
351 }
352
353 void VideoDecoderResource::OnPluginMsgInitializeComplete(
354 const ResourceMessageReplyParams& params) {
355 decoder_last_error_ = params.result();
356 if (decoder_last_error_ == PP_OK)
357 initialized_ = true;
358
359 // Let the plugin call Initialize again from its callback in case of failure.
360 scoped_refptr<TrackedCallback> callback;
361 callback.swap(initialize_callback_);
362 callback->Run(decoder_last_error_);
363 }
364
365 void VideoDecoderResource::OnPluginMsgGetShmComplete(
366 const ResourceMessageReplyParams& params,
367 uint32_t size) {
368 get_shm_buffer_pending_ = false;
369 uint32_t shm_id = pending_shm_id_;
dmichael (off chromium) 2014/05/20 22:40:38 So you only allow one request for a shared memory
bbudge 2014/05/22 18:34:55 It does add latency, but as discussed offline, it
370 int32_t result = params.result();
371 // Return the error to the plugin. It has the option of calling Reset and
372 // trying to seek around a problem frame.
373 if (result != PP_OK) {
374 RunDecodeCallback(PP_ERROR_NOMEMORY);
375 return;
376 }
377
378 base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle();
379 if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) {
380 RunDecodeCallback(PP_ERROR_NOMEMORY);
381 return;
382 }
383 scoped_ptr<base::SharedMemory> shm(
384 new base::SharedMemory(shm_handle, false /* read_only */));
385 scoped_ptr<ShmBuffer> shm_buffer(new ShmBuffer(shm.Pass(), size, shm_id));
386 if (!shm_buffer->addr) {
387 RunDecodeCallback(PP_ERROR_NOMEMORY);
388 return;
389 }
390
391 if (shm_id == shm_buffers_.size()) {
392 shm_buffers_.push_back(shm_buffer.release());
393 } else {
394 // When we grow a shm buffer, we always pick available_shm_buffers_.back().
395 // Remove it from the list now.
396 available_shm_buffers_.pop_back();
397 // Delete manually, since ScopedVector doesn't do the right thing.
398 delete shm_buffers_[shm_id];
399 shm_buffers_[shm_id] = shm_buffer.release();
400 }
401
402 SendDecodeMessage(shm_id);
dmichael (off chromium) 2014/05/20 22:40:38 So this adds to latency at the start of decode als
bbudge 2014/05/22 18:34:55 Not always. In the steady state, Decode calls TryD
403 RunDecodeCallback(PP_OK);
404 }
405
406 void VideoDecoderResource::OnPluginMsgDecodeComplete(
407 const ResourceMessageReplyParams& params,
408 uint32_t shm_id) {
409 if (shm_id >= shm_buffers_.size()) {
410 NOTREACHED();
411 return;
412 }
413 // Return the shm buffer to the available list.
414 available_shm_buffers_.push_back(shm_buffers_[shm_id]);
415
416 if (decode_callback_) {
417 // A buffer is free and there's no buffer awaiting decode. Allow the plugin
418 // to call Decode again.
dmichael (off chromium) 2014/05/20 22:40:38 I think it would be clearer with this comment insi
bbudge 2014/05/22 18:34:55 Done.
419 if (!decode_buffer_) {
420 RunDecodeCallback(PP_OK);
421 return;
422 }
423
424 // decode_buffer_ is waiting for a shm buffer allocated specifically for it.
425 if (get_shm_buffer_pending_)
426 return;
427
428 // Re-try the waiting decode.
429 int32_t result = TryDecode(decode_size_, decode_buffer_, decode_callback_);
430 // Return success or failure, but not a second completion pending result.
431 if (result != PP_OK_COMPLETIONPENDING)
432 RunDecodeCallback(result);
433 }
434 }
435
436 void VideoDecoderResource::OnPluginMsgFlushComplete(
437 const ResourceMessageReplyParams& params) {
438 if (get_picture_callback_) {
439 scoped_refptr<TrackedCallback> callback;
440 callback.swap(get_picture_callback_);
441 callback->Abort();
442 }
443
444 scoped_refptr<TrackedCallback> callback;
445 callback.swap(flush_callback_);
446 callback->Run(params.result());
447 }
448
449 void VideoDecoderResource::OnPluginMsgResetComplete(
450 const ResourceMessageReplyParams& params) {
451 scoped_refptr<TrackedCallback> callback;
452 callback.swap(reset_callback_);
453 callback->Run(params.result());
454 }
455
456 int32_t VideoDecoderResource::TryDecode(
457 uint32_t size,
458 const void* buffer,
459 scoped_refptr<TrackedCallback> callback) {
460 decode_size_ = size;
461 decode_buffer_ = buffer;
462 bool can_create_buffers = shm_buffers_.size() < kMaximumPendingDecodes;
463 // If the last available buffer is big enough, send the Decode. We don't try
464 // to do anything more sophisticated, since we expect in the steady state to
465 // have a pool of roughly equal sized buffers.
466 if (!available_shm_buffers_.empty() &&
467 available_shm_buffers_.back()->size >= size) {
468 ShmBuffer* shm_buffer = available_shm_buffers_.back();
469 available_shm_buffers_.pop_back();
470 SendDecodeMessage(shm_buffer->shm_id);
471 // If we have another free buffer, or we can still create new buffers, let
472 // the plugin call Decode again.
473 if (!available_shm_buffers_.empty() || can_create_buffers)
474 return PP_OK;
475
476 // All buffers are busy and we can't create more. Delay completion until a
477 // buffer is available.
478 decode_callback_ = callback;
479 return PP_OK_COMPLETIONPENDING;
480 }
481
482 decode_callback_ = callback;
483
484 // Copy the plugin's buffer if we haven't already. This only happens at
485 // startup when we're creating our pool of shm buffers, or when the plugin
486 // sends us a large buffer that won't fit in the last available shm buffer.
487 if (!temp_buffer_) {
488 temp_buffer_.reset(new char[size]);
489 memcpy(temp_buffer_.get(), decode_buffer_, decode_size_);
490 decode_buffer_ = temp_buffer_.get();
491 }
492
493 // If all buffers are busy and we can't create any more, then the plugin must
494 // wait for one to become free.
495 if (available_shm_buffers_.empty() && !can_create_buffers)
496 return PP_OK_COMPLETIONPENDING;
497
498 // If we can create a new buffer, signal the host by sending an index one
499 // greater than the number of existing buffers. If we can't create more,
500 // signal the host to grow the last available free shm buffer by passing an
501 // index in the legal range.
502 if (can_create_buffers)
503 pending_shm_id_ = static_cast<uint32_t>(shm_buffers_.size());
504 else
505 pending_shm_id_ = available_shm_buffers_.back()->shm_id;
506
507 get_shm_buffer_pending_ = true;
508 uint32_t alloc_size = std::max(size, kMinimumBitstreamBufferSize);
509 Call<PpapiPluginMsg_VideoDecoder_GetShmReply>(
510 RENDERER,
511 PpapiHostMsg_VideoDecoder_GetShm(alloc_size, pending_shm_id_),
512 base::Bind(&VideoDecoderResource::OnPluginMsgGetShmComplete, this));
513
514 return PP_OK_COMPLETIONPENDING;
515 }
516
517 void VideoDecoderResource::SendDecodeMessage(uint32_t shm_id) {
518 DCHECK(shm_id < shm_buffers_.size());
519 ShmBuffer* shm_buffer = shm_buffers_[shm_id];
520 memcpy(shm_buffer->addr, decode_buffer_, decode_size_);
521 if (temp_buffer_)
522 temp_buffer_.reset();
523
524 // We don't bother sending a decode_id value to the host. It uses its own
525 // counter to assign a unique id to the Decode in sync with this class, and
526 // returns the right value in the PictureReady message, which we use to look
527 // up the value we saved in decode_ids_.
528 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>(
529 RENDERER,
530 PpapiHostMsg_VideoDecoder_Decode(shm_id, decode_size_),
531 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this));
532
533 decode_buffer_ = NULL;
534 decode_size_ = 0;
535 }
536
537 void VideoDecoderResource::RunDecodeCallback(int32_t result) {
538 scoped_refptr<TrackedCallback> callback;
539 callback.swap(decode_callback_);
540 callback->Run(result);
541 }
542
543 void VideoDecoderResource::DeleteGLTexture(uint32_t id) {
544 if (gles2_impl_) {
545 gles2_impl_->DeleteTextures(1, &id);
546 gles2_impl_->Flush();
547 }
548 }
549
550 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) {
551 DCHECK(!received_pictures_.empty());
552 Picture& picture = received_pictures_.front();
553 uint32_t texture_id = picture.texture_id;
554 TextureMap::iterator it = textures_.find(texture_id);
555 DCHECK(it != textures_.end());
556 // Use decode_id to get the plugin's id that we saved in the ring buffer.
557 uint32_t decode_id = picture.decode_id % kMaximumPictureLatency;
558 pp_picture->decode_id = decode_ids_[decode_id];
dmichael (off chromium) 2014/05/20 22:40:38 How do you know that the picture.decode_id will ma
bbudge 2014/05/22 18:34:55 The resource and host both count decodes to give e
559 pp_picture->texture_id = texture_id;
560 pp_picture->texture_target = it->second.texture_target;
561 pp_picture->texture_size = it->second.size;
562 received_pictures_.pop();
563 }
564
565 } // namespace proxy
566 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698