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/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/shared_impl/scoped_pp_resource.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_, | |
yzshen1
2014/05/14 22:58:16
I think '_' suffix has quite specific meaning in o
bbudge
2014/05/15 00:24:54
That's a good convention. Done.
| |
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); | |
48 } | |
49 | |
50 VideoDecoderResource::ShmBuffer::~ShmBuffer() { | |
51 } | |
52 | |
53 VideoDecoderResource::Texture::Texture(uint32_t texture_target_, | |
54 const PP_Size& size_) | |
55 : texture_target(texture_target_), size(size_) { | |
56 } | |
57 | |
58 VideoDecoderResource::Texture::~Texture() { | |
59 } | |
60 | |
61 VideoDecoderResource::Picture::Picture(int32_t decode_id_, uint32_t texture_id_) | |
62 : decode_id(decode_id_), texture_id(texture_id_) { | |
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, which 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 ScopedPPResource graphics3d( | |
123 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 graphics3d_ = static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object()); | |
132 gles2_impl_ = graphics3d_->gles2_impl(); | |
133 host_resource = graphics3d_->host_resource(); | |
134 graphics3d.Release(); | |
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; | |
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( | |
yzshen1
2014/05/14 22:58:16
I think it is good to add indent in this case.
bbudge
2014/05/15 00:24:54
You're the second reviewer to point this out so I'
| |
239 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures) | |
240 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoDecoder_PictureReady, | |
241 OnPluginMsgPictureReady) | |
242 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( | |
243 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture) | |
244 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_VideoDecoder_NotifyError, | |
245 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) { | |
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 | |
OLD | NEW |