Roll src/third_party/WebKit 9d2dfea:3aea697 (svn 201972:201973)
[chromium-blink-merge.git] / content / renderer / pepper / pepper_video_decoder_host.cc
blob2c875ee07a3f417216dbea7464dcb60b8f26f9b9
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"
7 #include "base/bind.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;
33 namespace content {
35 namespace {
37 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
38 switch (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;
71 } // namespace
73 PepperVideoDecoderHost::PendingDecode::PendingDecode(
74 uint32_t shm_id,
75 const ppapi::host::ReplyMessageContext& reply_context)
76 : shm_id(shm_id), reply_context(reply_context) {
79 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {
82 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host,
83 PP_Instance instance,
84 PP_Resource resource)
85 : ResourceHost(host->GetPpapiHost(), instance, resource),
86 renderer_ppapi_host_(host),
87 min_picture_count_(0),
88 initialized_(false) {
91 PepperVideoDecoderHost::~PepperVideoDecoderHost() {
94 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
95 const IPC::Message& msg,
96 ppapi::host::HostMessageContext* context) {
97 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg)
98 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
99 OnHostMsgInitialize)
100 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
101 OnHostMsgGetShm)
102 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
103 OnHostMsgDecode)
104 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
105 OnHostMsgAssignTextures)
106 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
107 OnHostMsgRecyclePicture)
108 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
109 OnHostMsgFlush)
110 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
111 OnHostMsgReset)
112 PPAPI_END_MESSAGE_MAP()
113 return PP_ERROR_FAILED;
116 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
117 ppapi::host::HostMessageContext* context,
118 const ppapi::HostResource& graphics_context,
119 PP_VideoProfile profile,
120 PP_HardwareAcceleration acceleration,
121 uint32_t min_picture_count) {
122 if (initialized_)
123 return PP_ERROR_FAILED;
124 if (min_picture_count > ppapi::proxy::kMaximumPictureCount)
125 return PP_ERROR_BADARGUMENT;
127 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(
128 graphics_context.host_resource(), true);
129 if (enter_graphics.failed())
130 return PP_ERROR_FAILED;
131 PPB_Graphics3D_Impl* graphics3d =
132 static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
134 CommandBufferProxyImpl* command_buffer = graphics3d->GetCommandBufferProxy();
135 if (!command_buffer)
136 return PP_ERROR_FAILED;
138 media::VideoCodecProfile media_profile = PepperToMediaVideoProfile(profile);
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();
146 if (!allowed)
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(media_profile, this)) {
156 initialized_ = true;
157 return PP_OK;
159 decoder_.reset();
160 if (acceleration == PP_HARDWAREACCELERATION_ONLY)
161 return PP_ERROR_NOTSUPPORTED;
164 #if defined(OS_ANDROID)
165 return PP_ERROR_NOTSUPPORTED;
166 #else
167 uint32_t shim_texture_pool_size = media::limits::kMaxVideoFrames + 1;
168 shim_texture_pool_size = std::max(shim_texture_pool_size,
169 min_picture_count_);
170 decoder_.reset(new VideoDecoderShim(this, shim_texture_pool_size));
171 if (!decoder_->Initialize(media_profile, this))
172 return PP_ERROR_FAILED;
174 initialized_ = true;
175 return PP_OK;
176 #endif
179 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
180 ppapi::host::HostMessageContext* context,
181 uint32_t shm_id,
182 uint32_t shm_size) {
183 if (!initialized_)
184 return PP_ERROR_FAILED;
186 // Make the buffers larger since we hope to reuse them.
187 shm_size = std::max(
188 shm_size,
189 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
190 if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
191 return PP_ERROR_FAILED;
193 if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
194 return PP_ERROR_FAILED;
195 // The shm_id must be inside or at the end of shm_buffers_.
196 if (shm_id > shm_buffers_.size())
197 return PP_ERROR_FAILED;
198 // Reject an attempt to reallocate a busy shm buffer.
199 if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
200 return PP_ERROR_FAILED;
202 content::RenderThread* render_thread = content::RenderThread::Get();
203 scoped_ptr<base::SharedMemory> shm(
204 render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
205 if (!shm || !shm->Map(shm_size))
206 return PP_ERROR_FAILED;
208 base::SharedMemoryHandle shm_handle = shm->handle();
209 if (shm_id == shm_buffers_.size()) {
210 shm_buffers_.push_back(shm.release());
211 shm_buffer_busy_.push_back(false);
212 } else {
213 // Remove the old buffer. Delete manually since ScopedVector won't delete
214 // the existing element if we just assign over it.
215 delete shm_buffers_[shm_id];
216 shm_buffers_[shm_id] = shm.release();
219 SerializedHandle handle(
220 renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(shm_handle),
221 shm_size);
222 ppapi::host::ReplyMessageContext reply_context =
223 context->MakeReplyMessageContext();
224 reply_context.params.AppendHandle(handle);
225 host()->SendReply(reply_context,
226 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
228 return PP_OK_COMPLETIONPENDING;
231 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
232 ppapi::host::HostMessageContext* context,
233 uint32_t shm_id,
234 uint32_t size,
235 int32_t decode_id) {
236 if (!initialized_)
237 return PP_ERROR_FAILED;
238 DCHECK(decoder_);
239 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
240 if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
241 return PP_ERROR_FAILED;
242 // Reject an attempt to pass a busy buffer to the decoder again.
243 if (shm_buffer_busy_[shm_id])
244 return PP_ERROR_FAILED;
245 // Reject non-unique decode_id values.
246 if (pending_decodes_.find(decode_id) != pending_decodes_.end())
247 return PP_ERROR_FAILED;
249 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
250 return PP_ERROR_FAILED;
252 pending_decodes_.insert(std::make_pair(
253 decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
255 shm_buffer_busy_[shm_id] = true;
256 decoder_->Decode(
257 media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
259 return PP_OK_COMPLETIONPENDING;
262 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
263 ppapi::host::HostMessageContext* context,
264 const PP_Size& size,
265 const std::vector<uint32_t>& texture_ids) {
266 if (!initialized_)
267 return PP_ERROR_FAILED;
268 DCHECK(decoder_);
270 std::vector<media::PictureBuffer> picture_buffers;
271 for (uint32 i = 0; i < texture_ids.size(); i++) {
272 media::PictureBuffer buffer(
273 texture_ids[i], // Use the texture_id to identify the buffer.
274 gfx::Size(size.width, size.height),
275 texture_ids[i]);
276 picture_buffers.push_back(buffer);
278 decoder_->AssignPictureBuffers(picture_buffers);
279 return PP_OK;
282 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
283 ppapi::host::HostMessageContext* context,
284 uint32_t texture_id) {
285 if (!initialized_)
286 return PP_ERROR_FAILED;
287 DCHECK(decoder_);
289 TextureSet::iterator it = pictures_in_use_.find(texture_id);
290 if (it == pictures_in_use_.end())
291 return PP_ERROR_BADARGUMENT;
293 pictures_in_use_.erase(it);
295 TextureSet::iterator dismissed_texture =
296 dismissed_pictures_in_use_.find(texture_id);
297 if (dismissed_texture != dismissed_pictures_in_use_.end()) {
298 // The texture was already dismissed by the decoder. Notify the plugin.
299 host()->SendUnsolicitedReply(
300 pp_resource(),
301 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id));
302 dismissed_pictures_in_use_.erase(dismissed_texture);
303 } else {
304 decoder_->ReusePictureBuffer(texture_id);
307 return PP_OK;
310 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
311 ppapi::host::HostMessageContext* context) {
312 if (!initialized_)
313 return PP_ERROR_FAILED;
314 DCHECK(decoder_);
315 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
316 return PP_ERROR_FAILED;
318 flush_reply_context_ = context->MakeReplyMessageContext();
319 decoder_->Flush();
321 return PP_OK_COMPLETIONPENDING;
324 int32_t PepperVideoDecoderHost::OnHostMsgReset(
325 ppapi::host::HostMessageContext* context) {
326 if (!initialized_)
327 return PP_ERROR_FAILED;
328 DCHECK(decoder_);
329 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
330 return PP_ERROR_FAILED;
332 reset_reply_context_ = context->MakeReplyMessageContext();
333 decoder_->Reset();
335 return PP_OK_COMPLETIONPENDING;
338 void PepperVideoDecoderHost::ProvidePictureBuffers(
339 uint32 requested_num_of_buffers,
340 const gfx::Size& dimensions,
341 uint32 texture_target) {
342 RequestTextures(std::max(min_picture_count_, requested_num_of_buffers),
343 dimensions,
344 texture_target,
345 std::vector<gpu::Mailbox>());
348 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
349 // Don't bother validating the visible rect, since the plugin process is less
350 // trusted than the gpu process.
351 DCHECK(pictures_in_use_.find(picture.picture_buffer_id()) ==
352 pictures_in_use_.end());
353 pictures_in_use_.insert(picture.picture_buffer_id());
355 PP_Rect visible_rect = PP_FromGfxRect(picture.visible_rect());
356 host()->SendUnsolicitedReply(pp_resource(),
357 PpapiPluginMsg_VideoDecoder_PictureReady(
358 picture.bitstream_buffer_id(),
359 picture.picture_buffer_id(), visible_rect));
362 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
363 // If the texture is still used by the plugin keep it until the plugin
364 // recycles it.
365 if (pictures_in_use_.find(picture_buffer_id) != pictures_in_use_.end()) {
366 dismissed_pictures_in_use_.insert(picture_buffer_id);
367 return;
370 host()->SendUnsolicitedReply(
371 pp_resource(),
372 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
375 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
376 int32 bitstream_buffer_id) {
377 PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
378 if (it == pending_decodes_.end()) {
379 NOTREACHED();
380 return;
382 const PendingDecode& pending_decode = it->second;
383 host()->SendReply(
384 pending_decode.reply_context,
385 PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
386 shm_buffer_busy_[pending_decode.shm_id] = false;
387 pending_decodes_.erase(it);
390 void PepperVideoDecoderHost::NotifyFlushDone() {
391 DCHECK(pending_decodes_.empty());
392 host()->SendReply(flush_reply_context_,
393 PpapiPluginMsg_VideoDecoder_FlushReply());
394 flush_reply_context_ = ppapi::host::ReplyMessageContext();
397 void PepperVideoDecoderHost::NotifyResetDone() {
398 DCHECK(pending_decodes_.empty());
399 host()->SendReply(reset_reply_context_,
400 PpapiPluginMsg_VideoDecoder_ResetReply());
401 reset_reply_context_ = ppapi::host::ReplyMessageContext();
404 void PepperVideoDecoderHost::NotifyError(
405 media::VideoDecodeAccelerator::Error error) {
406 int32_t pp_error = PP_ERROR_FAILED;
407 switch (error) {
408 case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
409 pp_error = PP_ERROR_MALFORMED_INPUT;
410 break;
411 case media::VideoDecodeAccelerator::ILLEGAL_STATE:
412 case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
413 case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
414 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
415 pp_error = PP_ERROR_RESOURCE_FAILED;
416 break;
417 // No default case, to catch unhandled enum values.
419 host()->SendUnsolicitedReply(
420 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
423 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
424 PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
425 DCHECK(it != pending_decodes_.end());
426 uint32_t shm_id = it->second.shm_id;
427 return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
430 void PepperVideoDecoderHost::RequestTextures(
431 uint32 requested_num_of_buffers,
432 const gfx::Size& dimensions,
433 uint32 texture_target,
434 const std::vector<gpu::Mailbox>& mailboxes) {
435 host()->SendUnsolicitedReply(
436 pp_resource(),
437 PpapiPluginMsg_VideoDecoder_RequestTextures(
438 requested_num_of_buffers,
439 PP_MakeSize(dimensions.width(), dimensions.height()),
440 texture_target,
441 mailboxes));
444 } // namespace content