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