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