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 "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 | |
OLD | NEW |