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 <utility>
8
9 #include "base/bind.h"
10 #include "gpu/command_buffer/client/gles2_cmd_helper.h"
11 #include "gpu/command_buffer/client/gles2_implementation.h"
12 #include "gpu/command_buffer/common/mailbox.h"
13 #include "ipc/ipc_message.h"
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/ppb_opengles2.h"
16 #include "ppapi/proxy/plugin_dispatcher.h"
17 #include "ppapi/proxy/ppapi_messages.h"
18 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
19 #include "ppapi/proxy/serialized_handle.h"
20 #include "ppapi/proxy/video_decoder_constants.h"
21 #include "ppapi/shared_impl/ppapi_globals.h"
22 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h"
23 #include "ppapi/shared_impl/proxy_lock.h"
24 #include "ppapi/shared_impl/resource_tracker.h"
25 #include "ppapi/thunk/enter.h"
26
27 using ppapi::thunk::EnterResourceNoLock;
28 using ppapi::thunk::PPB_Graphics3D_API;
29 using ppapi::thunk::PPB_VideoDecoder_API;
30
31 namespace ppapi {
32 namespace proxy {
33
ShmBuffer(base::UnsafeSharedMemoryRegion region,uint32_t shm_id)34 VideoDecoderResource::ShmBuffer::ShmBuffer(
35 base::UnsafeSharedMemoryRegion region,
36 uint32_t shm_id)
37 : region(std::move(region)), shm_id(shm_id) {
38 mapping = this->region.Map();
39 if (mapping.IsValid())
40 addr = mapping.memory();
41 }
42
~ShmBuffer()43 VideoDecoderResource::ShmBuffer::~ShmBuffer() {
44 }
45
Texture(uint32_t texture_target,const PP_Size & size)46 VideoDecoderResource::Texture::Texture(uint32_t texture_target,
47 const PP_Size& size)
48 : texture_target(texture_target), size(size) {
49 }
50
~Texture()51 VideoDecoderResource::Texture::~Texture() {
52 }
53
Picture(int32_t decode_id,uint32_t texture_id,const PP_Rect & visible_rect)54 VideoDecoderResource::Picture::Picture(int32_t decode_id,
55 uint32_t texture_id,
56 const PP_Rect& visible_rect)
57 : decode_id(decode_id), texture_id(texture_id), visible_rect(visible_rect) {
58 }
59
~Picture()60 VideoDecoderResource::Picture::~Picture() {
61 }
62
VideoDecoderResource(Connection connection,PP_Instance instance)63 VideoDecoderResource::VideoDecoderResource(Connection connection,
64 PP_Instance instance)
65 : PluginResource(connection, instance),
66 num_decodes_(0),
67 min_picture_count_(0),
68 get_picture_(NULL),
69 get_picture_0_1_(NULL),
70 gles2_impl_(NULL),
71 initialized_(false),
72 testing_(false),
73 // Set |decoder_last_error_| to PP_OK after successful initialization.
74 // This makes error checking a little more concise, since we can check
75 // that the decoder has been initialized and hasn't returned an error by
76 // just testing |decoder_last_error_|.
77 decoder_last_error_(PP_ERROR_FAILED) {
78 // Clear the decode_ids_ array.
79 memset(decode_ids_, 0, sizeof(decode_ids_));
80 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create());
81 }
82
~VideoDecoderResource()83 VideoDecoderResource::~VideoDecoderResource() {
84 // Destroy any textures which haven't been dismissed.
85 TextureMap::iterator it = textures_.begin();
86 for (; it != textures_.end(); ++it)
87 DeleteGLTexture(it->first);
88 }
89
AsPPB_VideoDecoder_API()90 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() {
91 return this;
92 }
93
Initialize0_1(PP_Resource graphics_context,PP_VideoProfile profile,PP_Bool allow_software_fallback,scoped_refptr<TrackedCallback> callback)94 int32_t VideoDecoderResource::Initialize0_1(
95 PP_Resource graphics_context,
96 PP_VideoProfile profile,
97 PP_Bool allow_software_fallback,
98 scoped_refptr<TrackedCallback> callback) {
99 return Initialize(graphics_context,
100 profile,
101 allow_software_fallback
102 ? PP_HARDWAREACCELERATION_WITHFALLBACK
103 : PP_HARDWAREACCELERATION_ONLY,
104 0,
105 callback);
106 }
107
Initialize0_2(PP_Resource graphics_context,PP_VideoProfile profile,PP_HardwareAcceleration acceleration,scoped_refptr<TrackedCallback> callback)108 int32_t VideoDecoderResource::Initialize0_2(
109 PP_Resource graphics_context,
110 PP_VideoProfile profile,
111 PP_HardwareAcceleration acceleration,
112 scoped_refptr<TrackedCallback> callback) {
113 return Initialize(graphics_context,
114 profile,
115 acceleration,
116 0,
117 callback);
118 }
119
Initialize(PP_Resource graphics_context,PP_VideoProfile profile,PP_HardwareAcceleration acceleration,uint32_t min_picture_count,scoped_refptr<TrackedCallback> callback)120 int32_t VideoDecoderResource::Initialize(
121 PP_Resource graphics_context,
122 PP_VideoProfile profile,
123 PP_HardwareAcceleration acceleration,
124 uint32_t min_picture_count,
125 scoped_refptr<TrackedCallback> callback) {
126 if (initialized_)
127 return PP_ERROR_FAILED;
128 if (profile < 0 || profile > PP_VIDEOPROFILE_MAX)
129 return PP_ERROR_BADARGUMENT;
130 if (min_picture_count > kMaximumPictureCount)
131 return PP_ERROR_BADARGUMENT;
132 if (initialize_callback_.get())
133 return PP_ERROR_INPROGRESS;
134 if (!graphics_context)
135 return PP_ERROR_BADRESOURCE;
136
137 min_picture_count_ = min_picture_count;
138
139 HostResource host_resource;
140 if (!testing_) {
141 // Create a new Graphics3D resource that can create texture resources to
142 // share with the plugin. We can't use the plugin's Graphics3D, since we
143 // create textures on a proxy thread, and would interfere with the plugin.
144 thunk::EnterResourceCreationNoLock enter_create(pp_instance());
145 if (enter_create.failed())
146 return PP_ERROR_FAILED;
147 int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE};
148 graphics3d_ =
149 ScopedPPResource(ScopedPPResource::PassRef(),
150 enter_create.functions()->CreateGraphics3D(
151 pp_instance(), graphics_context, attrib_list));
152 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(),
153 false);
154 if (enter_graphics.failed())
155 return PP_ERROR_BADRESOURCE;
156
157 PPB_Graphics3D_Shared* ppb_graphics3d_shared =
158 static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object());
159 gles2_impl_ = ppb_graphics3d_shared->gles2_impl();
160 host_resource = ppb_graphics3d_shared->host_resource();
161 }
162
163 initialize_callback_ = callback;
164
165 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>(
166 RENDERER,
167 PpapiHostMsg_VideoDecoder_Initialize(
168 host_resource, profile, acceleration, min_picture_count),
169 base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this));
170
171 return PP_OK_COMPLETIONPENDING;
172 }
173
Decode(uint32_t decode_id,uint32_t size,const void * buffer,scoped_refptr<TrackedCallback> callback)174 int32_t VideoDecoderResource::Decode(uint32_t decode_id,
175 uint32_t size,
176 const void* buffer,
177 scoped_refptr<TrackedCallback> callback) {
178 if (decoder_last_error_)
179 return decoder_last_error_;
180 if (flush_callback_.get() || reset_callback_.get())
181 return PP_ERROR_FAILED;
182 if (decode_callback_.get())
183 return PP_ERROR_INPROGRESS;
184 if (size > kMaximumBitstreamBufferSize)
185 return PP_ERROR_NOMEMORY;
186
187 // If we allow the plugin to call Decode again, we must have somewhere to
188 // copy their buffer.
189 DCHECK(!available_shm_buffers_.empty() ||
190 shm_buffers_.size() < kMaximumPendingDecodes);
191
192 // Count up, wrapping back to 0 before overflowing.
193 int32_t uid = ++num_decodes_;
194 if (uid == std::numeric_limits<int32_t>::max())
195 num_decodes_ = 0;
196
197 // Save decode_id in a ring buffer. The ring buffer is sized to store
198 // decode_id for the maximum picture delay.
199 decode_ids_[uid % kMaximumPictureDelay] = decode_id;
200
201 if (available_shm_buffers_.empty() ||
202 available_shm_buffers_.back()->mapping.size() < size) {
203 uint32_t shm_id;
204 if (shm_buffers_.size() < kMaximumPendingDecodes) {
205 // Signal the host to create a new shm buffer by passing an index outside
206 // the legal range.
207 shm_id = static_cast<uint32_t>(shm_buffers_.size());
208 } else {
209 // Signal the host to grow a buffer by passing a legal index. Choose the
210 // last available shm buffer for simplicity.
211 shm_id = available_shm_buffers_.back()->shm_id;
212 available_shm_buffers_.pop_back();
213 }
214
215 // Synchronously get shared memory. Use GenericSyncCall so we can get the
216 // reply params, which contain the handle.
217 uint32_t shm_size = 0;
218 IPC::Message reply;
219 ResourceMessageReplyParams reply_params;
220 int32_t result =
221 GenericSyncCall(RENDERER,
222 PpapiHostMsg_VideoDecoder_GetShm(shm_id, size),
223 &reply,
224 &reply_params);
225 if (result != PP_OK)
226 return PP_ERROR_FAILED;
227 if (!UnpackMessage<PpapiPluginMsg_VideoDecoder_GetShmReply>(reply,
228 &shm_size))
229 return PP_ERROR_FAILED;
230 base::UnsafeSharedMemoryRegion shm_region;
231 if (!reply_params.TakeUnsafeSharedMemoryRegionAtIndex(0, &shm_region) ||
232 !shm_region.IsValid() || shm_region.GetSize() != shm_size)
233 return PP_ERROR_NOMEMORY;
234 std::unique_ptr<ShmBuffer> shm_buffer(
235 new ShmBuffer(std::move(shm_region), shm_id));
236 if (!shm_buffer->addr)
237 return PP_ERROR_NOMEMORY;
238
239 available_shm_buffers_.push_back(shm_buffer.get());
240 if (shm_buffers_.size() < kMaximumPendingDecodes)
241 shm_buffers_.push_back(std::move(shm_buffer));
242 else
243 shm_buffers_[shm_id] = std::move(shm_buffer);
244 }
245
246 // At this point we should have shared memory to hold the plugin's buffer.
247 DCHECK(!available_shm_buffers_.empty() &&
248 available_shm_buffers_.back()->mapping.size() >= size);
249
250 ShmBuffer* shm_buffer = available_shm_buffers_.back();
251 available_shm_buffers_.pop_back();
252 memcpy(shm_buffer->addr, buffer, size);
253
254 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>(
255 RENDERER,
256 PpapiHostMsg_VideoDecoder_Decode(shm_buffer->shm_id, size, uid),
257 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this));
258
259 // If we have another free buffer, or we can still create new buffers, let
260 // the plugin call Decode again.
261 if (!available_shm_buffers_.empty() ||
262 shm_buffers_.size() < kMaximumPendingDecodes)
263 return PP_OK;
264
265 // All buffers are busy and we can't create more. Delay completion until a
266 // buffer is available.
267 decode_callback_ = callback;
268 return PP_OK_COMPLETIONPENDING;
269 }
270
GetPicture0_1(PP_VideoPicture_0_1 * picture,scoped_refptr<TrackedCallback> callback)271 int32_t VideoDecoderResource::GetPicture0_1(
272 PP_VideoPicture_0_1* picture,
273 scoped_refptr<TrackedCallback> callback) {
274 get_picture_0_1_ = picture;
275 return GetPicture(NULL, callback);
276 }
277
GetPicture(PP_VideoPicture * picture,scoped_refptr<TrackedCallback> callback)278 int32_t VideoDecoderResource::GetPicture(
279 PP_VideoPicture* picture,
280 scoped_refptr<TrackedCallback> callback) {
281 if (decoder_last_error_)
282 return decoder_last_error_;
283 if (reset_callback_.get())
284 return PP_ERROR_FAILED;
285 if (get_picture_callback_.get())
286 return PP_ERROR_INPROGRESS;
287
288 get_picture_ = picture;
289
290 // If the next picture is ready, return it synchronously.
291 if (!received_pictures_.empty()) {
292 WriteNextPicture();
293 return PP_OK;
294 }
295
296 get_picture_callback_ = callback;
297
298 return PP_OK_COMPLETIONPENDING;
299 }
300
RecyclePicture(const PP_VideoPicture * picture)301 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) {
302 if (decoder_last_error_)
303 return;
304
305 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id));
306 }
307
Flush(scoped_refptr<TrackedCallback> callback)308 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) {
309 if (decoder_last_error_)
310 return decoder_last_error_;
311 if (reset_callback_.get())
312 return PP_ERROR_FAILED;
313 if (flush_callback_.get())
314 return PP_ERROR_INPROGRESS;
315 flush_callback_ = callback;
316
317 Call<PpapiPluginMsg_VideoDecoder_FlushReply>(
318 RENDERER,
319 PpapiHostMsg_VideoDecoder_Flush(),
320 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this));
321
322 return PP_OK_COMPLETIONPENDING;
323 }
324
Reset(scoped_refptr<TrackedCallback> callback)325 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) {
326 if (decoder_last_error_)
327 return decoder_last_error_;
328 if (flush_callback_.get())
329 return PP_ERROR_FAILED;
330 if (reset_callback_.get())
331 return PP_ERROR_INPROGRESS;
332 reset_callback_ = callback;
333
334 // Cause any pending Decode or GetPicture callbacks to abort after we return,
335 // to avoid reentering the plugin.
336 if (TrackedCallback::IsPending(decode_callback_))
337 decode_callback_->PostAbort();
338 decode_callback_.reset();
339 if (TrackedCallback::IsPending(get_picture_callback_))
340 get_picture_callback_->PostAbort();
341 get_picture_callback_.reset();
342 Call<PpapiPluginMsg_VideoDecoder_ResetReply>(
343 RENDERER,
344 PpapiHostMsg_VideoDecoder_Reset(),
345 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this));
346
347 return PP_OK_COMPLETIONPENDING;
348 }
349
OnReplyReceived(const ResourceMessageReplyParams & params,const IPC::Message & msg)350 void VideoDecoderResource::OnReplyReceived(
351 const ResourceMessageReplyParams& params,
352 const IPC::Message& msg) {
353 PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg)
354 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
355 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures)
356 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
357 PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady)
358 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
359 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture)
360 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
361 PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError)
362 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
363 PluginResource::OnReplyReceived(params, msg))
364 PPAPI_END_MESSAGE_MAP()
365 }
366
SetForTest()367 void VideoDecoderResource::SetForTest() {
368 testing_ = true;
369 }
370
OnPluginMsgRequestTextures(const ResourceMessageReplyParams & params,uint32_t num_textures,const PP_Size & size,uint32_t texture_target)371 void VideoDecoderResource::OnPluginMsgRequestTextures(
372 const ResourceMessageReplyParams& params,
373 uint32_t num_textures,
374 const PP_Size& size,
375 uint32_t texture_target) {
376 DCHECK(num_textures);
377 DCHECK(num_textures >= min_picture_count_);
378 std::vector<uint32_t> texture_ids(num_textures);
379 std::vector<gpu::Mailbox> mailboxes(num_textures);
380 if (gles2_impl_) {
381 gles2_impl_->GenTextures(num_textures, texture_ids.data());
382 for (uint32_t i = 0; i < num_textures; ++i) {
383 gles2_impl_->ActiveTexture(GL_TEXTURE0);
384 gles2_impl_->BindTexture(texture_target, texture_ids[i]);
385 gles2_impl_->TexParameteri(
386 texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
387 gles2_impl_->TexParameteri(
388 texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
389 gles2_impl_->TexParameterf(
390 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
391 gles2_impl_->TexParameterf(
392 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
393
394 if (texture_target == GL_TEXTURE_2D) {
395 gles2_impl_->TexImage2D(texture_target,
396 0,
397 GL_RGBA,
398 size.width,
399 size.height,
400 0,
401 GL_RGBA,
402 GL_UNSIGNED_BYTE,
403 NULL);
404 }
405 gles2_impl_->ProduceTextureDirectCHROMIUM(texture_ids[i],
406 mailboxes[i].name);
407
408 textures_.insert(
409 std::make_pair(texture_ids[i], Texture(texture_target, size)));
410 }
411 gles2_impl_->Flush();
412 } else {
413 DCHECK(testing_);
414 // Create some fake texture ids so we can test picture handling.
415 for (uint32_t i = 0; i < num_textures; ++i) {
416 texture_ids[i] = i + 1;
417 textures_.insert(
418 std::make_pair(texture_ids[i], Texture(texture_target, size)));
419 }
420 }
421
422 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(
423 size, std::move(texture_ids), std::move(mailboxes)));
424 }
425
OnPluginMsgPictureReady(const ResourceMessageReplyParams & params,int32_t decode_id,uint32_t texture_id,const PP_Rect & visible_rect)426 void VideoDecoderResource::OnPluginMsgPictureReady(
427 const ResourceMessageReplyParams& params,
428 int32_t decode_id,
429 uint32_t texture_id,
430 const PP_Rect& visible_rect) {
431 received_pictures_.push(Picture(decode_id, texture_id, visible_rect));
432
433 if (TrackedCallback::IsPending(get_picture_callback_)) {
434 // The plugin may call GetPicture in its callback.
435 scoped_refptr<TrackedCallback> callback;
436 callback.swap(get_picture_callback_);
437 WriteNextPicture();
438 callback->Run(PP_OK);
439 }
440 }
441
OnPluginMsgDismissPicture(const ResourceMessageReplyParams & params,uint32_t texture_id)442 void VideoDecoderResource::OnPluginMsgDismissPicture(
443 const ResourceMessageReplyParams& params,
444 uint32_t texture_id) {
445 DeleteGLTexture(texture_id);
446 textures_.erase(texture_id);
447 }
448
OnPluginMsgNotifyError(const ResourceMessageReplyParams & params,int32_t error)449 void VideoDecoderResource::OnPluginMsgNotifyError(
450 const ResourceMessageReplyParams& params,
451 int32_t error) {
452 decoder_last_error_ = error;
453 // Cause any pending callbacks to run immediately. Reentrancy isn't a problem,
454 // since the plugin wasn't calling us.
455 RunCallbackWithError(&initialize_callback_);
456 RunCallbackWithError(&decode_callback_);
457 RunCallbackWithError(&get_picture_callback_);
458 RunCallbackWithError(&flush_callback_);
459 RunCallbackWithError(&reset_callback_);
460 }
461
OnPluginMsgInitializeComplete(const ResourceMessageReplyParams & params)462 void VideoDecoderResource::OnPluginMsgInitializeComplete(
463 const ResourceMessageReplyParams& params) {
464 decoder_last_error_ = params.result();
465 if (decoder_last_error_ == PP_OK)
466 initialized_ = true;
467
468 // Let the plugin call Initialize again from its callback in case of failure.
469 scoped_refptr<TrackedCallback> callback;
470 callback.swap(initialize_callback_);
471 callback->Run(decoder_last_error_);
472 }
473
OnPluginMsgDecodeComplete(const ResourceMessageReplyParams & params,uint32_t shm_id)474 void VideoDecoderResource::OnPluginMsgDecodeComplete(
475 const ResourceMessageReplyParams& params,
476 uint32_t shm_id) {
477 if (shm_id >= shm_buffers_.size()) {
478 NOTREACHED();
479 return;
480 }
481 // Make the shm buffer available.
482 available_shm_buffers_.push_back(shm_buffers_[shm_id].get());
483 // If the plugin is waiting, let it call Decode again.
484 if (decode_callback_.get()) {
485 scoped_refptr<TrackedCallback> callback;
486 callback.swap(decode_callback_);
487 callback->Run(PP_OK);
488 }
489 }
490
OnPluginMsgFlushComplete(const ResourceMessageReplyParams & params)491 void VideoDecoderResource::OnPluginMsgFlushComplete(
492 const ResourceMessageReplyParams& params) {
493 // All shm buffers should have been made available by now.
494 DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size());
495
496 if (get_picture_callback_.get()) {
497 scoped_refptr<TrackedCallback> callback;
498 callback.swap(get_picture_callback_);
499 callback->Abort();
500 }
501
502 scoped_refptr<TrackedCallback> callback;
503 callback.swap(flush_callback_);
504 callback->Run(params.result());
505 }
506
OnPluginMsgResetComplete(const ResourceMessageReplyParams & params)507 void VideoDecoderResource::OnPluginMsgResetComplete(
508 const ResourceMessageReplyParams& params) {
509 // All shm buffers should have been made available by now.
510 DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size());
511 // Recycle any pictures which haven't been passed to the plugin.
512 while (!received_pictures_.empty()) {
513 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(
514 received_pictures_.front().texture_id));
515 received_pictures_.pop();
516 }
517
518 scoped_refptr<TrackedCallback> callback;
519 callback.swap(reset_callback_);
520 callback->Run(params.result());
521 }
522
RunCallbackWithError(scoped_refptr<TrackedCallback> * callback)523 void VideoDecoderResource::RunCallbackWithError(
524 scoped_refptr<TrackedCallback>* callback) {
525 SafeRunCallback(callback, decoder_last_error_);
526 }
527
DeleteGLTexture(uint32_t id)528 void VideoDecoderResource::DeleteGLTexture(uint32_t id) {
529 if (gles2_impl_) {
530 gles2_impl_->DeleteTextures(1, &id);
531 gles2_impl_->Flush();
532 }
533 }
534
WriteNextPicture()535 void VideoDecoderResource::WriteNextPicture() {
536 DCHECK(!received_pictures_.empty());
537 Picture& picture = received_pictures_.front();
538
539 // Internally, we identify decodes by a unique id, which the host returns
540 // to us in the picture. Use this to get the plugin's decode_id.
541 uint32_t decode_id = decode_ids_[picture.decode_id % kMaximumPictureDelay];
542 uint32_t texture_id = picture.texture_id;
543 uint32_t texture_target = 0;
544 PP_Size texture_size = PP_MakeSize(0, 0);
545 TextureMap::iterator it = textures_.find(picture.texture_id);
546 if (it != textures_.end()) {
547 texture_target = it->second.texture_target;
548 texture_size = it->second.size;
549 } else {
550 NOTREACHED();
551 }
552
553 if (get_picture_) {
554 DCHECK(!get_picture_0_1_);
555 get_picture_->decode_id = decode_id;
556 get_picture_->texture_id = texture_id;
557 get_picture_->texture_target = texture_target;
558 get_picture_->texture_size = texture_size;
559 get_picture_->visible_rect = picture.visible_rect;
560 get_picture_ = NULL;
561 } else {
562 DCHECK(get_picture_0_1_);
563 get_picture_0_1_->decode_id = decode_id;
564 get_picture_0_1_->texture_id = texture_id;
565 get_picture_0_1_->texture_target = texture_target;
566 get_picture_0_1_->texture_size = texture_size;
567 get_picture_0_1_ = NULL;
568 }
569
570 received_pictures_.pop();
571 }
572
573 } // namespace proxy
574 } // namespace ppapi
575