• 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_VP8MAIN:
57       return media::VP8PROFILE_MAIN;
58     case PP_VIDEOPROFILE_VP9MAIN:
59       return media::VP9PROFILE_MAIN;
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,bool allow_software_fallback)110 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
111     ppapi::host::HostMessageContext* context,
112     const ppapi::HostResource& graphics_context,
113     PP_VideoProfile profile,
114     bool allow_software_fallback) {
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   // This is not synchronous, but subsequent IPC messages will be buffered, so
132   // it is okay to immediately send IPC messages through the returned channel.
133   GpuChannelHost* channel = graphics3d->channel();
134   DCHECK(channel);
135   decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
136   if (decoder_ && decoder_->Initialize(media_profile, this)) {
137     initialized_ = true;
138     return PP_OK;
139   }
140   decoder_.reset();
141 
142   if (!allow_software_fallback)
143     return PP_ERROR_NOTSUPPORTED;
144 
145   decoder_.reset(new VideoDecoderShim(this));
146   initialize_reply_context_ = context->MakeReplyMessageContext();
147   decoder_->Initialize(media_profile, this);
148 
149   return PP_OK_COMPLETIONPENDING;
150 }
151 
OnHostMsgGetShm(ppapi::host::HostMessageContext * context,uint32_t shm_id,uint32_t shm_size)152 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
153     ppapi::host::HostMessageContext* context,
154     uint32_t shm_id,
155     uint32_t shm_size) {
156   if (!initialized_)
157     return PP_ERROR_FAILED;
158 
159   // Make the buffers larger since we hope to reuse them.
160   shm_size = std::max(
161       shm_size,
162       static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
163   if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
164     return PP_ERROR_FAILED;
165 
166   if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
167     return PP_ERROR_FAILED;
168   // The shm_id must be inside or at the end of shm_buffers_.
169   if (shm_id > shm_buffers_.size())
170     return PP_ERROR_FAILED;
171   // Reject an attempt to reallocate a busy shm buffer.
172   if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
173     return PP_ERROR_FAILED;
174 
175   content::RenderThread* render_thread = content::RenderThread::Get();
176   scoped_ptr<base::SharedMemory> shm(
177       render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
178   if (!shm || !shm->Map(shm_size))
179     return PP_ERROR_FAILED;
180 
181   base::SharedMemoryHandle shm_handle = shm->handle();
182   if (shm_id == shm_buffers_.size()) {
183     shm_buffers_.push_back(shm.release());
184     shm_buffer_busy_.push_back(false);
185   } else {
186     // Remove the old buffer. Delete manually since ScopedVector won't delete
187     // the existing element if we just assign over it.
188     delete shm_buffers_[shm_id];
189     shm_buffers_[shm_id] = shm.release();
190   }
191 
192 #if defined(OS_WIN)
193   base::PlatformFile platform_file = shm_handle;
194 #elif defined(OS_POSIX)
195   base::PlatformFile platform_file = shm_handle.fd;
196 #else
197 #error Not implemented.
198 #endif
199   SerializedHandle handle(
200       renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false),
201       shm_size);
202   ppapi::host::ReplyMessageContext reply_context =
203       context->MakeReplyMessageContext();
204   reply_context.params.AppendHandle(handle);
205   host()->SendReply(reply_context,
206                     PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
207 
208   return PP_OK_COMPLETIONPENDING;
209 }
210 
OnHostMsgDecode(ppapi::host::HostMessageContext * context,uint32_t shm_id,uint32_t size,int32_t decode_id)211 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
212     ppapi::host::HostMessageContext* context,
213     uint32_t shm_id,
214     uint32_t size,
215     int32_t decode_id) {
216   if (!initialized_)
217     return PP_ERROR_FAILED;
218   DCHECK(decoder_);
219   // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
220   if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
221     return PP_ERROR_FAILED;
222   // Reject an attempt to pass a busy buffer to the decoder again.
223   if (shm_buffer_busy_[shm_id])
224     return PP_ERROR_FAILED;
225   // Reject non-unique decode_id values.
226   if (pending_decodes_.find(decode_id) != pending_decodes_.end())
227     return PP_ERROR_FAILED;
228 
229   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
230     return PP_ERROR_FAILED;
231 
232   pending_decodes_.insert(std::make_pair(
233       decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
234 
235   shm_buffer_busy_[shm_id] = true;
236   decoder_->Decode(
237       media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
238 
239   return PP_OK_COMPLETIONPENDING;
240 }
241 
OnHostMsgAssignTextures(ppapi::host::HostMessageContext * context,const PP_Size & size,const std::vector<uint32_t> & texture_ids)242 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
243     ppapi::host::HostMessageContext* context,
244     const PP_Size& size,
245     const std::vector<uint32_t>& texture_ids) {
246   if (!initialized_)
247     return PP_ERROR_FAILED;
248   DCHECK(decoder_);
249 
250   std::vector<media::PictureBuffer> picture_buffers;
251   for (uint32 i = 0; i < texture_ids.size(); i++) {
252     media::PictureBuffer buffer(
253         texture_ids[i],  // Use the texture_id to identify the buffer.
254         gfx::Size(size.width, size.height),
255         texture_ids[i]);
256     picture_buffers.push_back(buffer);
257   }
258   decoder_->AssignPictureBuffers(picture_buffers);
259   return PP_OK;
260 }
261 
OnHostMsgRecyclePicture(ppapi::host::HostMessageContext * context,uint32_t texture_id)262 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
263     ppapi::host::HostMessageContext* context,
264     uint32_t texture_id) {
265   if (!initialized_)
266     return PP_ERROR_FAILED;
267   DCHECK(decoder_);
268 
269   decoder_->ReusePictureBuffer(texture_id);
270   return PP_OK;
271 }
272 
OnHostMsgFlush(ppapi::host::HostMessageContext * context)273 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
274     ppapi::host::HostMessageContext* context) {
275   if (!initialized_)
276     return PP_ERROR_FAILED;
277   DCHECK(decoder_);
278   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
279     return PP_ERROR_FAILED;
280 
281   flush_reply_context_ = context->MakeReplyMessageContext();
282   decoder_->Flush();
283 
284   return PP_OK_COMPLETIONPENDING;
285 }
286 
OnHostMsgReset(ppapi::host::HostMessageContext * context)287 int32_t PepperVideoDecoderHost::OnHostMsgReset(
288     ppapi::host::HostMessageContext* context) {
289   if (!initialized_)
290     return PP_ERROR_FAILED;
291   DCHECK(decoder_);
292   if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
293     return PP_ERROR_FAILED;
294 
295   reset_reply_context_ = context->MakeReplyMessageContext();
296   decoder_->Reset();
297 
298   return PP_OK_COMPLETIONPENDING;
299 }
300 
ProvidePictureBuffers(uint32 requested_num_of_buffers,const gfx::Size & dimensions,uint32 texture_target)301 void PepperVideoDecoderHost::ProvidePictureBuffers(
302     uint32 requested_num_of_buffers,
303     const gfx::Size& dimensions,
304     uint32 texture_target) {
305   RequestTextures(requested_num_of_buffers,
306                   dimensions,
307                   texture_target,
308                   std::vector<gpu::Mailbox>());
309 }
310 
PictureReady(const media::Picture & picture)311 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
312   host()->SendUnsolicitedReply(
313       pp_resource(),
314       PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
315                                                picture.picture_buffer_id()));
316 }
317 
DismissPictureBuffer(int32 picture_buffer_id)318 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
319   host()->SendUnsolicitedReply(
320       pp_resource(),
321       PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
322 }
323 
NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id)324 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
325     int32 bitstream_buffer_id) {
326   PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
327   if (it == pending_decodes_.end()) {
328     NOTREACHED();
329     return;
330   }
331   const PendingDecode& pending_decode = it->second;
332   host()->SendReply(
333       pending_decode.reply_context,
334       PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
335   shm_buffer_busy_[pending_decode.shm_id] = false;
336   pending_decodes_.erase(it);
337 }
338 
NotifyFlushDone()339 void PepperVideoDecoderHost::NotifyFlushDone() {
340   DCHECK(pending_decodes_.empty());
341   host()->SendReply(flush_reply_context_,
342                     PpapiPluginMsg_VideoDecoder_FlushReply());
343   flush_reply_context_ = ppapi::host::ReplyMessageContext();
344 }
345 
NotifyResetDone()346 void PepperVideoDecoderHost::NotifyResetDone() {
347   DCHECK(pending_decodes_.empty());
348   host()->SendReply(reset_reply_context_,
349                     PpapiPluginMsg_VideoDecoder_ResetReply());
350   reset_reply_context_ = ppapi::host::ReplyMessageContext();
351 }
352 
NotifyError(media::VideoDecodeAccelerator::Error error)353 void PepperVideoDecoderHost::NotifyError(
354     media::VideoDecodeAccelerator::Error error) {
355   int32_t pp_error = PP_ERROR_FAILED;
356   switch (error) {
357     case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
358       pp_error = PP_ERROR_MALFORMED_INPUT;
359       break;
360     case media::VideoDecodeAccelerator::ILLEGAL_STATE:
361     case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
362     case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
363     case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
364       pp_error = PP_ERROR_RESOURCE_FAILED;
365       break;
366     // No default case, to catch unhandled enum values.
367   }
368   host()->SendUnsolicitedReply(
369       pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
370 }
371 
OnInitializeComplete(int32_t result)372 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
373   if (!initialized_) {
374     if (result == PP_OK)
375       initialized_ = true;
376     initialize_reply_context_.params.set_result(result);
377     host()->SendReply(initialize_reply_context_,
378                       PpapiPluginMsg_VideoDecoder_InitializeReply());
379   }
380 }
381 
DecodeIdToAddress(uint32_t decode_id)382 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
383   PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
384   DCHECK(it != pending_decodes_.end());
385   uint32_t shm_id = it->second.shm_id;
386   return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
387 }
388 
RequestTextures(uint32 requested_num_of_buffers,const gfx::Size & dimensions,uint32 texture_target,const std::vector<gpu::Mailbox> & mailboxes)389 void PepperVideoDecoderHost::RequestTextures(
390     uint32 requested_num_of_buffers,
391     const gfx::Size& dimensions,
392     uint32 texture_target,
393     const std::vector<gpu::Mailbox>& mailboxes) {
394   host()->SendUnsolicitedReply(
395       pp_resource(),
396       PpapiPluginMsg_VideoDecoder_RequestTextures(
397           requested_num_of_buffers,
398           PP_MakeSize(dimensions.width(), dimensions.height()),
399           texture_target,
400           mailboxes));
401 }
402 
403 }  // namespace content
404