OLD | NEW |
---|---|
(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/proxy/video_decoder_constants.h" | |
17 #include "ppapi/shared_impl/ppapi_globals.h" | |
18 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h" | |
19 #include "ppapi/shared_impl/proxy_lock.h" | |
20 #include "ppapi/shared_impl/resource_tracker.h" | |
21 #include "ppapi/thunk/enter.h" | |
22 | |
23 using ppapi::thunk::EnterResourceNoLock; | |
24 using ppapi::thunk::PPB_Graphics3D_API; | |
25 using ppapi::thunk::PPB_VideoDecoder_API; | |
26 | |
27 namespace ppapi { | |
28 namespace proxy { | |
29 | |
30 VideoDecoderResource::ShmBuffer::ShmBuffer( | |
31 scoped_ptr<base::SharedMemory> shm_ptr, | |
32 uint32_t size, | |
33 uint32_t shm_id) | |
34 : shm(shm_ptr.Pass()), addr(NULL), shm_id(shm_id) { | |
35 if (shm->Map(size)) | |
36 addr = shm->memory(); | |
37 DCHECK(addr); | |
38 } | |
39 | |
40 VideoDecoderResource::ShmBuffer::~ShmBuffer() { | |
41 } | |
42 | |
43 VideoDecoderResource::Texture::Texture(uint32_t texture_target, | |
44 const PP_Size& size) | |
45 : texture_target(texture_target), size(size) { | |
46 } | |
47 | |
48 VideoDecoderResource::Texture::~Texture() { | |
49 } | |
50 | |
51 VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id) | |
52 : decode_id(decode_id), texture_id(texture_id) { | |
53 } | |
54 | |
55 VideoDecoderResource::Picture::~Picture() { | |
56 } | |
57 | |
58 VideoDecoderResource::VideoDecoderResource(Connection connection, | |
59 PP_Instance instance) | |
60 : PluginResource(connection, instance), | |
61 num_decodes_(0), | |
62 shm_buffer_pending_(false), | |
63 pending_shm_id_(0), | |
64 get_picture_(NULL), | |
65 gles2_impl_(NULL), | |
66 initialized_(false), | |
67 testing_(false), | |
68 // Set |decoder_last_error_| to PP_OK after successful initialization. | |
69 // This makes error checking a little more concise, since we can check | |
70 // that the decoder has been initialized and hasn't returned an error by | |
71 // just testing |decoder_last_error_|. | |
72 decoder_last_error_(PP_ERROR_FAILED) { | |
73 // Clear the decode_ids_ array. | |
74 memset(decode_ids_, 0, arraysize(decode_ids_)); | |
75 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create()); | |
76 } | |
77 | |
78 VideoDecoderResource::~VideoDecoderResource() { | |
79 // Destroy any textures which haven't been dismissed. | |
80 TextureMap::iterator it = textures_.begin(); | |
81 for (; it != textures_.end(); ++it) | |
82 DeleteGLTexture(it->first); | |
83 } | |
84 | |
85 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() { | |
86 return this; | |
87 } | |
88 | |
89 int32_t VideoDecoderResource::Initialize( | |
90 PP_Resource graphics_context, | |
91 PP_VideoProfile profile, | |
92 PP_Bool allow_software_fallback, | |
93 scoped_refptr<TrackedCallback> callback) { | |
94 if (initialized_) | |
95 return PP_ERROR_FAILED; | |
96 if (profile < 0 || profile > PP_VIDEOPROFILE_MAX) | |
97 return PP_ERROR_BADARGUMENT; | |
98 if (initialize_callback_) | |
99 return PP_ERROR_INPROGRESS; | |
100 if (!graphics_context) | |
101 return PP_ERROR_BADRESOURCE; | |
102 | |
103 // Create a new Graphics3D resource that can create texture resources to share | |
104 // with the plugin. We can't use the plugin's Graphics3D, since we create | |
105 // textures on a proxy thread, and would interfere with the plugin. | |
106 thunk::EnterResourceCreationNoLock enter_create(pp_instance()); | |
107 if (enter_create.failed()) | |
108 return PP_ERROR_FAILED; | |
109 int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE}; | |
110 HostResource host_resource; | |
111 if (!testing_) { | |
112 graphics3d_ = | |
113 ScopedPPResource(ScopedPPResource::PassRef(), | |
114 enter_create.functions()->CreateGraphics3D( | |
115 pp_instance(), graphics_context, attrib_list)); | |
116 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(), | |
117 true); | |
118 if (enter_graphics.failed()) | |
119 return PP_ERROR_BADRESOURCE; | |
120 | |
121 PPB_Graphics3D_Shared* ppb_graphics3d_shared = | |
122 static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object()); | |
123 gles2_impl_ = ppb_graphics3d_shared->gles2_impl(); | |
124 host_resource = ppb_graphics3d_shared->host_resource(); | |
125 } | |
126 | |
127 initialize_callback_ = callback; | |
128 | |
129 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>( | |
130 RENDERER, | |
131 PpapiHostMsg_VideoDecoder_Initialize( | |
132 host_resource, profile, PP_ToBool(allow_software_fallback)), | |
133 base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this)); | |
134 | |
135 return PP_OK_COMPLETIONPENDING; | |
136 } | |
137 | |
138 int32_t VideoDecoderResource::Decode(uint32_t decode_id, | |
139 uint32_t size, | |
140 const void* buffer, | |
141 scoped_refptr<TrackedCallback> callback) { | |
142 if (decoder_last_error_) | |
143 return decoder_last_error_; | |
144 if (flush_callback_ || reset_callback_) | |
145 return PP_ERROR_FAILED; | |
146 if (decode_callback_) | |
147 return PP_ERROR_INPROGRESS; | |
148 if (size > kMaximumBitstreamBufferSize) | |
149 return PP_ERROR_NOMEMORY; | |
150 | |
151 // Save decode_id in a ring buffer. The ring buffer is large enough to store | |
152 // decode_id for the maximum picture delay. | |
153 decode_ids_[num_decodes_ % kMaximumPictureDelay] = decode_id; | |
154 num_decodes_++; | |
155 | |
156 DCHECK(!shm_buffer_pending_); | |
157 bool can_create_buffers = shm_buffers_.size() < kMaximumPendingDecodes; | |
158 | |
159 // If the last available buffer is big enough, send the Decode. We don't try | |
160 // to do anything more sophisticated, since we expect in the steady state to | |
161 // have a pool of roughly equal size buffers. | |
162 if (!available_shm_buffers_.empty() && | |
163 available_shm_buffers_.back()->shm->mapped_size() >= size) { | |
164 ShmBuffer* shm_buffer = available_shm_buffers_.back(); | |
165 available_shm_buffers_.pop_back(); | |
166 memcpy(shm_buffer->addr, buffer, size); | |
167 | |
168 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>( | |
169 RENDERER, | |
170 PpapiHostMsg_VideoDecoder_Decode(shm_buffer->shm_id, size), | |
171 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this)); | |
172 | |
173 // If we have another free buffer, or we can still create new buffers, let | |
174 // the plugin call Decode again. | |
175 if (!available_shm_buffers_.empty() || can_create_buffers) | |
176 return PP_OK; | |
177 | |
178 // All buffers are busy and we can't create more. Delay completion until a | |
179 // buffer is available. | |
180 decode_callback_ = callback; | |
181 return PP_OK_COMPLETIONPENDING; | |
182 } | |
183 | |
184 decode_callback_ = callback; | |
185 | |
186 // Copy the plugin's buffer. This only happens at startup, when we create the | |
187 // pool of shm buffers, or when the plugin sends us a large buffer that won't | |
188 // fit in the last available shm buffer. | |
189 const uint8_t* buffer_bytes = static_cast<const uint8_t*>(buffer); | |
190 std::vector<uint8_t> copy(buffer_bytes, buffer_bytes + size); | |
piman
2014/05/22 20:43:19
I don't like the idea of having 2 separate paths t
bbudge
2014/05/23 12:24:35
I made the shm creation synchronous.
| |
191 | |
192 if (can_create_buffers) { | |
193 // Signal the host to create a new shm buffer by passing an index outside | |
194 // the legal range. | |
195 pending_shm_id_ = static_cast<uint32_t>(shm_buffers_.size()); | |
196 } else { | |
197 // Signal the host to grow a buffer by passing a legal index. Choose the | |
198 // last available shm buffer for simplicity. | |
199 pending_shm_id_ = available_shm_buffers_.back()->shm_id; | |
200 available_shm_buffers_.pop_back(); | |
201 } | |
202 | |
203 shm_buffer_pending_ = true; | |
204 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>( | |
205 RENDERER, | |
206 PpapiHostMsg_VideoDecoder_DecodeBuffer(copy, pending_shm_id_), | |
207 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this)); | |
208 | |
209 return PP_OK_COMPLETIONPENDING; | |
210 } | |
211 | |
212 int32_t VideoDecoderResource::GetPicture( | |
213 PP_VideoPicture* picture, | |
214 scoped_refptr<TrackedCallback> callback) { | |
215 if (decoder_last_error_) | |
216 return decoder_last_error_; | |
217 if (reset_callback_) | |
218 return PP_ERROR_FAILED; | |
219 if (get_picture_callback_) | |
220 return PP_ERROR_INPROGRESS; | |
221 | |
222 // If the next picture is ready, return it synchronously. | |
223 if (!received_pictures_.empty()) { | |
224 WriteNextPicture(picture); | |
225 return PP_OK; | |
226 } | |
227 | |
228 get_picture_callback_ = callback; | |
229 get_picture_ = picture; | |
230 return PP_OK_COMPLETIONPENDING; | |
231 } | |
232 | |
233 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) { | |
234 if (decoder_last_error_) | |
235 return; | |
236 if (reset_callback_) | |
237 return; | |
238 | |
239 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id)); | |
240 } | |
241 | |
242 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) { | |
243 if (decoder_last_error_) | |
244 return decoder_last_error_; | |
245 if (reset_callback_) | |
246 return PP_ERROR_FAILED; | |
247 if (flush_callback_) | |
248 return PP_ERROR_INPROGRESS; | |
249 flush_callback_ = callback; | |
250 | |
251 Call<PpapiPluginMsg_VideoDecoder_FlushReply>( | |
252 RENDERER, | |
253 PpapiHostMsg_VideoDecoder_Flush(), | |
254 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this)); | |
255 | |
256 return PP_OK_COMPLETIONPENDING; | |
257 } | |
258 | |
259 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) { | |
260 if (decoder_last_error_) | |
261 return decoder_last_error_; | |
262 if (flush_callback_) | |
263 return PP_ERROR_FAILED; | |
264 if (reset_callback_) | |
265 return PP_ERROR_INPROGRESS; | |
266 reset_callback_ = callback; | |
267 | |
268 // Cause any pending Decode or GetPicture callbacks to abort after we return, | |
269 // to avoid reentering the plugin. | |
270 if (TrackedCallback::IsPending(decode_callback_)) | |
271 decode_callback_->PostAbort(); | |
272 decode_callback_ = NULL; | |
273 if (TrackedCallback::IsPending(get_picture_callback_)) | |
274 get_picture_callback_->PostAbort(); | |
275 get_picture_callback_ = NULL; | |
276 Call<PpapiPluginMsg_VideoDecoder_ResetReply>( | |
277 RENDERER, | |
278 PpapiHostMsg_VideoDecoder_Reset(), | |
279 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this)); | |
280 | |
281 return PP_OK_COMPLETIONPENDING; | |
282 } | |
283 | |
284 void VideoDecoderResource::OnReplyReceived( | |
285 const ResourceMessageReplyParams& params, | |
286 const IPC::Message& msg) { | |
287 PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg) | |
288 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
289 PpapiPluginMsg_VideoDecoder_CreatedShm, OnPluginMsgCreatedShm) | |
290 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
291 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures) | |
292 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
293 PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady) | |
294 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
295 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture) | |
296 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
297 PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError) | |
298 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( | |
299 PluginResource::OnReplyReceived(params, msg)) | |
300 PPAPI_END_MESSAGE_MAP() | |
301 } | |
302 | |
303 void VideoDecoderResource::SetForTest() { | |
304 testing_ = true; | |
305 } | |
306 | |
307 void VideoDecoderResource::OnPluginMsgCreatedShm( | |
308 const ResourceMessageReplyParams& params, | |
309 uint32_t size) { | |
310 // We are receiving an unsolicited reply with shm information, because we sent | |
311 // a "DecodeBuffer" message. We will receive a "DecodeReply" when the host is | |
312 // finished with the shm buffer, and we can make it available. Now just take | |
313 // the shm. Return memory errors to the plugin. It has the option of calling | |
314 // Reset and trying to seek around a frame that is very large. | |
315 shm_buffer_pending_ = false; | |
316 uint32_t shm_id = pending_shm_id_; | |
317 int32_t result = params.result(); | |
318 if (result != PP_OK) { | |
319 RunDecodeCallback(PP_ERROR_NOMEMORY); | |
320 return; | |
321 } | |
322 | |
323 base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle(); | |
324 if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) { | |
325 RunDecodeCallback(PP_ERROR_NOMEMORY); | |
326 return; | |
327 } | |
328 scoped_ptr<base::SharedMemory> shm( | |
329 new base::SharedMemory(shm_handle, false /* read_only */)); | |
330 scoped_ptr<ShmBuffer> shm_buffer(new ShmBuffer(shm.Pass(), size, shm_id)); | |
331 if (!shm_buffer->addr) { | |
332 RunDecodeCallback(PP_ERROR_NOMEMORY); | |
333 return; | |
334 } | |
335 | |
336 if (shm_id == shm_buffers_.size()) { | |
337 shm_buffers_.push_back(shm_buffer.release()); | |
338 } else { | |
339 // Delete manually since ScopedVector won't delete the existing element if | |
340 // we just assign it. | |
341 delete shm_buffers_[shm_id]; | |
342 shm_buffers_[shm_id] = shm_buffer.release(); | |
343 } | |
344 | |
345 // If the plugin is waiting and there is a buffer available, allow it to call | |
346 // Decode again. | |
347 if (decode_callback_ && !available_shm_buffers_.empty()) | |
348 RunDecodeCallback(PP_OK); | |
349 } | |
350 | |
351 void VideoDecoderResource::OnPluginMsgRequestTextures( | |
352 const ResourceMessageReplyParams& params, | |
353 uint32_t num_textures, | |
354 const PP_Size& size, | |
355 uint32_t texture_target) { | |
356 DCHECK(num_textures); | |
357 std::vector<uint32_t> texture_ids(num_textures); | |
358 if (gles2_impl_) { | |
359 gles2_impl_->GenTextures(num_textures, &texture_ids.front()); | |
360 for (uint32_t i = 0; i < num_textures; ++i) { | |
361 gles2_impl_->ActiveTexture(GL_TEXTURE0); | |
362 gles2_impl_->BindTexture(texture_target, texture_ids[i]); | |
363 gles2_impl_->TexParameteri( | |
364 texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
365 gles2_impl_->TexParameteri( | |
366 texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
367 gles2_impl_->TexParameterf( | |
368 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
369 gles2_impl_->TexParameterf( | |
370 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
371 | |
372 if (texture_target == GL_TEXTURE_2D) { | |
373 gles2_impl_->TexImage2D(texture_target, | |
374 0, | |
375 GL_RGBA, | |
376 size.width, | |
377 size.height, | |
378 0, | |
379 GL_RGBA, | |
380 GL_UNSIGNED_BYTE, | |
381 NULL); | |
382 } | |
383 | |
384 textures_.insert( | |
385 std::make_pair(texture_ids[i], Texture(texture_target, size))); | |
386 } | |
387 gles2_impl_->Flush(); | |
388 } else if (testing_) { | |
389 // Create some fake texture ids so we can test picture handling. | |
390 for (uint32_t i = 0; i < num_textures; ++i) { | |
391 texture_ids[i] = i + 1; | |
392 textures_.insert( | |
393 std::make_pair(texture_ids[i], Texture(texture_target, size))); | |
394 } | |
395 } | |
396 | |
397 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids)); | |
398 } | |
399 | |
400 void VideoDecoderResource::OnPluginMsgPictureReady( | |
401 const ResourceMessageReplyParams& params, | |
402 uint32_t decode_id, | |
403 uint32_t texture_id) { | |
404 received_pictures_.push(Picture(decode_id, texture_id)); | |
405 // Prepare to accept another call to GetPicture in the callback. | |
406 scoped_refptr<TrackedCallback> callback; | |
407 callback.swap(get_picture_callback_); | |
408 PP_VideoPicture* picture = get_picture_; | |
409 get_picture_ = NULL; | |
410 if (TrackedCallback::IsPending(callback)) { | |
411 WriteNextPicture(picture); | |
412 callback->Run(PP_OK); | |
413 } | |
414 } | |
415 | |
416 void VideoDecoderResource::OnPluginMsgDismissPicture( | |
417 const ResourceMessageReplyParams& params, | |
418 uint32_t texture_id) { | |
419 DeleteGLTexture(texture_id); | |
420 textures_.erase(texture_id); | |
421 } | |
422 | |
423 void VideoDecoderResource::OnPluginMsgNotifyError( | |
424 const ResourceMessageReplyParams& params, | |
425 int32_t error) { | |
426 decoder_last_error_ = error; | |
427 // Cause any pending Decode or GetPicture callbacks to run immediately. | |
428 // Reentrancy isn't a problem, since the resource is unusable now. | |
429 if (TrackedCallback::IsPending(decode_callback_)) | |
430 decode_callback_->Run(decoder_last_error_); | |
431 decode_callback_ = NULL; | |
432 if (TrackedCallback::IsPending(get_picture_callback_)) | |
433 get_picture_callback_->Run(decoder_last_error_); | |
434 get_picture_callback_ = NULL; | |
435 } | |
436 | |
437 void VideoDecoderResource::OnPluginMsgInitializeComplete( | |
438 const ResourceMessageReplyParams& params) { | |
439 decoder_last_error_ = params.result(); | |
440 if (decoder_last_error_ == PP_OK) | |
441 initialized_ = true; | |
442 | |
443 // Let the plugin call Initialize again from its callback in case of failure. | |
444 scoped_refptr<TrackedCallback> callback; | |
445 callback.swap(initialize_callback_); | |
446 callback->Run(decoder_last_error_); | |
447 } | |
448 | |
449 void VideoDecoderResource::OnPluginMsgDecodeComplete( | |
450 const ResourceMessageReplyParams& params, | |
451 uint32_t shm_id) { | |
452 if (shm_id >= shm_buffers_.size()) { | |
453 NOTREACHED(); | |
454 return; | |
455 } | |
456 // Make the shm buffer available. | |
457 available_shm_buffers_.push_back(shm_buffers_[shm_id]); | |
458 // If the plugin is waiting and we're not creating or resizing a buffer, | |
459 // allow the plugin to call Decode again. The shm operation may fail, and we | |
460 // don't want to send another buffer until it completes, successfully or not. | |
461 if (decode_callback_ && !shm_buffer_pending_) | |
462 RunDecodeCallback(PP_OK); | |
463 } | |
464 | |
465 void VideoDecoderResource::OnPluginMsgFlushComplete( | |
466 const ResourceMessageReplyParams& params) { | |
467 if (get_picture_callback_) { | |
468 scoped_refptr<TrackedCallback> callback; | |
469 callback.swap(get_picture_callback_); | |
470 callback->Abort(); | |
471 } | |
472 | |
473 scoped_refptr<TrackedCallback> callback; | |
474 callback.swap(flush_callback_); | |
475 callback->Run(params.result()); | |
476 } | |
477 | |
478 void VideoDecoderResource::OnPluginMsgResetComplete( | |
479 const ResourceMessageReplyParams& params) { | |
480 scoped_refptr<TrackedCallback> callback; | |
481 callback.swap(reset_callback_); | |
482 callback->Run(params.result()); | |
483 } | |
484 | |
485 void VideoDecoderResource::RunDecodeCallback(int32_t result) { | |
486 DCHECK(decode_callback_); | |
487 scoped_refptr<TrackedCallback> callback; | |
488 callback.swap(decode_callback_); | |
489 callback->Run(result); | |
490 } | |
491 | |
492 void VideoDecoderResource::DeleteGLTexture(uint32_t id) { | |
493 if (gles2_impl_) { | |
494 gles2_impl_->DeleteTextures(1, &id); | |
495 gles2_impl_->Flush(); | |
496 } | |
497 } | |
498 | |
499 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) { | |
500 DCHECK(!received_pictures_.empty()); | |
501 Picture& picture = received_pictures_.front(); | |
502 uint32_t texture_id = picture.texture_id; | |
503 TextureMap::iterator it = textures_.find(texture_id); | |
504 DCHECK(it != textures_.end()); | |
505 // The resource and host identify decodes by a unique id, |num_decodes_|. Use | |
506 // this to get the plugin's decode_id value, which we stored in |decode_ids_|. | |
507 uint32_t decode_id = picture.decode_id % kMaximumPictureDelay; | |
508 pp_picture->decode_id = decode_ids_[decode_id]; | |
509 pp_picture->texture_id = texture_id; | |
510 pp_picture->texture_target = it->second.texture_target; | |
511 pp_picture->texture_size = it->second.size; | |
512 received_pictures_.pop(); | |
513 } | |
514 | |
515 } // namespace proxy | |
516 } // namespace ppapi | |
OLD | NEW |