Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / pepper_video_decoder_host.cc
blob4aecd5322eba2ab6c09e5441bde753bfdfc5fe2e
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/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;
30 namespace content {
32 namespace {
34 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
35 switch (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;
68 } // namespace
70 PepperVideoDecoderHost::PendingDecode::PendingDecode(
71 uint32_t shm_id,
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,
80 PP_Instance instance,
81 PP_Resource resource)
82 : ResourceHost(host->GetPpapiHost(), instance, resource),
83 renderer_ppapi_host_(host),
84 initialized_(false) {
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,
95 OnHostMsgInitialize)
96 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
97 OnHostMsgGetShm)
98 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
99 OnHostMsgDecode)
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,
105 OnHostMsgFlush)
106 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
107 OnHostMsgReset)
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) {
117 if (initialized_)
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();
137 DCHECK(channel);
138 decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
139 if (decoder_ && decoder_->Initialize(media_profile, this)) {
140 initialized_ = true;
141 return PP_OK;
143 decoder_.reset();
144 if (acceleration == PP_HARDWAREACCELERATION_ONLY)
145 return PP_ERROR_NOTSUPPORTED;
148 #if defined(OS_ANDROID)
149 return PP_ERROR_NOTSUPPORTED;
150 #else
151 decoder_.reset(new VideoDecoderShim(this));
152 initialize_reply_context_ = context->MakeReplyMessageContext();
153 decoder_->Initialize(media_profile, this);
155 return PP_OK_COMPLETIONPENDING;
156 #endif
159 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
160 ppapi::host::HostMessageContext* context,
161 uint32_t shm_id,
162 uint32_t shm_size) {
163 if (!initialized_)
164 return PP_ERROR_FAILED;
166 // Make the buffers larger since we hope to reuse them.
167 shm_size = std::max(
168 shm_size,
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);
192 } else {
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),
203 shm_size);
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,
215 uint32_t shm_id,
216 uint32_t size,
217 int32_t decode_id) {
218 if (!initialized_)
219 return PP_ERROR_FAILED;
220 DCHECK(decoder_);
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;
238 decoder_->Decode(
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,
246 const PP_Size& size,
247 const std::vector<uint32_t>& texture_ids) {
248 if (!initialized_)
249 return PP_ERROR_FAILED;
250 DCHECK(decoder_);
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),
257 texture_ids[i]);
258 picture_buffers.push_back(buffer);
260 decoder_->AssignPictureBuffers(picture_buffers);
261 return PP_OK;
264 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
265 ppapi::host::HostMessageContext* context,
266 uint32_t texture_id) {
267 if (!initialized_)
268 return PP_ERROR_FAILED;
269 DCHECK(decoder_);
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(
282 pp_resource(),
283 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id));
284 dismissed_pictures_in_use_.erase(dismissed_texture);
285 } else {
286 decoder_->ReusePictureBuffer(texture_id);
289 return PP_OK;
292 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
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;
300 flush_reply_context_ = context->MakeReplyMessageContext();
301 decoder_->Flush();
303 return PP_OK_COMPLETIONPENDING;
306 int32_t PepperVideoDecoderHost::OnHostMsgReset(
307 ppapi::host::HostMessageContext* context) {
308 if (!initialized_)
309 return PP_ERROR_FAILED;
310 DCHECK(decoder_);
311 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
312 return PP_ERROR_FAILED;
314 reset_reply_context_ = context->MakeReplyMessageContext();
315 decoder_->Reset();
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,
325 dimensions,
326 texture_target,
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
346 // recycles it.
347 if (pictures_in_use_.find(picture_buffer_id) != pictures_in_use_.end()) {
348 dismissed_pictures_in_use_.insert(picture_buffer_id);
349 return;
352 host()->SendUnsolicitedReply(
353 pp_resource(),
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()) {
361 NOTREACHED();
362 return;
364 const PendingDecode& pending_decode = it->second;
365 host()->SendReply(
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;
389 switch (error) {
390 case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
391 pp_error = PP_ERROR_MALFORMED_INPUT;
392 break;
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;
398 break;
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) {
406 if (!initialized_) {
407 if (result == PP_OK)
408 initialized_ = true;
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(
428 pp_resource(),
429 PpapiPluginMsg_VideoDecoder_RequestTextures(
430 requested_num_of_buffers,
431 PP_MakeSize(dimensions.width(), dimensions.height()),
432 texture_target,
433 mailboxes));
436 } // namespace content