1 // Copyright 2014 The Chromium Authors
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"
10 #include "base/memory/ptr_util.h"
11 #include "base/memory/unsafe_shared_memory_region.h"
12 #include "base/ranges/algorithm.h"
13 #include "build/build_config.h"
14 #include "content/common/pepper_file_util.h"
15 #include "content/public/common/content_client.h"
16 #include "content/public/renderer/content_renderer_client.h"
17 #include "content/public/renderer/ppapi_gfx_conversion.h"
18 #include "content/public/renderer/render_thread.h"
19 #include "content/public/renderer/renderer_ppapi_host.h"
20 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
21 #include "content/renderer/pepper/video_decoder_shim.h"
22 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
23 #include "media/base/limits.h"
24 #include "media/base/media_util.h"
25 #include "media/gpu/ipc/client/gpu_video_decode_accelerator_host.h"
26 #include "media/video/video_decode_accelerator.h"
27 #include "ppapi/c/pp_completion_callback.h"
28 #include "ppapi/c/pp_errors.h"
29 #include "ppapi/host/dispatch_host_message.h"
30 #include "ppapi/host/ppapi_host.h"
31 #include "ppapi/proxy/ppapi_messages.h"
32 #include "ppapi/proxy/video_decoder_constants.h"
33 #include "ppapi/thunk/enter.h"
34 #include "ppapi/thunk/ppb_graphics_3d_api.h"
36 using ppapi::proxy::SerializedHandle;
37 using ppapi::thunk::EnterResourceNoLock;
38 using ppapi::thunk::PPB_Graphics3D_API;
44 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) {
46 case PP_VIDEOPROFILE_H264BASELINE:
47 return media::H264PROFILE_BASELINE;
48 case PP_VIDEOPROFILE_H264MAIN:
49 return media::H264PROFILE_MAIN;
50 case PP_VIDEOPROFILE_H264EXTENDED:
51 return media::H264PROFILE_EXTENDED;
52 case PP_VIDEOPROFILE_H264HIGH:
53 return media::H264PROFILE_HIGH;
54 case PP_VIDEOPROFILE_H264HIGH10PROFILE:
55 return media::H264PROFILE_HIGH10PROFILE;
56 case PP_VIDEOPROFILE_H264HIGH422PROFILE:
57 return media::H264PROFILE_HIGH422PROFILE;
58 case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE:
59 return media::H264PROFILE_HIGH444PREDICTIVEPROFILE;
60 case PP_VIDEOPROFILE_H264SCALABLEBASELINE:
61 return media::H264PROFILE_SCALABLEBASELINE;
62 case PP_VIDEOPROFILE_H264SCALABLEHIGH:
63 return media::H264PROFILE_SCALABLEHIGH;
64 case PP_VIDEOPROFILE_H264STEREOHIGH:
65 return media::H264PROFILE_STEREOHIGH;
66 case PP_VIDEOPROFILE_H264MULTIVIEWHIGH:
67 return media::H264PROFILE_MULTIVIEWHIGH;
68 case PP_VIDEOPROFILE_VP8_ANY:
69 return media::VP8PROFILE_ANY;
70 case PP_VIDEOPROFILE_VP9_ANY:
71 return media::VP9PROFILE_PROFILE0;
72 // No default case, to catch unhandled PP_VideoProfile values.
75 return media::VIDEO_CODEC_PROFILE_UNKNOWN;
80 PepperVideoDecoderHost::PendingDecode::PendingDecode(
84 const ppapi::host::ReplyMessageContext& reply_context)
85 : decode_id(decode_id),
88 reply_context(reply_context) {}
90 PepperVideoDecoderHost::PendingDecode::~PendingDecode() {}
92 PepperVideoDecoderHost::MappedBuffer::MappedBuffer(
93 base::UnsafeSharedMemoryRegion region,
94 base::WritableSharedMemoryMapping mapping)
95 : region(std::move(region)), mapping(std::move(mapping)) {}
97 PepperVideoDecoderHost::MappedBuffer::~MappedBuffer() {}
99 PepperVideoDecoderHost::MappedBuffer::MappedBuffer(MappedBuffer&&) = default;
100 PepperVideoDecoderHost::MappedBuffer& PepperVideoDecoderHost::MappedBuffer::
101 operator=(MappedBuffer&&) = default;
103 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host,
104 PP_Instance instance,
105 PP_Resource resource)
106 : ResourceHost(host->GetPpapiHost(), instance, resource),
107 renderer_ppapi_host_(host) {}
109 PepperVideoDecoderHost::~PepperVideoDecoderHost() {}
111 int32_t PepperVideoDecoderHost::OnResourceMessageReceived(
112 const IPC::Message& msg,
113 ppapi::host::HostMessageContext* context) {
114 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg)
115 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize,
117 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm,
119 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode,
121 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures,
122 OnHostMsgAssignTextures)
123 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture,
124 OnHostMsgRecyclePicture)
125 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush,
127 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset,
129 PPAPI_END_MESSAGE_MAP()
130 return PP_ERROR_FAILED;
133 int32_t PepperVideoDecoderHost::OnHostMsgInitialize(
134 ppapi::host::HostMessageContext* context,
135 const ppapi::HostResource& graphics_context,
136 PP_VideoProfile profile,
137 PP_HardwareAcceleration acceleration,
138 uint32_t min_picture_count) {
140 return PP_ERROR_FAILED;
141 if (min_picture_count > ppapi::proxy::kMaximumPictureCount)
142 return PP_ERROR_BADARGUMENT;
144 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(
145 graphics_context.host_resource(), true);
146 if (enter_graphics.failed())
147 return PP_ERROR_FAILED;
148 PPB_Graphics3D_Impl* graphics3d =
149 static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object());
151 gpu::CommandBufferProxyImpl* command_buffer =
152 graphics3d->GetCommandBufferProxy();
154 return PP_ERROR_FAILED;
156 profile_ = PepperToMediaVideoProfile(profile);
157 software_fallback_allowed_ = (acceleration != PP_HARDWAREACCELERATION_ONLY);
159 min_picture_count_ = min_picture_count;
161 if (acceleration != PP_HARDWAREACCELERATION_NONE) {
162 // This is not synchronous, but subsequent IPC messages will be buffered, so
163 // it is okay to immediately send IPC messages.
164 if (command_buffer->channel()) {
165 decoder_ = base::WrapUnique<media::VideoDecodeAccelerator>(
166 new media::GpuVideoDecodeAcceleratorHost(command_buffer));
167 media::VideoDecodeAccelerator::Config vda_config(profile_);
168 vda_config.supported_output_formats.assign(
169 {media::PIXEL_FORMAT_XRGB, media::PIXEL_FORMAT_ARGB});
170 if (decoder_->Initialize(vda_config, this)) {
176 if (acceleration == PP_HARDWAREACCELERATION_ONLY)
177 return PP_ERROR_NOTSUPPORTED;
180 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_TIZEN)
181 return PP_ERROR_NOTSUPPORTED;
183 if (!TryFallbackToSoftwareDecoder())
184 return PP_ERROR_FAILED;
191 int32_t PepperVideoDecoderHost::OnHostMsgGetShm(
192 ppapi::host::HostMessageContext* context,
196 return PP_ERROR_FAILED;
198 // Make the buffers larger since we hope to reuse them.
201 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize));
202 if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize)
203 return PP_ERROR_FAILED;
205 if (shm_id >= ppapi::proxy::kMaximumPendingDecodes)
206 return PP_ERROR_FAILED;
207 // The shm_id must be inside or at the end of shm_buffers_.
208 if (shm_id > shm_buffers_.size())
209 return PP_ERROR_FAILED;
210 // Reject an attempt to reallocate a busy shm buffer.
211 if (shm_id < shm_buffers_.size() && shm_buffers_[shm_id].busy)
212 return PP_ERROR_FAILED;
214 auto shm = base::UnsafeSharedMemoryRegion::Create(shm_size);
215 auto mapping = shm.Map();
216 if (!shm.IsValid() || !mapping.IsValid())
217 return PP_ERROR_FAILED;
219 SerializedHandle handle(
220 base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
221 renderer_ppapi_host_->ShareUnsafeSharedMemoryRegionWithRemote(shm)));
222 if (shm_id == shm_buffers_.size()) {
223 shm_buffers_.emplace_back(std::move(shm), std::move(mapping));
225 // Note by the check above this buffer cannot be busy.
226 shm_buffers_[shm_id] = MappedBuffer(std::move(shm), std::move(mapping));
229 ppapi::host::ReplyMessageContext reply_context =
230 context->MakeReplyMessageContext();
231 reply_context.params.AppendHandle(std::move(handle));
232 host()->SendReply(reply_context,
233 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size));
235 return PP_OK_COMPLETIONPENDING;
238 int32_t PepperVideoDecoderHost::OnHostMsgDecode(
239 ppapi::host::HostMessageContext* context,
244 return PP_ERROR_FAILED;
246 // |shm_id| is just an index into shm_buffers_. Make sure it's in range.
247 if (static_cast<size_t>(shm_id) >= shm_buffers_.size())
248 return PP_ERROR_FAILED;
249 // Reject an attempt to pass a busy buffer to the decoder again.
250 if (shm_buffers_[shm_id].busy)
251 return PP_ERROR_FAILED;
252 // Reject non-unique decode_id values.
253 if (GetPendingDecodeById(decode_id) != pending_decodes_.end())
254 return PP_ERROR_FAILED;
256 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
257 return PP_ERROR_FAILED;
259 pending_decodes_.push_back(PendingDecode(decode_id, shm_id, size,
260 context->MakeReplyMessageContext()));
262 shm_buffers_[shm_id].busy = true;
263 decoder_->Decode(media::BitstreamBuffer(
264 decode_id, shm_buffers_[shm_id].region.Duplicate(), size));
266 return PP_OK_COMPLETIONPENDING;
269 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures(
270 ppapi::host::HostMessageContext* context,
272 const std::vector<uint32_t>& texture_ids,
273 const std::vector<gpu::Mailbox>& mailboxes) {
275 return PP_ERROR_FAILED;
276 if (texture_ids.size() != mailboxes.size()) {
277 LOG(ERROR) << "Texture_ids size was invalid";
278 return PP_ERROR_FAILED;
282 pending_texture_requests_--;
283 DCHECK_GE(pending_texture_requests_, 0);
285 // If |assign_textures_messages_to_dismiss_| is not 0 then decrement it and
286 // dismiss the textures. This is necessary to ensure that after SW decoder
287 // fallback the textures that were requested by the failed HW decoder are not
288 // passed to the SW decoder.
289 if (assign_textures_messages_to_dismiss_ > 0) {
290 assign_textures_messages_to_dismiss_--;
291 PictureBufferMap pictures_pending_dismission;
292 for (auto& texture_id : texture_ids) {
293 host()->SendUnsolicitedReply(
295 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id));
297 picture_buffer_map_.swap(pictures_pending_dismission);
301 // Verify that the new texture IDs are unique and store them in
303 PictureBufferMap new_textures;
304 for (uint32_t i = 0; i < texture_ids.size(); i++) {
305 if (picture_buffer_map_.find(texture_ids[i]) != picture_buffer_map_.end() ||
306 new_textures.find(texture_ids[i]) != new_textures.end()) {
307 // Can't assign the same texture more than once.
308 return PP_ERROR_BADARGUMENT;
311 std::make_pair(texture_ids[i], PictureBufferState::ASSIGNED));
314 picture_buffer_map_.insert(new_textures.begin(), new_textures.end());
316 std::vector<media::PictureBuffer> picture_buffers;
317 for (uint32_t i = 0; i < texture_ids.size(); i++) {
318 media::PictureBuffer::TextureIds ids;
319 ids.push_back(texture_ids[i]);
320 media::PictureBuffer buffer(
321 texture_ids[i], // Use the texture_id to identify the buffer.
322 gfx::Size(size.width, size.height), ids);
323 picture_buffers.push_back(buffer);
325 texture_mailboxes_ = mailboxes;
326 decoder_->AssignPictureBuffers(picture_buffers);
330 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture(
331 ppapi::host::HostMessageContext* context,
332 uint32_t texture_id) {
334 return PP_ERROR_FAILED;
337 auto it = picture_buffer_map_.find(texture_id);
338 if (it == picture_buffer_map_.end())
339 return PP_ERROR_BADARGUMENT;
341 switch (it->second) {
342 case PictureBufferState::ASSIGNED:
343 return PP_ERROR_BADARGUMENT;
345 case PictureBufferState::IN_USE:
346 it->second = PictureBufferState::ASSIGNED;
347 decoder_->ReusePictureBuffer(texture_id);
350 case PictureBufferState::DISMISSED:
351 picture_buffer_map_.erase(it);
352 // The texture was already dismissed by the decoder. Notify the plugin.
353 host()->SendUnsolicitedReply(
355 PpapiPluginMsg_VideoDecoder_DismissPicture(texture_id));
362 int32_t PepperVideoDecoderHost::OnHostMsgFlush(
363 ppapi::host::HostMessageContext* context) {
365 return PP_ERROR_FAILED;
367 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
368 return PP_ERROR_FAILED;
370 flush_reply_context_ = context->MakeReplyMessageContext();
373 return PP_OK_COMPLETIONPENDING;
376 int32_t PepperVideoDecoderHost::OnHostMsgReset(
377 ppapi::host::HostMessageContext* context) {
379 return PP_ERROR_FAILED;
381 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid())
382 return PP_ERROR_FAILED;
384 reset_reply_context_ = context->MakeReplyMessageContext();
387 return PP_OK_COMPLETIONPENDING;
390 void PepperVideoDecoderHost::ProvidePictureBuffers(
391 uint32_t requested_num_of_buffers,
392 media::VideoPixelFormat format,
393 uint32_t textures_per_buffer,
394 const gfx::Size& dimensions,
395 uint32_t texture_target) {
396 DCHECK_EQ(1u, textures_per_buffer);
397 coded_size_ = dimensions;
398 pending_texture_requests_++;
399 host()->SendUnsolicitedReply(
400 pp_resource(), PpapiPluginMsg_VideoDecoder_RequestTextures(
401 std::max(min_picture_count_, requested_num_of_buffers),
402 PP_MakeSize(dimensions.width(), dimensions.height()),
406 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) {
407 auto it = picture_buffer_map_.find(picture.picture_buffer_id());
408 DCHECK(it != picture_buffer_map_.end());
409 // VDA might send the same picture multiple times in VP9 video. However the
410 // Pepper client might not able to handle it. Therefore we just catch it here.
411 // https://crbug.com/755887
412 CHECK(it->second == PictureBufferState::ASSIGNED);
413 it->second = PictureBufferState::IN_USE;
415 // Don't bother validating the visible rect, since the plugin process is less
416 // trusted than the gpu process.
417 PP_Rect visible_rect = PP_FromGfxRect(picture.visible_rect());
418 host()->SendUnsolicitedReply(pp_resource(),
419 PpapiPluginMsg_VideoDecoder_PictureReady(
420 picture.bitstream_buffer_id(),
421 picture.picture_buffer_id(), visible_rect));
424 void PepperVideoDecoderHost::DismissPictureBuffer(int32_t picture_buffer_id) {
425 auto it = picture_buffer_map_.find(picture_buffer_id);
426 DCHECK(it != picture_buffer_map_.end());
428 // If the texture is still used by the plugin keep it until the plugin
430 if (it->second == PictureBufferState::IN_USE) {
431 it->second = PictureBufferState::DISMISSED;
435 DCHECK(it->second == PictureBufferState::ASSIGNED);
436 picture_buffer_map_.erase(it);
437 host()->SendUnsolicitedReply(
439 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id));
442 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer(
443 int32_t bitstream_buffer_id) {
444 auto it = GetPendingDecodeById(bitstream_buffer_id);
445 if (it == pending_decodes_.end()) {
449 host()->SendReply(it->reply_context,
450 PpapiPluginMsg_VideoDecoder_DecodeReply(it->shm_id));
451 shm_buffers_[it->shm_id].busy = false;
452 pending_decodes_.erase(it);
455 void PepperVideoDecoderHost::NotifyFlushDone() {
456 DCHECK(pending_decodes_.empty());
457 host()->SendReply(flush_reply_context_,
458 PpapiPluginMsg_VideoDecoder_FlushReply());
459 flush_reply_context_ = ppapi::host::ReplyMessageContext();
462 void PepperVideoDecoderHost::NotifyResetDone() {
463 DCHECK(pending_decodes_.empty());
464 host()->SendReply(reset_reply_context_,
465 PpapiPluginMsg_VideoDecoder_ResetReply());
466 reset_reply_context_ = ppapi::host::ReplyMessageContext();
469 void PepperVideoDecoderHost::NotifyError(
470 media::VideoDecodeAccelerator::Error error) {
471 int32_t pp_error = PP_ERROR_FAILED;
473 case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
474 pp_error = PP_ERROR_MALFORMED_INPUT;
476 case media::VideoDecodeAccelerator::ILLEGAL_STATE:
477 case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
478 case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
479 pp_error = PP_ERROR_RESOURCE_FAILED;
481 // No default case, to catch unhandled enum values.
484 // Try to initialize software decoder and use it instead.
485 if (!software_fallback_used_ && software_fallback_allowed_) {
487 << "Hardware decoder has returned an error. Trying Software decoder.";
488 if (TryFallbackToSoftwareDecoder())
492 host()->SendUnsolicitedReply(
493 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error));
496 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) {
497 PendingDecodeList::const_iterator it = GetPendingDecodeById(decode_id);
498 DCHECK(it != pending_decodes_.end());
499 uint32_t shm_id = it->shm_id;
500 return static_cast<uint8_t*>(shm_buffers_[shm_id].mapping.memory());
503 bool PepperVideoDecoderHost::TryFallbackToSoftwareDecoder() {
504 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_TIZEN)
507 DCHECK(!software_fallback_used_ && software_fallback_allowed_);
509 uint32_t shim_texture_pool_size = media::limits::kMaxVideoFrames + 1;
510 shim_texture_pool_size = std::max(shim_texture_pool_size,
512 std::unique_ptr<VideoDecoderShim> new_decoder(
513 new VideoDecoderShim(this, shim_texture_pool_size));
514 if (!new_decoder->Initialize(media::VideoDecodeAccelerator::Config(profile_),
519 software_fallback_used_ = true;
520 decoder_.reset(new_decoder.release());
522 // Dismiss all assigned pictures and mark all pictures in use as DISMISSED.
523 PictureBufferMap pictures_pending_dismission;
524 for (auto& picture : picture_buffer_map_) {
525 if (picture.second == PictureBufferState::ASSIGNED) {
526 host()->SendUnsolicitedReply(
528 PpapiPluginMsg_VideoDecoder_DismissPicture(picture.first));
530 pictures_pending_dismission.insert(
531 std::make_pair(picture.first, PictureBufferState::DISMISSED));
534 picture_buffer_map_.swap(pictures_pending_dismission);
536 // Dismiss all outstanding texture requests.
537 DCHECK_EQ(assign_textures_messages_to_dismiss_, 0);
538 assign_textures_messages_to_dismiss_ = pending_texture_requests_;
540 // If there was a pending Reset() it can be finished now.
541 if (reset_reply_context_.is_valid()) {
542 while (!pending_decodes_.empty()) {
543 const PendingDecode& decode = pending_decodes_.front();
544 host()->SendReply(decode.reply_context,
545 PpapiPluginMsg_VideoDecoder_DecodeReply(decode.shm_id));
546 DCHECK(shm_buffers_[decode.shm_id].busy);
547 shm_buffers_[decode.shm_id].busy = false;
548 pending_decodes_.pop_front();
553 // Resubmit all pending decodes.
554 for (const PendingDecode& decode : pending_decodes_) {
555 DCHECK(shm_buffers_[decode.shm_id].busy);
556 decoder_->Decode(media::BitstreamBuffer(
557 decode.decode_id, shm_buffers_[decode.shm_id].region.Duplicate(),
561 // Flush the new decoder if Flush() was pending.
562 if (flush_reply_context_.is_valid())
569 PepperVideoDecoderHost::PendingDecodeList::iterator
570 PepperVideoDecoderHost::GetPendingDecodeById(int32_t decode_id) {
571 return base::ranges::find(pending_decodes_, decode_id,
572 &PendingDecode::decode_id);
575 } // namespace content