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.
4
5 #include "content/renderer/pepper/pepper_video_decoder_host.h"
6
7 #include "base/bind.h"
8 #include "base/memory/shared_memory.h"
9 #include "content/common/gpu/client/gpu_channel_host.h"
10 #include "content/public/renderer/render_thread.h"
11 #include "content/public/renderer/renderer_ppapi_host.h"
12 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
13 #include "content/renderer/pepper/video_decoder_shim.h"
14 #include "media/video/video_decode_accelerator.h"
15 #include "ppapi/c/pp_completion_callback.h"
16 #include "ppapi/c/pp_errors.h"
17 #include "ppapi/host/dispatch_host_message.h"
18 #include "ppapi/host/ppapi_host.h"
19 #include "ppapi/proxy/ppapi_messages.h"
20 #include "ppapi/proxy/video_decoder_constants.h"
21 #include "ppapi/thunk/enter.h"
22 #include "ppapi/thunk/ppb_graphics_3d_api.h"
23
24 using ppapi::proxy::SerializedHandle;
25 using ppapi::thunk::EnterResourceNoLock;
26 using ppapi::thunk::PPB_Graphics3D_API;
27
28 namespace content {
29
30 namespace {
31
PepperToMediaVideoProfile(PP_VideoProfile profile)32 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
33 switch (profile) {
34 case PP_VIDEOPROFILE_H264BASELINE:
35 return media::H264PROFILE_BASELINE;
36 case PP_VIDEOPROFILE_H264MAIN:
37 return media::H264PROFILE_MAIN;
38 case PP_VIDEOPROFILE_H264EXTENDED:
39 return media::H264PROFILE_EXTENDED;
40 case PP_VIDEOPROFILE_H264HIGH:
41 return media::H264PROFILE_HIGH;
42 case PP_VIDEOPROFILE_H264HIGH10PROFILE:
43 return media::H264PROFILE_HIGH10PROFILE;
44 case PP_VIDEOPROFILE_H264HIGH422PROFILE:
45 return media::H264PROFILE_HIGH422PROFILE;
46 case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE:
47 return media::H264PROFILE_HIGH444PREDICTIVEPROFILE;
48 case PP_VIDEOPROFILE_H264SCALABLEBASELINE:
49 return media::H264PROFILE_SCALABLEBASELINE;
50 case PP_VIDEOPROFILE_H264SCALABLEHIGH:
51 return media::H264PROFILE_SCALABLEHIGH;
52 case PP_VIDEOPROFILE_H264STEREOHIGH:
53 return media::H264PROFILE_STEREOHIGH;
54 case PP_VIDEOPROFILE_H264MULTIVIEWHIGH:
55 return media::H264PROFILE_MULTIVIEWHIGH;
56 case PP_VIDEOPROFILE_VP8_ANY:
57 return media::VP8PROFILE_ANY;
58 case PP_VIDEOPROFILE_VP9_ANY:
59 return media::VP9PROFILE_ANY;
60 // No default case, to catch unhandled PP_VideoProfile values.
61 }
62
63 return media::VIDEO_CODEC_PROFILE_UNKNOWN;
64 }
65
66 } // namespace
67
PendingDecode(uint32_t shm_id,const ppapi::host::ReplyMessageContext & reply_context)68 PepperVideoDecoderHost::PendingDecode::PendingDecode(
69 uint32_t shm_id,
70 const ppapi::host::ReplyMessageContext& reply_context)
71 : shm_id(shm_id), reply_context(reply_context) {
72 }
73
~PendingDecode()74 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {
75 }
76
PepperVideoDecoderHost(RendererPpapiHost * host,PP_Instance instance,PP_Resource resource)77 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host,
78 PP_Instance instance,
79 PP_Resource resource)
80 : ResourceHost(host->GetPpapiHost(), instance, resource),
81 renderer_ppapi_host_(host),
82 initialized_(false) {
83 }
84
~PepperVideoDecoderHost()85 PepperVideoDecoderHost::~PepperVideoDecoderHost() {
86 }
87
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)88 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
89 const IPC::Message& msg,
90 ppapi::host::HostMessageContext* context) {
91 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg)
92 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
93 OnHostMsgInitialize)
94 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
95 OnHostMsgGetShm)
96 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
97 OnHostMsgDecode)
98 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
99 OnHostMsgAssignTextures)
100 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
101 OnHostMsgRecyclePicture)
102 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
103 OnHostMsgFlush)
104 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
105 OnHostMsgReset)
106 PPAPI_END_MESSAGE_MAP()
107 return PP_ERROR_FAILED;
108 }
109
OnHostMsgInitialize(ppapi::host::HostMessageContext * context,const ppapi::HostResource & graphics_context,PP_VideoProfile profile,PP_HardwareAcceleration acceleration)110 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
111 ppapi::host::HostMessageContext* context,
112 const ppapi::HostResource& graphics_context,
113 PP_VideoProfile profile,
114 PP_HardwareAcceleration acceleration) {
115 if (initialized_)
116 return PP_ERROR_FAILED;
117
118 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(
119 graphics_context.host_resource(), true);
120 if (enter_graphics.failed())
121 return PP_ERROR_FAILED;
122 PPB_Graphics3D_Impl* graphics3d =
123 static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
124
125 int command_buffer_route_id = graphics3d->GetCommandBufferRouteId();
126 if (!command_buffer_route_id)
127 return PP_ERROR_FAILED;
128
129 media::VideoCodecProfile media_profile = PepperToMediaVideoProfile(profile);
130
131 if (acceleration != PP_HARDWAREACCELERATION_NONE) {
132 // This is not synchronous, but subsequent IPC messages will be buffered, so
133 // it is okay to immediately send IPC messages through the returned channel.
134 GpuChannelHost* channel = graphics3d->channel();
135 DCHECK(channel);
136 decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
137 if (decoder_ && decoder_->Initialize(media_profile, this)) {
138 initialized_ = true;
139 return PP_OK;
140 }
141 decoder_.reset();
142 if (acceleration == PP_HARDWAREACCELERATION_ONLY)
143 return PP_ERROR_NOTSUPPORTED;
144 }
145
146 #if defined(OS_ANDROID)
147 return PP_ERROR_NOTSUPPORTED;
148 #else
149 decoder_.reset(new VideoDecoderShim(this));
150 initialize_reply_context_ = context->MakeReplyMessageContext();
151 decoder_->Initialize(media_profile, this);
152
153 return PP_OK_COMPLETIONPENDING;
154 #endif
155 }
156
OnHostMsgGetShm(ppapi::host::HostMessageContext * context,uint32_t shm_id,uint32_t shm_size)157 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
158 ppapi::host::HostMessageContext* context,
159 uint32_t shm_id,
160 uint32_t shm_size) {
161 if (!initialized_)
162 return PP_ERROR_FAILED;
163
164 // Make the buffers larger since we hope to reuse them.
165 shm_size = std::max(
166 shm_size,
167 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
168 if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
169 return PP_ERROR_FAILED;
170
171 if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
172 return PP_ERROR_FAILED;
173 // The shm_id must be inside or at the end of shm_buffers_.
174 if (shm_id > shm_buffers_.size())
175 return PP_ERROR_FAILED;
176 // Reject an attempt to reallocate a busy shm buffer.
177 if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
178 return PP_ERROR_FAILED;
179
180 content::RenderThread* render_thread = content::RenderThread::Get();
181 scoped_ptr<base::SharedMemory> shm(
182 render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
183 if (!shm || !shm->Map(shm_size))
184 return PP_ERROR_FAILED;
185
186 base::SharedMemoryHandle shm_handle = shm->handle();
187 if (shm_id == shm_buffers_.size()) {
188 shm_buffers_.push_back(shm.release());
189 shm_buffer_busy_.push_back(false);
190 } else {
191 // Remove the old buffer. Delete manually since ScopedVector won't delete
192 // the existing element if we just assign over it.
193 delete shm_buffers_[shm_id];
194 shm_buffers_[shm_id] = shm.release();
195 }
196
197 #if defined(OS_WIN)
198 base::PlatformFile platform_file = shm_handle;
199 #elif defined(OS_POSIX)
200 base::PlatformFile platform_file = shm_handle.fd;
201 #else
202 #error Not implemented.
203 #endif
204 SerializedHandle handle(
205 renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false),
206 shm_size);
207 ppapi::host::ReplyMessageContext reply_context =
208 context->MakeReplyMessageContext();
209 reply_context.params.AppendHandle(handle);
210 host()->SendReply(reply_context,
211 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
212
213 return PP_OK_COMPLETIONPENDING;
214 }
215
OnHostMsgDecode(ppapi::host::HostMessageContext * context,uint32_t shm_id,uint32_t size,int32_t decode_id)216 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
217 ppapi::host::HostMessageContext* context,
218 uint32_t shm_id,
219 uint32_t size,
220 int32_t decode_id) {
221 if (!initialized_)
222 return PP_ERROR_FAILED;
223 DCHECK(decoder_);
224 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
225 if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
226 return PP_ERROR_FAILED;
227 // Reject an attempt to pass a busy buffer to the decoder again.
228 if (shm_buffer_busy_[shm_id])
229 return PP_ERROR_FAILED;
230 // Reject non-unique decode_id values.
231 if (pending_decodes_.find(decode_id) != pending_decodes_.end())
232 return PP_ERROR_FAILED;
233
234 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
235 return PP_ERROR_FAILED;
236
237 pending_decodes_.insert(std::make_pair(
238 decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
239
240 shm_buffer_busy_[shm_id] = true;
241 decoder_->Decode(
242 media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
243
244 return PP_OK_COMPLETIONPENDING;
245 }
246
OnHostMsgAssignTextures(ppapi::host::HostMessageContext * context,const PP_Size & size,const std::vector<uint32_t> & texture_ids)247 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
248 ppapi::host::HostMessageContext* context,
249 const PP_Size& size,
250 const std::vector<uint32_t>& texture_ids) {
251 if (!initialized_)
252 return PP_ERROR_FAILED;
253 DCHECK(decoder_);
254
255 std::vector<media::PictureBuffer> picture_buffers;
256 for (uint32 i = 0; i < texture_ids.size(); i++) {
257 media::PictureBuffer buffer(
258 texture_ids[i], // Use the texture_id to identify the buffer.
259 gfx::Size(size.width, size.height),
260 texture_ids[i]);
261 picture_buffers.push_back(buffer);
262 }
263 decoder_->AssignPictureBuffers(picture_buffers);
264 return PP_OK;
265 }
266
OnHostMsgRecyclePicture(ppapi::host::HostMessageContext * context,uint32_t texture_id)267 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
268 ppapi::host::HostMessageContext* context,
269 uint32_t texture_id) {
270 if (!initialized_)
271 return PP_ERROR_FAILED;
272 DCHECK(decoder_);
273
274 decoder_->ReusePictureBuffer(texture_id);
275 return PP_OK;
276 }
277
OnHostMsgFlush(ppapi::host::HostMessageContext * context)278 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
279 ppapi::host::HostMessageContext* context) {
280 if (!initialized_)
281 return PP_ERROR_FAILED;
282 DCHECK(decoder_);
283 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
284 return PP_ERROR_FAILED;
285
286 flush_reply_context_ = context->MakeReplyMessageContext();
287 decoder_->Flush();
288
289 return PP_OK_COMPLETIONPENDING;
290 }
291
OnHostMsgReset(ppapi::host::HostMessageContext * context)292 int32_t PepperVideoDecoderHost::OnHostMsgReset(
293 ppapi::host::HostMessageContext* context) {
294 if (!initialized_)
295 return PP_ERROR_FAILED;
296 DCHECK(decoder_);
297 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
298 return PP_ERROR_FAILED;
299
300 reset_reply_context_ = context->MakeReplyMessageContext();
301 decoder_->Reset();
302
303 return PP_OK_COMPLETIONPENDING;
304 }
305
ProvidePictureBuffers(uint32 requested_num_of_buffers,const gfx::Size & dimensions,uint32 texture_target)306 void PepperVideoDecoderHost::ProvidePictureBuffers(
307 uint32 requested_num_of_buffers,
308 const gfx::Size& dimensions,
309 uint32 texture_target) {
310 RequestTextures(requested_num_of_buffers,
311 dimensions,
312 texture_target,
313 std::vector<gpu::Mailbox>());
314 }
315
PictureReady(const media::Picture & picture)316 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
317 // So far picture.visible_rect is not used. If used, visible_rect should
318 // be validated since it comes from GPU process and may not be trustworthy.
319 host()->SendUnsolicitedReply(
320 pp_resource(),
321 PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
322 picture.picture_buffer_id()));
323 }
324
DismissPictureBuffer(int32 picture_buffer_id)325 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
326 host()->SendUnsolicitedReply(
327 pp_resource(),
328 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
329 }
330
NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id)331 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
332 int32 bitstream_buffer_id) {
333 PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
334 if (it == pending_decodes_.end()) {
335 NOTREACHED();
336 return;
337 }
338 const PendingDecode& pending_decode = it->second;
339 host()->SendReply(
340 pending_decode.reply_context,
341 PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
342 shm_buffer_busy_[pending_decode.shm_id] = false;
343 pending_decodes_.erase(it);
344 }
345
NotifyFlushDone()346 void PepperVideoDecoderHost::NotifyFlushDone() {
347 DCHECK(pending_decodes_.empty());
348 host()->SendReply(flush_reply_context_,
349 PpapiPluginMsg_VideoDecoder_FlushReply());
350 flush_reply_context_ = ppapi::host::ReplyMessageContext();
351 }
352
NotifyResetDone()353 void PepperVideoDecoderHost::NotifyResetDone() {
354 DCHECK(pending_decodes_.empty());
355 host()->SendReply(reset_reply_context_,
356 PpapiPluginMsg_VideoDecoder_ResetReply());
357 reset_reply_context_ = ppapi::host::ReplyMessageContext();
358 }
359
NotifyError(media::VideoDecodeAccelerator::Error error)360 void PepperVideoDecoderHost::NotifyError(
361 media::VideoDecodeAccelerator::Error error) {
362 int32_t pp_error = PP_ERROR_FAILED;
363 switch (error) {
364 case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
365 pp_error = PP_ERROR_MALFORMED_INPUT;
366 break;
367 case media::VideoDecodeAccelerator::ILLEGAL_STATE:
368 case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
369 case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
370 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
371 pp_error = PP_ERROR_RESOURCE_FAILED;
372 break;
373 // No default case, to catch unhandled enum values.
374 }
375 host()->SendUnsolicitedReply(
376 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
377 }
378
OnInitializeComplete(int32_t result)379 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
380 if (!initialized_) {
381 if (result == PP_OK)
382 initialized_ = true;
383 initialize_reply_context_.params.set_result(result);
384 host()->SendReply(initialize_reply_context_,
385 PpapiPluginMsg_VideoDecoder_InitializeReply());
386 }
387 }
388
DecodeIdToAddress(uint32_t decode_id)389 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
390 PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
391 DCHECK(it != pending_decodes_.end());
392 uint32_t shm_id = it->second.shm_id;
393 return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
394 }
395
RequestTextures(uint32 requested_num_of_buffers,const gfx::Size & dimensions,uint32 texture_target,const std::vector<gpu::Mailbox> & mailboxes)396 void PepperVideoDecoderHost::RequestTextures(
397 uint32 requested_num_of_buffers,
398 const gfx::Size& dimensions,
399 uint32 texture_target,
400 const std::vector<gpu::Mailbox>& mailboxes) {
401 host()->SendUnsolicitedReply(
402 pp_resource(),
403 PpapiPluginMsg_VideoDecoder_RequestTextures(
404 requested_num_of_buffers,
405 PP_MakeSize(dimensions.width(), dimensions.height()),
406 texture_target,
407 mailboxes));
408 }
409
410 } // namespace content
411