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

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: 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 "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/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 {
28
29 // Maximum number of concurrent decodes which can be pending.
30 const uint32_t kMaximumPendingDecodes = 8;
31
32 // Minimum size of shared-memory buffers we allocate. Make them large since
33 // we try to reuse them.
34 const uint32_t kMinimumBitstreamBufferSize = 100 << 10;
35
36 } // namespace
37
38 namespace ppapi {
39 namespace proxy {
40
41 VideoDecoderResource::ShmBuffer::ShmBuffer(base::SharedMemory* shm,
42 uint32_t size,
43 uint32_t shm_id)
44 : shm_(shm), size_(size), addr_(NULL), shm_id_(shm_id) {
45 if (shm_->Map(size_))
46 addr_ = shm_->memory();
47 DCHECK(addr_);
Tom Sepez 2014/05/08 20:35:07 might want to handle error cases here. I'd suspec
bbudge 2014/05/14 19:35:04 Added a check that Map succeeded below, when we cr
48 }
49
50 VideoDecoderResource::ShmBuffer::~ShmBuffer() {
51 shm_->Unmap();
piman 2014/05/08 04:26:04 nit: that's implicit with the destruction.
bbudge 2014/05/14 16:40:41 Done.
52 addr_ = NULL;
53 }
54
55 VideoDecoderResource::Texture::Texture(uint32_t texture_target,
56 const PP_Size& size)
57 : texture_target_(texture_target), size_(size) {
58 }
59
60 VideoDecoderResource::Texture::~Texture() {
61 }
62
63 VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id)
64 : decode_id_(decode_id), texture_id_(texture_id) {
65 }
66
67 VideoDecoderResource::Picture::~Picture() {
68 }
69
70 VideoDecoderResource::VideoDecoderResource(Connection connection,
71 PP_Instance instance)
72 : PluginResource(connection, instance),
73 decode_id_(0),
74 decode_size_(0),
75 decode_buffer_(NULL),
76 pending_decode_count_(0),
77 get_shm_buffer_pending_(false),
78 get_picture_(NULL),
79 gles2_impl_(NULL),
80 initialized_(false),
81 // Set |decoder_last_error_| to PP_OK after successful initialization.
82 // This makes error checking a little more concise, since we can check
83 // that the decoder is initialized and hasn't returned an error in one
84 // comparison.
85 decoder_last_error_(PP_ERROR_FAILED) {
86 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create());
87 }
88
89 VideoDecoderResource::~VideoDecoderResource() {
90 FlushCommandBuffer();
91
92 // Destroy any textures which haven't been dismissed.
93 TextureMap::iterator it = textures_.begin();
94 for (; it != textures_.end(); ++it)
95 DeleteGLTexture(it->first);
96 // Release our ref on the graphics resource.
97 graphics3d_ = NULL;
98 gles2_impl_ = NULL;
99 }
100
101 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() {
102 return this;
103 }
104
105 int32_t VideoDecoderResource::Initialize(
106 PP_Resource graphics_context,
107 PP_VideoProfile profile,
108 PP_Bool allow_software_fallback,
109 scoped_refptr<TrackedCallback> callback) {
110 if (initialized_)
111 return PP_ERROR_FAILED;
112 if (initialize_callback_)
113 return PP_ERROR_INPROGRESS;
114 if (!graphics_context)
115 return PP_ERROR_BADRESOURCE;
116 EnterResourceNoLock<PPB_Graphics3D_API> enter_context(graphics_context, true);
117 if (enter_context.failed())
118 return PP_ERROR_BADRESOURCE;
119
120 initialize_callback_ = callback;
121
122 // Take a reference to keep the graphics resource alive for the lifetime of
123 // this resource.
124 graphics3d_ = static_cast<PPB_Graphics3D_Shared*>(enter_context.object());
125 gles2_impl_ = graphics3d_->gles2_impl();
126
127 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>(
128 RENDERER,
129 PpapiHostMsg_VideoDecoder_Initialize(graphics3d_->host_resource(),
130 profile,
131 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
148 return TryDecode(decode_id, size, buffer, callback);
149 }
150
151 int32_t VideoDecoderResource::GetPicture(
152 PP_VideoPicture* picture,
153 scoped_refptr<TrackedCallback> callback) {
154 if (decoder_last_error_)
155 return decoder_last_error_;
156 if (reset_callback_)
157 return PP_ERROR_FAILED;
158 if (get_picture_callback_)
159 return PP_ERROR_INPROGRESS;
160
161 // If the next picture is ready, return it synchronously.
162 if (!received_pictures_.empty()) {
163 WriteNextPicture(picture);
164 return PP_OK;
165 }
166
167 get_picture_callback_ = callback;
168 get_picture_ = picture;
169 return PP_OK_COMPLETIONPENDING;
170 }
171
172 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) {
173 if (decoder_last_error_)
174 return;
175 if (reset_callback_)
176 return;
177
178 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id));
179 }
180
181 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) {
182 if (decoder_last_error_)
183 return decoder_last_error_;
184 if (reset_callback_)
185 return PP_ERROR_FAILED;
186 if (flush_callback_)
187 return PP_ERROR_INPROGRESS;
188 flush_callback_ = callback;
189
190 FlushCommandBuffer();
191 Call<PpapiPluginMsg_VideoDecoder_FlushReply>(
192 RENDERER,
193 PpapiHostMsg_VideoDecoder_Flush(),
194 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this));
195
196 return PP_OK_COMPLETIONPENDING;
197 }
198
199 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) {
200 if (decoder_last_error_)
201 return decoder_last_error_;
202 if (flush_callback_)
203 return PP_ERROR_FAILED;
204 if (reset_callback_)
205 return PP_ERROR_INPROGRESS;
206 reset_callback_ = callback;
207
208 // Cause any pending Decode or GetPicture callbacks to abort immediately.
209 // Reentrancy isn't a problem, since all calls fail until Reset completes.
210 if (TrackedCallback::IsPending(decode_callback_))
211 decode_callback_->Abort();
212 decode_callback_ = NULL;
213 if (TrackedCallback::IsPending(get_picture_callback_))
214 get_picture_callback_->Abort();
215 get_picture_callback_ = NULL;
216 FlushCommandBuffer();
217 Call<PpapiPluginMsg_VideoDecoder_ResetReply>(
218 RENDERER,
219 PpapiHostMsg_VideoDecoder_Reset(),
220 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this));
221
222 return PP_OK_COMPLETIONPENDING;
223 }
224
225 void VideoDecoderResource::OnReplyReceived(
226 const ResourceMessageReplyParams& params,
227 const IPC::Message& msg) {
228 IPC_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg)
229 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
230 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures)
231 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoDecoder_PictureReady,
232 OnPluginMsgPictureReady)
233 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
234 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture)
235 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoDecoder_NotifyError,
236 OnPluginMsgNotifyError)
237 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
238 PluginResource::OnReplyReceived(params, msg))
239 IPC_END_MESSAGE_MAP()
240 }
241
242 void VideoDecoderResource::OnPluginMsgRequestTextures(
243 const ResourceMessageReplyParams& params,
244 uint32_t num_textures,
245 PP_Size size,
246 uint32_t texture_target,
247 const std::vector<gpu::Mailbox>& mailboxes) {
Tom Sepez 2014/05/08 20:35:07 Are the mailboxes are supplied by a trusted proces
bbudge 2014/05/14 16:40:41 They are generated by the renderer process. I am r
248 std::vector<uint32_t> texture_ids;
249 for (uint32_t i = 0; i < num_textures; ++i) {
250 GLuint texture_id;
251 gles2_impl_->GenTextures(1, &texture_id);
piman 2014/05/08 04:26:04 It's really better to GenTextures(num_textures, ar
bbudge 2014/05/14 16:40:41 Done.
252
253 gles2_impl_->ActiveTexture(GL_TEXTURE0);
bbudge 2014/05/14 19:35:04 piman: Can I move this out of the loop, to after G
254 gles2_impl_->BindTexture(texture_target, texture_id);
piman 2014/05/08 04:26:04 So, these 2 calls modify GL state. Since this come
bbudge 2014/05/14 16:40:41 I've changed the resource to create its own Graphi
255 gles2_impl_->TexParameteri(
256 texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
257 gles2_impl_->TexParameteri(
258 texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
259 gles2_impl_->TexParameterf(
260 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
261 gles2_impl_->TexParameterf(
262 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
263
264 if (texture_target == GL_TEXTURE_2D) {
265 gles2_impl_->TexImage2D(texture_target,
266 0,
267 GL_RGBA,
268 size.width,
269 size.height,
270 0,
271 GL_RGBA,
272 GL_UNSIGNED_BYTE,
273 NULL);
274 }
275
276 if (!mailboxes.empty())
piman 2014/05/08 04:26:04 We should check somewhere that mailbox.size() == n
bbudge 2014/05/14 16:40:41 Will do in future CL (mailboxes removed for now)
277 gles2_impl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailboxes[i].name);
278
279 textures_.insert(std::make_pair(texture_id, Texture(texture_target, size)));
280 texture_ids.push_back(texture_id);
281 }
282
283 FlushCommandBuffer();
284
285 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids));
286 }
287
288 void VideoDecoderResource::OnPluginMsgPictureReady(
289 const ResourceMessageReplyParams& params,
290 int32_t shm_id,
291 uint32_t texture_id) {
292 // This value is inserted in OnPluginMsgDecodeComplete.
293 uint32_t decode_id = decode_ids_[shm_id];
294 received_pictures_.push(Picture(decode_id, texture_id));
295 // Get ready to accept another call to GetPicture in the callback.
296 scoped_refptr<TrackedCallback> callback;
297 callback.swap(get_picture_callback_);
298 PP_VideoPicture* picture = get_picture_;
299 get_picture_ = NULL;
300 if (TrackedCallback::IsPending(callback)) {
301 WriteNextPicture(picture);
302 callback->Run(PP_OK);
303 }
304 }
305
306 void VideoDecoderResource::OnPluginMsgDismissPicture(
307 const ResourceMessageReplyParams& params,
308 uint32_t texture_id) {
309 DeleteGLTexture(texture_id);
310 textures_.erase(texture_id);
311 }
312
313 void VideoDecoderResource::OnPluginMsgNotifyError(
314 const ResourceMessageReplyParams& params,
315 int32_t error) {
316 decoder_last_error_ = error;
317 // Cause any pending Decode or GetPicture callbacks to run immediately.
318 // Reentrancy isn't a problem, since the resource is unusable now.
319 if (TrackedCallback::IsPending(decode_callback_))
320 decode_callback_->Run(decoder_last_error_);
321 decode_callback_ = NULL;
322 if (TrackedCallback::IsPending(get_picture_callback_))
323 get_picture_callback_->Run(decoder_last_error_);
324 get_picture_callback_ = NULL;
325 }
326
327 void VideoDecoderResource::OnPluginMsgInitializeComplete(
328 const ResourceMessageReplyParams& params) {
329 decoder_last_error_ = params.result();
330 if (decoder_last_error_ == PP_OK)
331 initialized_ = true;
332 scoped_refptr<TrackedCallback> callback;
333 callback.swap(initialize_callback_);
334 callback->Run(decoder_last_error_);
335 }
336
337 void VideoDecoderResource::OnPluginMsgGetShmComplete(
338 const ResourceMessageReplyParams& params,
339 uint32_t size) {
340 get_shm_buffer_pending_ = false;
341 int32_t result = params.result();
342 if (result == PP_OK) {
343 base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle();
344 if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) {
345 RunDecodeCallback(PP_ERROR_FAILED);
346 return;
347 }
348 uint32_t shm_id = static_cast<uint32_t>(shm_buffers_.size());
349 ShmBuffer* shm_buffer =
350 new ShmBuffer(new base::SharedMemory(shm_handle, false /* read_only */),
351 size,
352 shm_id);
353 shm_buffers_.push_back(shm_buffer);
354 SendDecodeMessage(shm_id);
355 RunDecodeCallback(PP_OK);
356 }
357 }
358
359 void VideoDecoderResource::OnPluginMsgDecodeComplete(
360 uint32_t decode_id,
361 const ResourceMessageReplyParams& params,
362 uint32_t shm_id) {
piman 2014/05/08 04:26:04 It makes me a bit uncomfortable to have the shm_id
bbudge 2014/05/14 16:40:41 I must pass the shm_id round trip to identify busy
bbudge 2014/05/14 19:35:04 Just to reiterate, shm_id is the only Id I can cou
363 pending_decode_count_--;
364 available_shm_buffers_.push_back(shm_buffers_[shm_id]);
365 // Save the user id associated with the Decode now, in case it generates a
366 // call to OnPluginMsgPictureReady.
367 decode_ids_[shm_id] = decode_id;
piman 2014/05/08 04:26:04 Is this ok, to use the shm_id (which is just an in
bbudge 2014/05/14 16:40:41 The decode_id is provided by the plugin, which may
368 // If a Decode is pending, and we're not waiting for a shm buffer, attempt
369 // the Decode again.
370 if (decode_callback_ && !get_shm_buffer_pending_) {
371 int32_t result =
372 TryDecode(decode_id_, decode_size_, decode_buffer_, decode_callback_);
373 if (result == PP_OK)
374 RunDecodeCallback(PP_OK);
375 }
376 }
377
378 void VideoDecoderResource::OnPluginMsgFlushComplete(
379 const ResourceMessageReplyParams& params) {
380 if (get_picture_callback_) {
381 scoped_refptr<TrackedCallback> callback;
382 callback.swap(get_picture_callback_);
383 callback->Abort();
384 }
385
386 scoped_refptr<TrackedCallback> callback;
387 callback.swap(flush_callback_);
388 callback->Run(params.result());
389 }
390
391 void VideoDecoderResource::OnPluginMsgResetComplete(
392 const ResourceMessageReplyParams& params) {
393 scoped_refptr<TrackedCallback> callback;
394 callback.swap(reset_callback_);
395 callback->Run(params.result());
396 }
397
398 int32_t VideoDecoderResource::TryDecode(
399 uint32_t decode_id,
400 uint32_t size,
401 const void* buffer,
402 scoped_refptr<TrackedCallback> callback) {
403 decode_id_ = decode_id;
404 decode_size_ = size;
405 decode_buffer_ = buffer;
406 if (available_shm_buffers_.empty() ||
407 available_shm_buffers_.back()->size_ < size) {
408 decode_callback_ = callback;
409
410 if (pending_decode_count_ < kMaximumPendingDecodes) {
411 get_shm_buffer_pending_ = true;
412 uint32_t alloc_size = std::max(size, kMinimumBitstreamBufferSize);
413 Call<PpapiPluginMsg_VideoDecoder_GetShmReply>(
414 RENDERER,
piman 2014/05/08 04:26:04 Why not allocate the shm by going to the browser?
bbudge 2014/05/14 19:35:04 This was simpler, and the patch is already large.
piman 2014/05/15 04:02:53 I disagree... the API is too subtle. See the comme
415 PpapiHostMsg_VideoDecoder_GetShm(alloc_size),
416 base::Bind(&VideoDecoderResource::OnPluginMsgGetShmComplete, this));
417 }
418
419 return PP_OK_COMPLETIONPENDING;
420 }
421
422 ShmBuffer* shm_buffer = available_shm_buffers_.back();
423 available_shm_buffers_.pop_back();
424 SendDecodeMessage(shm_buffer->shm_id_);
425 return PP_OK;
426 }
427
428 void VideoDecoderResource::SendDecodeMessage(uint32_t shm_id) {
429 ShmBuffer* shm_buffer = shm_buffers_[shm_id];
430 memcpy(shm_buffer->addr_, decode_buffer_, decode_size_);
431 pending_decode_count_++;
432
433 FlushCommandBuffer();
434 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>(
435 RENDERER,
436 PpapiHostMsg_VideoDecoder_Decode(shm_id, decode_size_),
437 base::Bind(
438 &VideoDecoderResource::OnPluginMsgDecodeComplete, this, decode_id_));
439 }
440
441 void VideoDecoderResource::RunDecodeCallback(int32_t result) {
442 scoped_refptr<TrackedCallback> callback;
443 callback.swap(decode_callback_);
444 callback->Run(result);
445 }
446
447 void VideoDecoderResource::FlushCommandBuffer() {
448 if (gles2_impl_)
449 gles2_impl_->Flush();
450 }
451
452 void VideoDecoderResource::DeleteGLTexture(uint32_t id) {
453 gles2_impl_->DeleteTextures(1, &id);
454 }
455
456 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) {
457 DCHECK(!received_pictures_.empty());
458 Picture& picture = received_pictures_.front();
459 uint32_t texture_id = picture.texture_id_;
460 TextureMap::iterator it = textures_.find(texture_id);
461 DCHECK(it != textures_.end());
462 pp_picture->decode_id = picture.decode_id_;
463 pp_picture->texture_id = texture_id;
464 pp_picture->texture_target = it->second.texture_target_;
465 pp_picture->texture_size = it->second.size_;
466 received_pictures_.pop();
467 }
468
469 } // namespace proxy
470 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698