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/gpu_channel_host.h"
10 #include "content/common/pepper_file_util.h"
11 #include "content/public/renderer/render_thread.h"
12 #include "content/public/renderer/renderer_ppapi_host.h"
13 #include "content/renderer/pepper/gfx_conversion.h"
14 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
15 #include "content/renderer/pepper/video_decoder_shim.h"
16 #include "media/video/video_decode_accelerator.h"
17 #include "ppapi/c/pp_completion_callback.h"
18 #include "ppapi/c/pp_errors.h"
19 #include "ppapi/host/dispatch_host_message.h"
20 #include "ppapi/host/ppapi_host.h"
21 #include "ppapi/proxy/ppapi_messages.h"
22 #include "ppapi/proxy/video_decoder_constants.h"
23 #include "ppapi/thunk/enter.h"
24 #include "ppapi/thunk/ppb_graphics_3d_api.h"
26 using ppapi::proxy::SerializedHandle
;
27 using ppapi::thunk::EnterResourceNoLock
;
28 using ppapi::thunk::PPB_Graphics3D_API
;
34 media::VideoCodecProfile
PepperToMediaVideoProfile(PP_VideoProfile profile
) {
36 case PP_VIDEOPROFILE_H264BASELINE
:
37 return media::H264PROFILE_BASELINE
;
38 case PP_VIDEOPROFILE_H264MAIN
:
39 return media::H264PROFILE_MAIN
;
40 case PP_VIDEOPROFILE_H264EXTENDED
:
41 return media::H264PROFILE_EXTENDED
;
42 case PP_VIDEOPROFILE_H264HIGH
:
43 return media::H264PROFILE_HIGH
;
44 case PP_VIDEOPROFILE_H264HIGH10PROFILE
:
45 return media::H264PROFILE_HIGH10PROFILE
;
46 case PP_VIDEOPROFILE_H264HIGH422PROFILE
:
47 return media::H264PROFILE_HIGH422PROFILE
;
48 case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE
:
49 return media::H264PROFILE_HIGH444PREDICTIVEPROFILE
;
50 case PP_VIDEOPROFILE_H264SCALABLEBASELINE
:
51 return media::H264PROFILE_SCALABLEBASELINE
;
52 case PP_VIDEOPROFILE_H264SCALABLEHIGH
:
53 return media::H264PROFILE_SCALABLEHIGH
;
54 case PP_VIDEOPROFILE_H264STEREOHIGH
:
55 return media::H264PROFILE_STEREOHIGH
;
56 case PP_VIDEOPROFILE_H264MULTIVIEWHIGH
:
57 return media::H264PROFILE_MULTIVIEWHIGH
;
58 case PP_VIDEOPROFILE_VP8_ANY
:
59 return media::VP8PROFILE_ANY
;
60 case PP_VIDEOPROFILE_VP9_ANY
:
61 return media::VP9PROFILE_ANY
;
62 // No default case, to catch unhandled PP_VideoProfile values.
65 return media::VIDEO_CODEC_PROFILE_UNKNOWN
;
70 PepperVideoDecoderHost::PendingDecode::PendingDecode(
72 const ppapi::host::ReplyMessageContext
& reply_context
)
73 : shm_id(shm_id
), reply_context(reply_context
) {
76 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {
79 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost
* host
,
82 : ResourceHost(host
->GetPpapiHost(), instance
, resource
),
83 renderer_ppapi_host_(host
),
87 PepperVideoDecoderHost::~PepperVideoDecoderHost() {
90 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
91 const IPC::Message
& msg
,
92 ppapi::host::HostMessageContext
* context
) {
93 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost
, msg
)
94 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize
,
96 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm
,
98 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode
,
100 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures
,
101 OnHostMsgAssignTextures
)
102 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture
,
103 OnHostMsgRecyclePicture
)
104 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush
,
106 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset
,
108 PPAPI_END_MESSAGE_MAP()
109 return PP_ERROR_FAILED
;
112 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
113 ppapi::host::HostMessageContext
* context
,
114 const ppapi::HostResource
& graphics_context
,
115 PP_VideoProfile profile
,
116 PP_HardwareAcceleration acceleration
) {
118 return PP_ERROR_FAILED
;
120 EnterResourceNoLock
<PPB_Graphics3D_API
> enter_graphics(
121 graphics_context
.host_resource(), true);
122 if (enter_graphics
.failed())
123 return PP_ERROR_FAILED
;
124 PPB_Graphics3D_Impl
* graphics3d
=
125 static_cast<PPB_Graphics3D_Impl
*>(enter_graphics
.object());
127 int command_buffer_route_id
= graphics3d
->GetCommandBufferRouteId();
128 if (!command_buffer_route_id
)
129 return PP_ERROR_FAILED
;
131 media::VideoCodecProfile media_profile
= PepperToMediaVideoProfile(profile
);
133 if (acceleration
!= PP_HARDWAREACCELERATION_NONE
) {
134 // This is not synchronous, but subsequent IPC messages will be buffered, so
135 // it is okay to immediately send IPC messages through the returned channel.
136 GpuChannelHost
* channel
= graphics3d
->channel();
138 decoder_
= channel
->CreateVideoDecoder(command_buffer_route_id
);
139 if (decoder_
&& decoder_
->Initialize(media_profile
, this)) {
144 if (acceleration
== PP_HARDWAREACCELERATION_ONLY
)
145 return PP_ERROR_NOTSUPPORTED
;
148 #if defined(OS_ANDROID)
149 return PP_ERROR_NOTSUPPORTED
;
151 decoder_
.reset(new VideoDecoderShim(this));
152 initialize_reply_context_
= context
->MakeReplyMessageContext();
153 decoder_
->Initialize(media_profile
, this);
155 return PP_OK_COMPLETIONPENDING
;
159 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
160 ppapi::host::HostMessageContext
* context
,
164 return PP_ERROR_FAILED
;
166 // Make the buffers larger since we hope to reuse them.
169 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize
));
170 if (shm_size
> ppapi::proxy::kMaximumBitstreamBufferSize
)
171 return PP_ERROR_FAILED
;
173 if (shm_id
>= ppapi::proxy::kMaximumPendingDecodes
)
174 return PP_ERROR_FAILED
;
175 // The shm_id must be inside or at the end of shm_buffers_.
176 if (shm_id
> shm_buffers_
.size())
177 return PP_ERROR_FAILED
;
178 // Reject an attempt to reallocate a busy shm buffer.
179 if (shm_id
< shm_buffers_
.size() && shm_buffer_busy_
[shm_id
])
180 return PP_ERROR_FAILED
;
182 content::RenderThread
* render_thread
= content::RenderThread::Get();
183 scoped_ptr
<base::SharedMemory
> shm(
184 render_thread
->HostAllocateSharedMemoryBuffer(shm_size
).Pass());
185 if (!shm
|| !shm
->Map(shm_size
))
186 return PP_ERROR_FAILED
;
188 base::SharedMemoryHandle shm_handle
= shm
->handle();
189 if (shm_id
== shm_buffers_
.size()) {
190 shm_buffers_
.push_back(shm
.release());
191 shm_buffer_busy_
.push_back(false);
193 // Remove the old buffer. Delete manually since ScopedVector won't delete
194 // the existing element if we just assign over it.
195 delete shm_buffers_
[shm_id
];
196 shm_buffers_
[shm_id
] = shm
.release();
199 base::PlatformFile platform_file
=
200 PlatformFileFromSharedMemoryHandle(shm_handle
);
201 SerializedHandle
handle(
202 renderer_ppapi_host_
->ShareHandleWithRemote(platform_file
, false),
204 ppapi::host::ReplyMessageContext reply_context
=
205 context
->MakeReplyMessageContext();
206 reply_context
.params
.AppendHandle(handle
);
207 host()->SendReply(reply_context
,
208 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size
));
210 return PP_OK_COMPLETIONPENDING
;
213 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
214 ppapi::host::HostMessageContext
* context
,
219 return PP_ERROR_FAILED
;
221 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
222 if (static_cast<size_t>(shm_id
) >= shm_buffers_
.size())
223 return PP_ERROR_FAILED
;
224 // Reject an attempt to pass a busy buffer to the decoder again.
225 if (shm_buffer_busy_
[shm_id
])
226 return PP_ERROR_FAILED
;
227 // Reject non-unique decode_id values.
228 if (pending_decodes_
.find(decode_id
) != pending_decodes_
.end())
229 return PP_ERROR_FAILED
;
231 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
232 return PP_ERROR_FAILED
;
234 pending_decodes_
.insert(std::make_pair(
235 decode_id
, PendingDecode(shm_id
, context
->MakeReplyMessageContext())));
237 shm_buffer_busy_
[shm_id
] = true;
239 media::BitstreamBuffer(decode_id
, shm_buffers_
[shm_id
]->handle(), size
));
241 return PP_OK_COMPLETIONPENDING
;
244 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
245 ppapi::host::HostMessageContext
* context
,
247 const std::vector
<uint32_t>& texture_ids
) {
249 return PP_ERROR_FAILED
;
252 std::vector
<media::PictureBuffer
> picture_buffers
;
253 for (uint32 i
= 0; i
< texture_ids
.size(); i
++) {
254 media::PictureBuffer
buffer(
255 texture_ids
[i
], // Use the texture_id to identify the buffer.
256 gfx::Size(size
.width
, size
.height
),
258 picture_buffers
.push_back(buffer
);
260 decoder_
->AssignPictureBuffers(picture_buffers
);
264 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
265 ppapi::host::HostMessageContext
* context
,
266 uint32_t texture_id
) {
268 return PP_ERROR_FAILED
;
271 TextureSet::iterator it
= pictures_in_use_
.find(texture_id
);
272 if (it
== pictures_in_use_
.end())
273 return PP_ERROR_BADARGUMENT
;
275 pictures_in_use_
.erase(it
);
277 TextureSet::iterator dismissed_texture
=
278 dismissed_pictures_in_use_
.find(texture_id
);
279 if (dismissed_texture
!= dismissed_pictures_in_use_
.end()) {
280 // The texture was already dismissed by the decoder. Notify the plugin.
281 host()->SendUnsolicitedReply(
283 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id
));
284 dismissed_pictures_in_use_
.erase(dismissed_texture
);
286 decoder_
->ReusePictureBuffer(texture_id
);
292 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
293 ppapi::host::HostMessageContext
* context
) {
295 return PP_ERROR_FAILED
;
297 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
298 return PP_ERROR_FAILED
;
300 flush_reply_context_
= context
->MakeReplyMessageContext();
303 return PP_OK_COMPLETIONPENDING
;
306 int32_t PepperVideoDecoderHost::OnHostMsgReset(
307 ppapi::host::HostMessageContext
* context
) {
309 return PP_ERROR_FAILED
;
311 if (flush_reply_context_
.is_valid() || reset_reply_context_
.is_valid())
312 return PP_ERROR_FAILED
;
314 reset_reply_context_
= context
->MakeReplyMessageContext();
317 return PP_OK_COMPLETIONPENDING
;
320 void PepperVideoDecoderHost::ProvidePictureBuffers(
321 uint32 requested_num_of_buffers
,
322 const gfx::Size
& dimensions
,
323 uint32 texture_target
) {
324 RequestTextures(requested_num_of_buffers
,
327 std::vector
<gpu::Mailbox
>());
330 void PepperVideoDecoderHost::PictureReady(const media::Picture
& picture
) {
331 // Don't bother validating the visible rect, since the plugin process is less
332 // trusted than the gpu process.
333 DCHECK(pictures_in_use_
.find(picture
.picture_buffer_id()) ==
334 pictures_in_use_
.end());
335 pictures_in_use_
.insert(picture
.picture_buffer_id());
337 PP_Rect visible_rect
= PP_FromGfxRect(picture
.visible_rect());
338 host()->SendUnsolicitedReply(pp_resource(),
339 PpapiPluginMsg_VideoDecoder_PictureReady(
340 picture
.bitstream_buffer_id(),
341 picture
.picture_buffer_id(), visible_rect
));
344 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id
) {
345 // If the texture is still used by the plugin keep it until the plugin
347 if (pictures_in_use_
.find(picture_buffer_id
) != pictures_in_use_
.end()) {
348 dismissed_pictures_in_use_
.insert(picture_buffer_id
);
352 host()->SendUnsolicitedReply(
354 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id
));
357 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
358 int32 bitstream_buffer_id
) {
359 PendingDecodeMap::iterator it
= pending_decodes_
.find(bitstream_buffer_id
);
360 if (it
== pending_decodes_
.end()) {
364 const PendingDecode
& pending_decode
= it
->second
;
366 pending_decode
.reply_context
,
367 PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode
.shm_id
));
368 shm_buffer_busy_
[pending_decode
.shm_id
] = false;
369 pending_decodes_
.erase(it
);
372 void PepperVideoDecoderHost::NotifyFlushDone() {
373 DCHECK(pending_decodes_
.empty());
374 host()->SendReply(flush_reply_context_
,
375 PpapiPluginMsg_VideoDecoder_FlushReply());
376 flush_reply_context_
= ppapi::host::ReplyMessageContext();
379 void PepperVideoDecoderHost::NotifyResetDone() {
380 DCHECK(pending_decodes_
.empty());
381 host()->SendReply(reset_reply_context_
,
382 PpapiPluginMsg_VideoDecoder_ResetReply());
383 reset_reply_context_
= ppapi::host::ReplyMessageContext();
386 void PepperVideoDecoderHost::NotifyError(
387 media::VideoDecodeAccelerator::Error error
) {
388 int32_t pp_error
= PP_ERROR_FAILED
;
390 case media::VideoDecodeAccelerator::UNREADABLE_INPUT
:
391 pp_error
= PP_ERROR_MALFORMED_INPUT
;
393 case media::VideoDecodeAccelerator::ILLEGAL_STATE
:
394 case media::VideoDecodeAccelerator::INVALID_ARGUMENT
:
395 case media::VideoDecodeAccelerator::PLATFORM_FAILURE
:
396 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM
:
397 pp_error
= PP_ERROR_RESOURCE_FAILED
;
399 // No default case, to catch unhandled enum values.
401 host()->SendUnsolicitedReply(
402 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error
));
405 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result
) {
409 initialize_reply_context_
.params
.set_result(result
);
410 host()->SendReply(initialize_reply_context_
,
411 PpapiPluginMsg_VideoDecoder_InitializeReply());
415 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id
) {
416 PendingDecodeMap::const_iterator it
= pending_decodes_
.find(decode_id
);
417 DCHECK(it
!= pending_decodes_
.end());
418 uint32_t shm_id
= it
->second
.shm_id
;
419 return static_cast<uint8_t*>(shm_buffers_
[shm_id
]->memory());
422 void PepperVideoDecoderHost::RequestTextures(
423 uint32 requested_num_of_buffers
,
424 const gfx::Size
& dimensions
,
425 uint32 texture_target
,
426 const std::vector
<gpu::Mailbox
>& mailboxes
) {
427 host()->SendUnsolicitedReply(
429 PpapiPluginMsg_VideoDecoder_RequestTextures(
430 requested_num_of_buffers
,
431 PP_MakeSize(dimensions
.width(), dimensions
.height()),
436 } // namespace content