blob: fdb5cc33485025b55efd3cdbd16b999fb5a77f57 [file] [log] [blame]
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +01001// 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"
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +010013#include "content/renderer/pepper/video_decoder_shim.h"
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +010014#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
24using ppapi::proxy::SerializedHandle;
25using ppapi::thunk::EnterResourceNoLock;
26using ppapi::thunk::PPB_Graphics3D_API;
27
28namespace content {
29
30namespace {
31
32media::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;
Torne (Richard Coles)6e8cce62014-08-19 13:00:08 +010056 case PP_VIDEOPROFILE_VP8_ANY:
57 return media::VP8PROFILE_ANY;
58 case PP_VIDEOPROFILE_VP9_ANY:
59 return media::VP9PROFILE_ANY;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +010060 // No default case, to catch unhandled PP_VideoProfile values.
61 }
62
63 return media::VIDEO_CODEC_PROFILE_UNKNOWN;
64}
65
66} // namespace
67
68PepperVideoDecoderHost::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
74PepperVideoDecoderHost::PendingDecode::~PendingDecode() {
75}
76
77PepperVideoDecoderHost::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
85PepperVideoDecoderHost::~PepperVideoDecoderHost() {
86}
87
88int32_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
110int32_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;
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100122 PPB_Graphics3D_Impl* graphics3d =
123 static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100124
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100125 int command_buffer_route_id = graphics3d->GetCommandBufferRouteId();
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100126 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.
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100133 GpuChannelHost* channel = graphics3d->channel();
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100134 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
Ben Murdoch116680a2014-07-20 18:25:52 -0700142#if defined(OS_ANDROID)
143 return PP_ERROR_NOTSUPPORTED;
144#else
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100145 if (!allow_software_fallback)
146 return PP_ERROR_NOTSUPPORTED;
147
148 decoder_.reset(new VideoDecoderShim(this));
149 initialize_reply_context_ = context->MakeReplyMessageContext();
150 decoder_->Initialize(media_profile, this);
151
152 return PP_OK_COMPLETIONPENDING;
Ben Murdoch116680a2014-07-20 18:25:52 -0700153#endif
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100154}
155
156int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
157 ppapi::host::HostMessageContext* context,
158 uint32_t shm_id,
159 uint32_t shm_size) {
160 if (!initialized_)
161 return PP_ERROR_FAILED;
162
163 // Make the buffers larger since we hope to reuse them.
164 shm_size = std::max(
165 shm_size,
166 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
167 if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
168 return PP_ERROR_FAILED;
169
170 if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
171 return PP_ERROR_FAILED;
172 // The shm_id must be inside or at the end of shm_buffers_.
173 if (shm_id > shm_buffers_.size())
174 return PP_ERROR_FAILED;
175 // Reject an attempt to reallocate a busy shm buffer.
176 if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id])
177 return PP_ERROR_FAILED;
178
179 content::RenderThread* render_thread = content::RenderThread::Get();
180 scoped_ptr<base::SharedMemory> shm(
181 render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass());
182 if (!shm || !shm->Map(shm_size))
183 return PP_ERROR_FAILED;
184
185 base::SharedMemoryHandle shm_handle = shm->handle();
186 if (shm_id == shm_buffers_.size()) {
187 shm_buffers_.push_back(shm.release());
188 shm_buffer_busy_.push_back(false);
189 } else {
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100190 // Remove the old buffer. Delete manually since ScopedVector won't delete
191 // the existing element if we just assign over it.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100192 delete shm_buffers_[shm_id];
193 shm_buffers_[shm_id] = shm.release();
194 }
195
196#if defined(OS_WIN)
197 base::PlatformFile platform_file = shm_handle;
198#elif defined(OS_POSIX)
199 base::PlatformFile platform_file = shm_handle.fd;
200#else
201#error Not implemented.
202#endif
203 SerializedHandle handle(
204 renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false),
205 shm_size);
206 ppapi::host::ReplyMessageContext reply_context =
207 context->MakeReplyMessageContext();
208 reply_context.params.AppendHandle(handle);
209 host()->SendReply(reply_context,
210 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
211
212 return PP_OK_COMPLETIONPENDING;
213}
214
215int32_t PepperVideoDecoderHost::OnHostMsgDecode(
216 ppapi::host::HostMessageContext* context,
217 uint32_t shm_id,
218 uint32_t size,
219 int32_t decode_id) {
220 if (!initialized_)
221 return PP_ERROR_FAILED;
222 DCHECK(decoder_);
223 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
224 if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
225 return PP_ERROR_FAILED;
226 // Reject an attempt to pass a busy buffer to the decoder again.
227 if (shm_buffer_busy_[shm_id])
228 return PP_ERROR_FAILED;
229 // Reject non-unique decode_id values.
230 if (pending_decodes_.find(decode_id) != pending_decodes_.end())
231 return PP_ERROR_FAILED;
232
233 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
234 return PP_ERROR_FAILED;
235
236 pending_decodes_.insert(std::make_pair(
237 decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext())));
238
239 shm_buffer_busy_[shm_id] = true;
240 decoder_->Decode(
241 media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size));
242
243 return PP_OK_COMPLETIONPENDING;
244}
245
246int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
247 ppapi::host::HostMessageContext* context,
248 const PP_Size& size,
249 const std::vector<uint32_t>& texture_ids) {
250 if (!initialized_)
251 return PP_ERROR_FAILED;
252 DCHECK(decoder_);
253
254 std::vector<media::PictureBuffer> picture_buffers;
255 for (uint32 i = 0; i < texture_ids.size(); i++) {
256 media::PictureBuffer buffer(
257 texture_ids[i], // Use the texture_id to identify the buffer.
258 gfx::Size(size.width, size.height),
259 texture_ids[i]);
260 picture_buffers.push_back(buffer);
261 }
262 decoder_->AssignPictureBuffers(picture_buffers);
263 return PP_OK;
264}
265
266int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
267 ppapi::host::HostMessageContext* context,
268 uint32_t texture_id) {
269 if (!initialized_)
270 return PP_ERROR_FAILED;
271 DCHECK(decoder_);
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100272
273 decoder_->ReusePictureBuffer(texture_id);
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100274 return PP_OK;
275}
276
277int32_t PepperVideoDecoderHost::OnHostMsgFlush(
278 ppapi::host::HostMessageContext* context) {
279 if (!initialized_)
280 return PP_ERROR_FAILED;
281 DCHECK(decoder_);
282 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
283 return PP_ERROR_FAILED;
284
285 flush_reply_context_ = context->MakeReplyMessageContext();
286 decoder_->Flush();
287
288 return PP_OK_COMPLETIONPENDING;
289}
290
291int32_t PepperVideoDecoderHost::OnHostMsgReset(
292 ppapi::host::HostMessageContext* context) {
293 if (!initialized_)
294 return PP_ERROR_FAILED;
295 DCHECK(decoder_);
296 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
297 return PP_ERROR_FAILED;
298
299 reset_reply_context_ = context->MakeReplyMessageContext();
300 decoder_->Reset();
301
302 return PP_OK_COMPLETIONPENDING;
303}
304
305void PepperVideoDecoderHost::ProvidePictureBuffers(
306 uint32 requested_num_of_buffers,
307 const gfx::Size& dimensions,
308 uint32 texture_target) {
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100309 RequestTextures(requested_num_of_buffers,
310 dimensions,
311 texture_target,
312 std::vector<gpu::Mailbox>());
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100313}
314
315void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100316 host()->SendUnsolicitedReply(
317 pp_resource(),
318 PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(),
319 picture.picture_buffer_id()));
320}
321
322void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) {
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100323 host()->SendUnsolicitedReply(
324 pp_resource(),
325 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
326}
327
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100328void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
329 int32 bitstream_buffer_id) {
330 PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id);
331 if (it == pending_decodes_.end()) {
332 NOTREACHED();
333 return;
334 }
335 const PendingDecode& pending_decode = it->second;
336 host()->SendReply(
337 pending_decode.reply_context,
338 PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id));
339 shm_buffer_busy_[pending_decode.shm_id] = false;
340 pending_decodes_.erase(it);
341}
342
343void PepperVideoDecoderHost::NotifyFlushDone() {
344 DCHECK(pending_decodes_.empty());
345 host()->SendReply(flush_reply_context_,
346 PpapiPluginMsg_VideoDecoder_FlushReply());
347 flush_reply_context_ = ppapi::host::ReplyMessageContext();
348}
349
350void PepperVideoDecoderHost::NotifyResetDone() {
351 DCHECK(pending_decodes_.empty());
352 host()->SendReply(reset_reply_context_,
353 PpapiPluginMsg_VideoDecoder_ResetReply());
354 reset_reply_context_ = ppapi::host::ReplyMessageContext();
355}
356
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100357void PepperVideoDecoderHost::NotifyError(
358 media::VideoDecodeAccelerator::Error error) {
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100359 int32_t pp_error = PP_ERROR_FAILED;
360 switch (error) {
361 case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
362 pp_error = PP_ERROR_MALFORMED_INPUT;
363 break;
364 case media::VideoDecodeAccelerator::ILLEGAL_STATE:
365 case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
366 case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
367 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM:
368 pp_error = PP_ERROR_RESOURCE_FAILED;
369 break;
370 // No default case, to catch unhandled enum values.
371 }
372 host()->SendUnsolicitedReply(
373 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
374}
375
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100376void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) {
377 if (!initialized_) {
378 if (result == PP_OK)
379 initialized_ = true;
380 initialize_reply_context_.params.set_result(result);
381 host()->SendReply(initialize_reply_context_,
382 PpapiPluginMsg_VideoDecoder_InitializeReply());
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100383 }
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100384}
385
Torne (Richard Coles)f8ee7882014-06-20 14:52:04 +0100386const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
387 PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id);
388 DCHECK(it != pending_decodes_.end());
389 uint32_t shm_id = it->second.shm_id;
390 return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory());
391}
392
393void PepperVideoDecoderHost::RequestTextures(
394 uint32 requested_num_of_buffers,
395 const gfx::Size& dimensions,
396 uint32 texture_target,
397 const std::vector<gpu::Mailbox>& mailboxes) {
398 host()->SendUnsolicitedReply(
399 pp_resource(),
400 PpapiPluginMsg_VideoDecoder_RequestTextures(
401 requested_num_of_buffers,
402 PP_MakeSize(dimensions.width(), dimensions.height()),
403 texture_target,
404 mailboxes));
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100405}
406
407} // namespace content