• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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