1 // Copyright (c) 2014 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.
5 #include "content/renderer/pepper/pepper_video_decoder_host.h"
8 #include "base/memory/shared_memory.h"
9 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
10 #include "content/common/pepper_file_util.h"
11 #include "content/public/common/content_client.h"
12 #include "content/public/renderer/content_renderer_client.h"
13 #include "content/public/renderer/render_thread.h"
14 #include "content/public/renderer/renderer_ppapi_host.h"
15 #include "content/renderer/pepper/gfx_conversion.h"
16 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
17 #include "content/renderer/pepper/video_decoder_shim.h"
18 #include "media/base/limits.h"
19 #include "media/video/video_decode_accelerator.h"
20 #include "ppapi/c/pp_completion_callback.h"
21 #include "ppapi/c/pp_errors.h"
22 #include "ppapi/host/dispatch_host_message.h"
23 #include "ppapi/host/ppapi_host.h"
24 #include "ppapi/proxy/ppapi_messages.h"
25 #include "ppapi/proxy/video_decoder_constants.h"
26 #include "ppapi/thunk/enter.h"
27 #include "ppapi/thunk/ppb_graphics_3d_api.h"
29 using ppapi::proxy::SerializedHandle
;
30 using ppapi::thunk::EnterResourceNoLock
;
31 using ppapi::thunk::PPB_Graphics3D_API
;
37 media::VideoCodecProfile
PepperToMediaVideoProfile(PP_VideoProfile profile
) {
39 case PP_VIDEOPROFILE_H264BASELINE
:
40 return media::H264PROFILE_BASELINE
;
41 case PP_VIDEOPROFILE_H264MAIN
:
42 return media::H264PROFILE_MAIN
;
43 case PP_VIDEOPROFILE_H264EXTENDED
:
44 return media::H264PROFILE_EXTENDED
;
45 case PP_VIDEOPROFILE_H264HIGH
:
46 return media::H264PROFILE_HIGH
;
47 case PP_VIDEOPROFILE_H264HIGH10PROFILE
:
48 return media::H264PROFILE_HIGH10PROFILE
;
49 case PP_VIDEOPROFILE_H264HIGH422PROFILE
:
50 return media::H264PROFILE_HIGH422PROFILE
;
51 case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE
:
52 return media::H264PROFILE_HIGH444PREDICTIVEPROFILE
;
53 case PP_VIDEOPROFILE_H264SCALABLEBASELINE
:
54 return media::H264PROFILE_SCALABLEBASELINE
;
55 case PP_VIDEOPROFILE_H264SCALABLEHIGH
:
56 return media::H264PROFILE_SCALABLEHIGH
;
57 case PP_VIDEOPROFILE_H264STEREOHIGH
:
58 return media::H264PROFILE_STEREOHIGH
;
59 case PP_VIDEOPROFILE_H264MULTIVIEWHIGH
:
60 return media::H264PROFILE_MULTIVIEWHIGH
;
61 case PP_VIDEOPROFILE_VP8_ANY
:
62 return media::VP8PROFILE_ANY
;
63 case PP_VIDEOPROFILE_VP9_ANY
:
64 return media::VP9PROFILE_ANY
;
65 // No default case, to catch unhandled PP_VideoProfile values.
68 return media::VIDEO_CODEC_PROFILE_UNKNOWN
;
73 PepperVideoDecoderHost::PendingDecode::PendingDecode(
77 const ppapi::host::ReplyMessageContext
& reply_context
)
78 : decode_id(decode_id
),
81 reply_context(reply_context
) {}
83 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {}
85 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost
* host
,
88 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
89 renderer_ppapi_host_(host
) {}
91 PepperVideoDecoderHost::~PepperVideoDecoderHost() {}
93 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
94 const IPC::Message
& msg
,
95 ppapi::host::HostMessageContext
* context
) {
96 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost
, msg
)
97 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize
,
99 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm
,
101 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode
,
103 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures
,
104 OnHostMsgAssignTextures
)
105 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture
,
106 OnHostMsgRecyclePicture
)
107 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush
,
109 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset
,
111 PPAPI_END_MESSAGE_MAP()
112 return PP_ERROR_FAILED
;
115 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
116 ppapi::host::HostMessageContext
* context
,
117 const ppapi::HostResource
& graphics_context
,
118 PP_VideoProfile profile
,
119 PP_HardwareAcceleration acceleration
,
120 uint32_t min_picture_count
) {
122 return PP_ERROR_FAILED
;
123 if (min_picture_count
> ppapi::proxy::kMaximumPictureCount
)
124 return PP_ERROR_BADARGUMENT
;
126 EnterResourceNoLock
<PPB_Graphics3D_API
> enter_graphics(
127 graphics_context
.host_resource(), true);
128 if (enter_graphics
.failed())
129 return PP_ERROR_FAILED
;
130 PPB_Graphics3D_Impl
* graphics3d
=
131 static_cast<PPB_Graphics3D_Impl
*>(enter_graphics
.object());
133 CommandBufferProxyImpl
* command_buffer
= graphics3d
->GetCommandBufferProxy();
135 return PP_ERROR_FAILED
;
137 profile_
= PepperToMediaVideoProfile(profile
);
138 software_fallback_allowed_
= (acceleration
!= PP_HARDWAREACCELERATION_ONLY
);
140 // Check for Dev API use
141 // TODO(lpique): remove check when PPB_VideoDecoder_1_1 reaches beta/stable.
142 // https://crbug.com/520323
143 if (min_picture_count
!= 0) {
144 ContentRendererClient
* client
= GetContentClient()->renderer();
145 bool allowed
= client
->IsPluginAllowedToUseDevChannelAPIs();
147 return PP_ERROR_NOTSUPPORTED
;
149 min_picture_count_
= min_picture_count
;
151 if (acceleration
!= PP_HARDWAREACCELERATION_NONE
) {
152 // This is not synchronous, but subsequent IPC messages will be buffered, so
153 // it is okay to immediately send IPC messages.
154 decoder_
= command_buffer
->CreateVideoDecoder();
155 if (decoder_
&& decoder_
->Initialize(profile_
, this)) {
160 if (acceleration
== PP_HARDWAREACCELERATION_ONLY
)
161 return PP_ERROR_NOTSUPPORTED
;
164 #if defined(OS_ANDROID)
165 return PP_ERROR_NOTSUPPORTED
;
167 if (!TryFallbackToSoftwareDecoder())
168 return PP_ERROR_FAILED
;
175 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
176 ppapi::host::HostMessageContext
* context
,
180 return PP_ERROR_FAILED
;
182 // Make the buffers larger since we hope to reuse them.
185 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize
));
186 if (shm_size
> ppapi::proxy::kMaximumBitstreamBufferSize
)
187 return PP_ERROR_FAILED
;
189 if (shm_id
>= ppapi::proxy::kMaximumPendingDecodes
)
190 return PP_ERROR_FAILED
;
191 // The shm_id must be inside or at the end of shm_buffers_.
192 if (shm_id
> shm_buffers_
.size())
193 return PP_ERROR_FAILED
;
194 // Reject an attempt to reallocate a busy shm buffer.
195 if (shm_id
< shm_buffers_
.size() && shm_buffer_busy_
[shm_id
])
196 return PP_ERROR_FAILED
;
198 content::RenderThread
* render_thread
= content::RenderThread::Get();
199 scoped_ptr
<base::SharedMemory
> shm(
200 render_thread
->HostAllocateSharedMemoryBuffer(shm_size
).Pass());
201 if (!shm
|| !shm
->Map(shm_size
))
202 return PP_ERROR_FAILED
;
204 base::SharedMemoryHandle shm_handle
= shm
->handle();
205 if (shm_id
== shm_buffers_
.size()) {
206 shm_buffers_
.push_back(shm
.release());
207 shm_buffer_busy_
.push_back(false);
209 // Remove the old buffer. Delete manually since ScopedVector won't delete
210 // the existing element if we just assign over it.
211 delete shm_buffers_
[shm_id
];
212 shm_buffers_
[shm_id
] = shm
.release();
215 SerializedHandle
handle(
216 renderer_ppapi_host_
->ShareSharedMemoryHandleWithRemote(shm_handle
),
218 ppapi::host::ReplyMessageContext reply_context
=
219 context
->MakeReplyMessageContext();
220 reply_context
.params
.AppendHandle(handle
);
221 host()->SendReply(reply_context
,
222 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size
));
224 return PP_OK_COMPLETIONPENDING
;
227 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
228 ppapi::host::HostMessageContext
* context
,
233 return PP_ERROR_FAILED
;
235 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
236 if (static_cast<size_t>(shm_id
) >= shm_buffers_
.size())
237 return PP_ERROR_FAILED
;
238 // Reject an attempt to pass a busy buffer to the decoder again.
239 if (shm_buffer_busy_
[shm_id
])
240 return PP_ERROR_FAILED
;
241 // Reject non-unique decode_id values.
242 if (GetPendingDecodeById(decode_id
) != pending_decodes_
.end())
243 return PP_ERROR_FAILED
;
245 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
246 return PP_ERROR_FAILED
;
248 pending_decodes_
.push_back(PendingDecode(decode_id
, shm_id
, size
,
249 context
->MakeReplyMessageContext()));
251 shm_buffer_busy_
[shm_id
] = true;
253 media::BitstreamBuffer(decode_id
, shm_buffers_
[shm_id
]->handle(), size
));
255 return PP_OK_COMPLETIONPENDING
;
258 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
259 ppapi::host::HostMessageContext
* context
,
261 const std::vector
<uint32_t>& texture_ids
) {
263 return PP_ERROR_FAILED
;
266 // Verify that the new texture IDs are unique and store them in
268 PictureBufferMap new_textures
;
269 for (uint32_t i
= 0; i
< texture_ids
.size(); i
++) {
270 if (picture_buffer_map_
.find(texture_ids
[i
]) != picture_buffer_map_
.end() ||
271 new_textures
.find(texture_ids
[i
]) != new_textures
.end()) {
272 // Can't assign the same texture more than once.
273 return PP_ERROR_BADARGUMENT
;
276 std::make_pair(texture_ids
[i
], PictureBufferState::ASSIGNED
));
279 picture_buffer_map_
.insert(new_textures
.begin(), new_textures
.end());
281 std::vector
<media::PictureBuffer
> picture_buffers
;
282 for (uint32_t i
= 0; i
< texture_ids
.size(); i
++) {
283 media::PictureBuffer
buffer(
284 texture_ids
[i
], // Use the texture_id to identify the buffer.
285 gfx::Size(size
.width
, size
.height
),
287 picture_buffers
.push_back(buffer
);
289 decoder_
->AssignPictureBuffers(picture_buffers
);
293 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
294 ppapi::host::HostMessageContext
* context
,
295 uint32_t texture_id
) {
297 return PP_ERROR_FAILED
;
300 PictureBufferMap::iterator it
= picture_buffer_map_
.find(texture_id
);
301 if (it
== picture_buffer_map_
.end())
302 return PP_ERROR_BADARGUMENT
;
304 switch (it
->second
) {
305 case PictureBufferState::ASSIGNED
:
306 return PP_ERROR_BADARGUMENT
;
308 case PictureBufferState::IN_USE
:
309 it
->second
= PictureBufferState::ASSIGNED
;
310 decoder_
->ReusePictureBuffer(texture_id
);
313 case PictureBufferState::DISMISSED
:
314 picture_buffer_map_
.erase(it
);
315 // The texture was already dismissed by the decoder. Notify the plugin.
316 host()->SendUnsolicitedReply(
318 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id
));
325 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
326 ppapi::host::HostMessageContext
* context
) {
328 return PP_ERROR_FAILED
;
330 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
331 return PP_ERROR_FAILED
;
333 flush_reply_context_
= context
->MakeReplyMessageContext();
336 return PP_OK_COMPLETIONPENDING
;
339 int32_t PepperVideoDecoderHost::OnHostMsgReset(
340 ppapi::host::HostMessageContext
* context
) {
342 return PP_ERROR_FAILED
;
344 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
345 return PP_ERROR_FAILED
;
347 reset_reply_context_
= context
->MakeReplyMessageContext();
350 return PP_OK_COMPLETIONPENDING
;
353 void PepperVideoDecoderHost::ProvidePictureBuffers(
354 uint32 requested_num_of_buffers
,
355 const gfx::Size
& dimensions
,
356 uint32 texture_target
) {
357 RequestTextures(std::max(min_picture_count_
, requested_num_of_buffers
),
360 std::vector
<gpu::Mailbox
>());
363 void PepperVideoDecoderHost::PictureReady(const media::Picture
& picture
) {
364 PictureBufferMap::iterator it
=
365 picture_buffer_map_
.find(picture
.picture_buffer_id());
366 DCHECK(it
!= picture_buffer_map_
.end());
367 DCHECK(it
->second
== PictureBufferState::ASSIGNED
);
368 it
->second
= PictureBufferState::IN_USE
;
370 // Don't bother validating the visible rect, since the plugin process is less
371 // trusted than the gpu process.
372 PP_Rect visible_rect
= PP_FromGfxRect(picture
.visible_rect());
373 host()->SendUnsolicitedReply(pp_resource(),
374 PpapiPluginMsg_VideoDecoder_PictureReady(
375 picture
.bitstream_buffer_id(),
376 picture
.picture_buffer_id(), visible_rect
));
379 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id
) {
380 PictureBufferMap::iterator it
= picture_buffer_map_
.find(picture_buffer_id
);
381 DCHECK(it
!= picture_buffer_map_
.end());
383 // If the texture is still used by the plugin keep it until the plugin
385 if (it
->second
== PictureBufferState::IN_USE
) {
386 it
->second
= PictureBufferState::DISMISSED
;
390 DCHECK(it
->second
== PictureBufferState::ASSIGNED
);
391 picture_buffer_map_
.erase(it
);
392 host()->SendUnsolicitedReply(
394 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id
));
397 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
398 int32 bitstream_buffer_id
) {
399 PendingDecodeList::iterator it
= GetPendingDecodeById(bitstream_buffer_id
);
400 if (it
== pending_decodes_
.end()) {
404 host()->SendReply(it
->reply_context
,
405 PpapiPluginMsg_VideoDecoder_DecodeReply(it
->shm_id
));
406 shm_buffer_busy_
[it
->shm_id
] = false;
407 pending_decodes_
.erase(it
);
410 void PepperVideoDecoderHost::NotifyFlushDone() {
411 DCHECK(pending_decodes_
.empty());
412 host()->SendReply(flush_reply_context_
,
413 PpapiPluginMsg_VideoDecoder_FlushReply());
414 flush_reply_context_
= ppapi::host::ReplyMessageContext();
417 void PepperVideoDecoderHost::NotifyResetDone() {
418 DCHECK(pending_decodes_
.empty());
419 host()->SendReply(reset_reply_context_
,
420 PpapiPluginMsg_VideoDecoder_ResetReply());
421 reset_reply_context_
= ppapi::host::ReplyMessageContext();
424 void PepperVideoDecoderHost::NotifyError(
425 media::VideoDecodeAccelerator::Error error
) {
426 int32_t pp_error
= PP_ERROR_FAILED
;
428 case media::VideoDecodeAccelerator::UNREADABLE_INPUT
:
429 pp_error
= PP_ERROR_MALFORMED_INPUT
;
431 case media::VideoDecodeAccelerator::ILLEGAL_STATE
:
432 case media::VideoDecodeAccelerator::INVALID_ARGUMENT
:
433 case media::VideoDecodeAccelerator::PLATFORM_FAILURE
:
434 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM
:
435 pp_error
= PP_ERROR_RESOURCE_FAILED
;
437 // No default case, to catch unhandled enum values.
440 // Try to initialize software decoder and use it instead.
441 if (!software_fallback_used_
&& software_fallback_allowed_
) {
443 << "Hardware decoder has returned an error. Trying Software decoder.";
444 if (TryFallbackToSoftwareDecoder())
448 host()->SendUnsolicitedReply(
449 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error
));
452 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id
) {
453 PendingDecodeList::const_iterator it
= GetPendingDecodeById(decode_id
);
454 DCHECK(it
!= pending_decodes_
.end());
455 uint32_t shm_id
= it
->shm_id
;
456 return static_cast<uint8_t*>(shm_buffers_
[shm_id
]->memory());
459 void PepperVideoDecoderHost::RequestTextures(
460 uint32 requested_num_of_buffers
,
461 const gfx::Size
& dimensions
,
462 uint32 texture_target
,
463 const std::vector
<gpu::Mailbox
>& mailboxes
) {
464 host()->SendUnsolicitedReply(
466 PpapiPluginMsg_VideoDecoder_RequestTextures(
467 requested_num_of_buffers
,
468 PP_MakeSize(dimensions
.width(), dimensions
.height()),
473 bool PepperVideoDecoderHost::TryFallbackToSoftwareDecoder() {
474 #if defined(OS_ANDROID)
477 DCHECK(!software_fallback_used_
&& software_fallback_allowed_
);
479 uint32_t shim_texture_pool_size
= media::limits::kMaxVideoFrames
+ 1;
480 shim_texture_pool_size
= std::max(shim_texture_pool_size
,
482 scoped_ptr
<VideoDecoderShim
> new_decoder(
483 new VideoDecoderShim(this, shim_texture_pool_size
));
484 if (!new_decoder
->Initialize(profile_
, this))
487 software_fallback_used_
= true;
488 decoder_
.reset(new_decoder
.release());
490 // Dismiss all assigned pictures and mark all pictures in use as DISMISSED.
491 PictureBufferMap pictures_pending_dismission
;
492 for (auto& picture
: picture_buffer_map_
) {
493 if (picture
.second
== PictureBufferState::ASSIGNED
) {
494 host()->SendUnsolicitedReply(
496 PpapiPluginMsg_VideoDecoder_DismissPicture(picture
.first
));
498 pictures_pending_dismission
.insert(
499 std::make_pair(picture
.first
, PictureBufferState::DISMISSED
));
502 picture_buffer_map_
.swap(pictures_pending_dismission
);
504 // If there was a pending Reset() it can be finished now.
505 if (reset_reply_context_
.is_valid()) {
506 while (!pending_decodes_
.empty()) {
507 const PendingDecode
& decode
= pending_decodes_
.front();
508 host()->SendReply(decode
.reply_context
,
509 PpapiPluginMsg_VideoDecoder_DecodeReply(decode
.shm_id
));
510 DCHECK(shm_buffer_busy_
[decode
.shm_id
]);
511 shm_buffer_busy_
[decode
.shm_id
] = false;
512 pending_decodes_
.pop_front();
517 // Resubmit all pending decodes.
518 for (const PendingDecode
& decode
: pending_decodes_
) {
519 DCHECK(shm_buffer_busy_
[decode
.shm_id
]);
520 decoder_
->Decode(media::BitstreamBuffer(
521 decode
.decode_id
, shm_buffers_
[decode
.shm_id
]->handle(), decode
.size
));
524 // Flush the new decoder if Flush() was pending.
525 if (flush_reply_context_
.is_valid())
532 PepperVideoDecoderHost::PendingDecodeList::iterator
533 PepperVideoDecoderHost::GetPendingDecodeById(int32_t decode_id
) {
534 return std::find_if(pending_decodes_
.begin(), pending_decodes_
.end(),
535 [decode_id
](const PendingDecode
& item
) {
536 return item
.decode_id
== decode_id
;
540 } // namespace content