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

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: Changed docs for PP_ERROR_PLATFORM_FAILED. 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 "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/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h"
18 #include "ppapi/shared_impl/proxy_lock.h"
19 #include "ppapi/shared_impl/resource_tracker.h"
20 #include "ppapi/thunk/enter.h"
21
22 using ppapi::thunk::EnterResourceNoLock;
23 using ppapi::thunk::PPB_Graphics3D_API;
24 using ppapi::thunk::PPB_VideoDecoder_API;
25
26 namespace {
27
28 // Maximum number of concurrent decodes which can be pending.
29 const uint32_t kMaximumPendingDecodes = 8;
30
31 // Minimum size of shared-memory buffers we allocate. Make them large since
32 // we try to reuse them.
33 const uint32_t kMinimumBitstreamBufferSize = 100 << 10;
34
35 } // namespace
36
37 namespace ppapi {
38 namespace proxy {
39
40 VideoDecoderResource::ShmBuffer::ShmBuffer(base::SharedMemory* in_shm,
dmichael (off chromium) 2014/05/15 21:18:22 nit: scoped_ptr<> would make it clearer that this
bbudge 2014/05/15 22:27:44 Done.
41 uint32_t in_size,
42 uint32_t in_shm_id)
43 : shm(in_shm), size(in_size), addr(NULL), shm_id(in_shm_id) {
piman 2014/05/15 04:02:53 nit: no need for in_ prefixes. For an initializer,
bbudge 2014/05/15 16:31:57 Awesome. I didn't know that! I'll fix this in the
bbudge 2014/05/15 22:27:44 Done.
44 if (shm->Map(size))
45 addr = shm->memory();
46 DCHECK(addr);
47 }
48
49 VideoDecoderResource::ShmBuffer::~ShmBuffer() {
50 }
51
52 VideoDecoderResource::Texture::Texture(uint32_t in_texture_target,
53 const PP_Size& in_size)
54 : texture_target(in_texture_target), size(in_size) {
55 }
56
57 VideoDecoderResource::Texture::~Texture() {
58 }
59
60 VideoDecoderResource::Picture::Picture(int32_t in_decode_id,
61 uint32_t in_texture_id)
62 : decode_id( in_decode_id), texture_id(in_texture_id) {
dmichael (off chromium) 2014/05/15 21:18:22 nit: extra space before in_decode_id
bbudge 2014/05/15 22:27:44 Done.
63 }
64
65 VideoDecoderResource::Picture::~Picture() {
66 }
67
68 VideoDecoderResource::VideoDecoderResource(Connection connection,
69 PP_Instance instance)
70 : PluginResource(connection, instance),
71 decode_id_(0),
72 decode_size_(0),
73 decode_buffer_(NULL),
74 pending_decode_count_(0),
75 get_shm_buffer_pending_(false),
76 get_picture_(NULL),
77 gles2_impl_(NULL),
78 initialized_(false),
79 testing_(false),
80 // Set |decoder_last_error_| to PP_OK after successful initialization.
81 // This makes error checking a little more concise, since we can check
82 // that the decoder has been initialized and hasn't returned an error by
83 // just testing |decoder_last_error_|.
84 decoder_last_error_(PP_ERROR_FAILED) {
85 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create());
86 }
87
88 VideoDecoderResource::~VideoDecoderResource() {
89 // Destroy any textures which haven't been dismissed.
90 TextureMap::iterator it = textures_.begin();
91 for (; it != textures_.end(); ++it)
92 DeleteGLTexture(it->first);
93 }
94
95 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() {
96 return this;
97 }
98
99 int32_t VideoDecoderResource::Initialize(
100 PP_Resource graphics_context,
101 PP_VideoProfile profile,
102 PP_Bool allow_software_fallback,
103 scoped_refptr<TrackedCallback> callback) {
104 if (initialized_)
105 return PP_ERROR_FAILED;
106 if (profile < 0 || profile > PP_VIDEOPROFILE_MAX)
107 return PP_ERROR_BADARGUMENT;
108 if (initialize_callback_)
109 return PP_ERROR_INPROGRESS;
110 if (!graphics_context)
111 return PP_ERROR_BADRESOURCE;
112
113 // Create a new Graphics3D resource that can create texture resources to share
114 // with the plugin. We can't use the plugin's Graphics3D, since we create
115 // textures on a proxy thread, and would interfere with the plugin.
116 thunk::EnterResourceCreationNoLock enter_create(pp_instance());
117 if (enter_create.failed())
118 return PP_ERROR_FAILED;
119 int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE};
120 HostResource host_resource;
121 if (!testing_) {
122 graphics3d_ =
123 ScopedPPResource(ScopedPPResource::PassRef(),
124 enter_create.functions()->CreateGraphics3D(
125 pp_instance(), graphics_context, attrib_list));
126 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(),
127 true);
128 if (enter_graphics.failed())
129 return PP_ERROR_BADRESOURCE;
130
131 PPB_Graphics3D_Shared* ppb_graphics3d_shared =
132 static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object());
133 gles2_impl_ = ppb_graphics3d_shared->gles2_impl();
134 host_resource = ppb_graphics3d_shared->host_resource();
135 }
136
137 initialize_callback_ = callback;
138
139 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>(
140 RENDERER,
141 PpapiHostMsg_VideoDecoder_Initialize(
142 host_resource, profile, PP_ToBool(allow_software_fallback)),
143 base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this));
144
145 return PP_OK_COMPLETIONPENDING;
146 }
147
148 int32_t VideoDecoderResource::Decode(uint32_t decode_id,
149 uint32_t size,
150 const void* buffer,
151 scoped_refptr<TrackedCallback> callback) {
152 if (decoder_last_error_)
153 return decoder_last_error_;
154 if (flush_callback_ || reset_callback_)
155 return PP_ERROR_FAILED;
156 if (decode_callback_)
157 return PP_ERROR_INPROGRESS;
piman 2014/05/15 04:02:53 This is actually very unfortunate. It means you ca
bbudge 2014/05/15 16:31:57 It's not as bad as it looks. If a shared memory bu
158
159 return TryDecode(decode_id, size, buffer, callback);
160 }
161
162 int32_t VideoDecoderResource::GetPicture(
163 PP_VideoPicture* picture,
164 scoped_refptr<TrackedCallback> callback) {
165 if (decoder_last_error_)
166 return decoder_last_error_;
167 if (reset_callback_)
168 return PP_ERROR_FAILED;
169 if (get_picture_callback_)
170 return PP_ERROR_INPROGRESS;
171
172 // If the next picture is ready, return it synchronously.
173 if (!received_pictures_.empty()) {
174 WriteNextPicture(picture);
175 return PP_OK;
176 }
177
178 get_picture_callback_ = callback;
179 get_picture_ = picture;
180 return PP_OK_COMPLETIONPENDING;
181 }
182
183 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) {
184 if (decoder_last_error_)
185 return;
186 if (reset_callback_)
187 return;
188
189 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id));
190 }
191
192 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) {
193 if (decoder_last_error_)
194 return decoder_last_error_;
195 if (reset_callback_)
196 return PP_ERROR_FAILED;
197 if (flush_callback_)
198 return PP_ERROR_INPROGRESS;
199 flush_callback_ = callback;
200
201 Call<PpapiPluginMsg_VideoDecoder_FlushReply>(
202 RENDERER,
203 PpapiHostMsg_VideoDecoder_Flush(),
204 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this));
205
206 return PP_OK_COMPLETIONPENDING;
207 }
208
209 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) {
210 if (decoder_last_error_)
211 return decoder_last_error_;
212 if (flush_callback_)
213 return PP_ERROR_FAILED;
214 if (reset_callback_)
215 return PP_ERROR_INPROGRESS;
216 reset_callback_ = callback;
217
218 // Cause any pending Decode or GetPicture callbacks to abort immediately.
219 // Reentrancy isn't a problem, since all calls fail until Reset completes.
220 if (TrackedCallback::IsPending(decode_callback_))
221 decode_callback_->Abort();
222 decode_callback_ = NULL;
223 if (TrackedCallback::IsPending(get_picture_callback_))
224 get_picture_callback_->Abort();
225 get_picture_callback_ = NULL;
226 Call<PpapiPluginMsg_VideoDecoder_ResetReply>(
227 RENDERER,
228 PpapiHostMsg_VideoDecoder_Reset(),
229 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this));
230
231 return PP_OK_COMPLETIONPENDING;
232 }
233
234 void VideoDecoderResource::OnReplyReceived(
235 const ResourceMessageReplyParams& params,
236 const IPC::Message& msg) {
237 PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg)
238 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
239 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures)
240 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
241 PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady)
242 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
243 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture)
244 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
245 PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError)
246 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
247 PluginResource::OnReplyReceived(params, msg))
248 PPAPI_END_MESSAGE_MAP()
249 }
250
251 void VideoDecoderResource::SetForTest() {
252 testing_ = true;
253 }
254
255 void VideoDecoderResource::OnPluginMsgRequestTextures(
256 const ResourceMessageReplyParams& params,
257 uint32_t num_textures,
258 const PP_Size& size,
259 uint32_t texture_target) {
260 DCHECK(num_textures);
261 std::vector<uint32_t> texture_ids(num_textures);
262 if (gles2_impl_) {
263 gles2_impl_->GenTextures(num_textures, &texture_ids.front());
264 for (uint32_t i = 0; i < num_textures; ++i) {
265 gles2_impl_->ActiveTexture(GL_TEXTURE0);
266 gles2_impl_->BindTexture(texture_target, texture_ids[i]);
267 gles2_impl_->TexParameteri(
268 texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
269 gles2_impl_->TexParameteri(
270 texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
271 gles2_impl_->TexParameterf(
272 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
273 gles2_impl_->TexParameterf(
274 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
275
276 if (texture_target == GL_TEXTURE_2D) {
277 gles2_impl_->TexImage2D(texture_target,
278 0,
279 GL_RGBA,
280 size.width,
281 size.height,
282 0,
283 GL_RGBA,
284 GL_UNSIGNED_BYTE,
285 NULL);
286 }
287
288 textures_.insert(
289 std::make_pair(texture_ids[i], Texture(texture_target, size)));
290 }
291 gles2_impl_->Flush();
292 } else if (testing_) {
293 // Create some fake texture ids so we can test picture handling.
294 for (uint32_t i = 0; i < num_textures; ++i) {
295 texture_ids[i] = i + 1;
296 textures_.insert(
297 std::make_pair(texture_ids[i], Texture(texture_target, size)));
298 }
299 }
300
301 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids));
302 }
303
304 void VideoDecoderResource::OnPluginMsgPictureReady(
305 const ResourceMessageReplyParams& params,
306 uint32_t decode_id,
307 uint32_t texture_id) {
308 received_pictures_.push(Picture(decode_id, texture_id));
309 // Prepare to accept another call to GetPicture in the callback.
310 scoped_refptr<TrackedCallback> callback;
311 callback.swap(get_picture_callback_);
312 PP_VideoPicture* picture = get_picture_;
313 get_picture_ = NULL;
314 if (TrackedCallback::IsPending(callback)) {
315 WriteNextPicture(picture);
316 callback->Run(PP_OK);
317 }
318 }
319
320 void VideoDecoderResource::OnPluginMsgDismissPicture(
321 const ResourceMessageReplyParams& params,
322 uint32_t texture_id) {
323 DeleteGLTexture(texture_id);
324 textures_.erase(texture_id);
325 }
326
327 void VideoDecoderResource::OnPluginMsgNotifyError(
328 const ResourceMessageReplyParams& params,
329 int32_t error) {
330 decoder_last_error_ = error;
331 // Cause any pending Decode or GetPicture callbacks to run immediately.
332 // Reentrancy isn't a problem, since the resource is unusable now.
333 if (TrackedCallback::IsPending(decode_callback_))
334 decode_callback_->Run(decoder_last_error_);
335 decode_callback_ = NULL;
336 if (TrackedCallback::IsPending(get_picture_callback_))
337 get_picture_callback_->Run(decoder_last_error_);
338 get_picture_callback_ = NULL;
339 }
340
341 void VideoDecoderResource::OnPluginMsgInitializeComplete(
342 const ResourceMessageReplyParams& params) {
343 decoder_last_error_ = params.result();
344 if (decoder_last_error_ == PP_OK)
345 initialized_ = true;
346
347 // Let the plugin call Initialize again from its callback in case of failure.
348 scoped_refptr<TrackedCallback> callback;
349 callback.swap(initialize_callback_);
350 callback->Run(decoder_last_error_);
351 }
352
353 void VideoDecoderResource::OnPluginMsgGetShmComplete(
354 const ResourceMessageReplyParams& params,
355 uint32_t size) {
356 get_shm_buffer_pending_ = false;
357 int32_t result = params.result();
358 if (result == PP_OK) {
359 base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle();
360 if (!params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) {
361 RunDecodeCallback(PP_ERROR_FAILED);
362 return;
363 }
364 uint32_t shm_id = static_cast<uint32_t>(shm_buffers_.size());
365 scoped_ptr<ShmBuffer> shm_buffer(
366 new ShmBuffer(new base::SharedMemory(shm_handle, false /* read_only */),
367 size,
368 shm_id));
369 if (!shm_buffer->addr) {
370 RunDecodeCallback(PP_ERROR_FAILED);
371 return;
372 }
373 shm_buffers_.push_back(shm_buffer.release());
374 SendDecodeMessage(shm_id);
375 RunDecodeCallback(PP_OK);
376 }
377 }
378
379 void VideoDecoderResource::OnPluginMsgDecodeComplete(
380 const ResourceMessageReplyParams& params,
381 uint32_t shm_id) {
382 pending_decode_count_--;
383 // Return the shm buffer to the available list.
384 if (shm_id < shm_buffers_.size())
385 available_shm_buffers_.push_back(shm_buffers_[shm_id]);
386 else
387 NOTREACHED();
388
389 // If a Decode is pending, and we're not waiting for a shm buffer, attempt
390 // the Decode again.
391 if (decode_callback_ && !get_shm_buffer_pending_) {
392 int32_t result =
393 TryDecode(decode_id_, decode_size_, decode_buffer_, decode_callback_);
394 if (result == PP_OK)
395 RunDecodeCallback(PP_OK);
396 }
397 }
398
399 void VideoDecoderResource::OnPluginMsgFlushComplete(
400 const ResourceMessageReplyParams& params) {
401 if (get_picture_callback_) {
402 scoped_refptr<TrackedCallback> callback;
403 callback.swap(get_picture_callback_);
404 callback->Abort();
405 }
406
407 scoped_refptr<TrackedCallback> callback;
408 callback.swap(flush_callback_);
409 callback->Run(params.result());
410 }
411
412 void VideoDecoderResource::OnPluginMsgResetComplete(
413 const ResourceMessageReplyParams& params) {
414 scoped_refptr<TrackedCallback> callback;
415 callback.swap(reset_callback_);
416 callback->Run(params.result());
417 }
418
419 int32_t VideoDecoderResource::TryDecode(
420 uint32_t decode_id,
421 uint32_t size,
422 const void* buffer,
423 scoped_refptr<TrackedCallback> callback) {
424 decode_id_ = decode_id;
425 decode_size_ = size;
426 decode_buffer_ = buffer;
427 if (available_shm_buffers_.empty() ||
428 available_shm_buffers_.back()->size < size) {
dmichael (off chromium) 2014/05/15 21:18:22 The way I'm reading this, it seems like you may or
bbudge 2014/05/15 22:27:44 I've been worried about this too. This technique
429 decode_callback_ = callback;
430
431 if (pending_decode_count_ < kMaximumPendingDecodes) {
432 get_shm_buffer_pending_ = true;
433 uint32_t alloc_size = std::max(size, kMinimumBitstreamBufferSize);
434 Call<PpapiPluginMsg_VideoDecoder_GetShmReply>(
435 RENDERER,
436 PpapiHostMsg_VideoDecoder_GetShm(alloc_size),
437 base::Bind(&VideoDecoderResource::OnPluginMsgGetShmComplete, this));
438 }
439
440 return PP_OK_COMPLETIONPENDING;
441 }
442
443 ShmBuffer* shm_buffer = available_shm_buffers_.back();
444 available_shm_buffers_.pop_back();
445 SendDecodeMessage(shm_buffer->shm_id);
446 return PP_OK;
447 }
448
449 void VideoDecoderResource::SendDecodeMessage(uint32_t shm_id) {
450 DCHECK(shm_id < shm_buffers_.size());
451 ShmBuffer* shm_buffer = shm_buffers_[shm_id];
452 memcpy(shm_buffer->addr, decode_buffer_, decode_size_);
453 pending_decode_count_++;
454
455 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>(
456 RENDERER,
457 PpapiHostMsg_VideoDecoder_Decode(shm_id, decode_id_, decode_size_),
458 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this));
459 }
460
461 void VideoDecoderResource::RunDecodeCallback(int32_t result) {
462 scoped_refptr<TrackedCallback> callback;
463 callback.swap(decode_callback_);
464 callback->Run(result);
465 }
466
467 void VideoDecoderResource::DeleteGLTexture(uint32_t id) {
468 if (gles2_impl_) {
469 gles2_impl_->DeleteTextures(1, &id);
470 gles2_impl_->Flush();
471 }
472 }
473
474 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) {
475 DCHECK(!received_pictures_.empty());
476 Picture& picture = received_pictures_.front();
477 uint32_t texture_id = picture.texture_id;
478 TextureMap::iterator it = textures_.find(texture_id);
479 DCHECK(it != textures_.end());
480 pp_picture->decode_id = picture.decode_id;
481 pp_picture->texture_id = texture_id;
482 pp_picture->texture_target = it->second.texture_target;
483 pp_picture->texture_size = it->second.size;
484 received_pictures_.pop();
485 }
486
487 } // namespace proxy
488 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698