// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/pepper/ppb_video_decoder_impl.h" #include #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "content/common/gpu/client/command_buffer_proxy_impl.h" #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h" #include "content/renderer/pepper/host_globals.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/pepper/plugin_module.h" #include "content/renderer/pepper/ppb_buffer_impl.h" #include "content/renderer/pepper/ppb_graphics_3d_impl.h" #include "content/renderer/render_thread_impl.h" #include "media/video/picture.h" #include "media/video/video_decode_accelerator.h" #include "ppapi/c/dev/pp_video_dev.h" #include "ppapi/c/dev/ppb_video_decoder_dev.h" #include "ppapi/c/dev/ppp_video_decoder_dev.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_errors.h" #include "ppapi/shared_impl/resource_tracker.h" #include "ppapi/thunk/enter.h" using ppapi::TrackedCallback; using ppapi::thunk::EnterResourceNoLock; using ppapi::thunk::PPB_Buffer_API; using ppapi::thunk::PPB_Graphics3D_API; namespace { // Convert PP_VideoDecoder_Profile to media::VideoCodecProfile. media::VideoCodecProfile PPToMediaProfile( const PP_VideoDecoder_Profile pp_profile) { switch (pp_profile) { case PP_VIDEODECODER_H264PROFILE_NONE: // HACK: PPAPI contains a bogus "none" h264 profile that doesn't // correspond to anything in h.264; but a number of released chromium // versions silently promoted this to Baseline profile, so we retain that // behavior here. Fall through. case PP_VIDEODECODER_H264PROFILE_BASELINE: return media::H264PROFILE_BASELINE; case PP_VIDEODECODER_H264PROFILE_MAIN: return media::H264PROFILE_MAIN; case PP_VIDEODECODER_H264PROFILE_EXTENDED: return media::H264PROFILE_EXTENDED; case PP_VIDEODECODER_H264PROFILE_HIGH: return media::H264PROFILE_HIGH; case PP_VIDEODECODER_H264PROFILE_HIGH10PROFILE: return media::H264PROFILE_HIGH10PROFILE; case PP_VIDEODECODER_H264PROFILE_HIGH422PROFILE: return media::H264PROFILE_HIGH422PROFILE; case PP_VIDEODECODER_H264PROFILE_HIGH444PREDICTIVEPROFILE: return media::H264PROFILE_HIGH444PREDICTIVEPROFILE; case PP_VIDEODECODER_H264PROFILE_SCALABLEBASELINE: return media::H264PROFILE_SCALABLEBASELINE; case PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH: return media::H264PROFILE_SCALABLEHIGH; case PP_VIDEODECODER_H264PROFILE_STEREOHIGH: return media::H264PROFILE_STEREOHIGH; case PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH: return media::H264PROFILE_MULTIVIEWHIGH; case PP_VIDEODECODER_VP8PROFILE_ANY: return media::VP8PROFILE_ANY; default: return media::VIDEO_CODEC_PROFILE_UNKNOWN; } } PP_VideoDecodeError_Dev MediaToPPError( media::VideoDecodeAccelerator::Error error) { switch (error) { case media::VideoDecodeAccelerator::ILLEGAL_STATE: return PP_VIDEODECODERERROR_ILLEGAL_STATE; case media::VideoDecodeAccelerator::INVALID_ARGUMENT: return PP_VIDEODECODERERROR_INVALID_ARGUMENT; case media::VideoDecodeAccelerator::UNREADABLE_INPUT: return PP_VIDEODECODERERROR_UNREADABLE_INPUT; case media::VideoDecodeAccelerator::PLATFORM_FAILURE: return PP_VIDEODECODERERROR_PLATFORM_FAILURE; default: NOTREACHED(); return PP_VIDEODECODERERROR_ILLEGAL_STATE; } } } // namespace namespace content { PPB_VideoDecoder_Impl::PPB_VideoDecoder_Impl(PP_Instance instance) : PPB_VideoDecoder_Shared(instance), ppp_videodecoder_(NULL) { } PPB_VideoDecoder_Impl::~PPB_VideoDecoder_Impl() { Destroy(); } // static PP_Resource PPB_VideoDecoder_Impl::Create(PP_Instance instance, PP_Resource graphics_context, PP_VideoDecoder_Profile profile) { scoped_refptr decoder( new PPB_VideoDecoder_Impl(instance)); if (decoder->Init(graphics_context, profile)) return decoder->GetReference(); return 0; } bool PPB_VideoDecoder_Impl::Init(PP_Resource graphics_context, PP_VideoDecoder_Profile profile) { EnterResourceNoLock enter_context(graphics_context, true); if (enter_context.failed()) return false; PPB_Graphics3D_Impl* graphics_3d = static_cast(enter_context.object()); CommandBufferProxyImpl* command_buffer = graphics_3d->GetCommandBufferProxy(); if (!command_buffer) return false; InitCommon(graphics_context, graphics_3d->gles2_impl()); FlushCommandBuffer(); // This is not synchronous, but subsequent IPC messages will be buffered, so // it is okay to immediately send IPC messages. GpuChannelHost* channel = command_buffer->channel(); if (channel) { decoder_.reset(new GpuVideoDecodeAcceleratorHost(channel, command_buffer)); return decoder_->Initialize(PPToMediaProfile(profile), this); } return false; } const PPP_VideoDecoder_Dev* PPB_VideoDecoder_Impl::GetPPP() { if (!ppp_videodecoder_) { PluginModule* plugin_module = HostGlobals::Get()->GetInstance(pp_instance())->module(); if (plugin_module) { ppp_videodecoder_ = static_cast( plugin_module->GetPluginInterface(PPP_VIDEODECODER_DEV_INTERFACE)); } } return ppp_videodecoder_; } int32_t PPB_VideoDecoder_Impl::Decode( const PP_VideoBitstreamBuffer_Dev* bitstream_buffer, scoped_refptr callback) { if (!decoder_) return PP_ERROR_BADRESOURCE; EnterResourceNoLock enter(bitstream_buffer->data, true); if (enter.failed()) return PP_ERROR_FAILED; PPB_Buffer_Impl* buffer = static_cast(enter.object()); DCHECK_GE(bitstream_buffer->id, 0); media::BitstreamBuffer decode_buffer(bitstream_buffer->id, buffer->shared_memory()->handle(), bitstream_buffer->size); if (!SetBitstreamBufferCallback(bitstream_buffer->id, callback)) return PP_ERROR_BADARGUMENT; FlushCommandBuffer(); decoder_->Decode(decode_buffer); return PP_OK_COMPLETIONPENDING; } void PPB_VideoDecoder_Impl::AssignPictureBuffers( uint32_t no_of_buffers, const PP_PictureBuffer_Dev* buffers) { if (!decoder_) return; UMA_HISTOGRAM_COUNTS_100("Media.PepperVideoDecoderPictureCount", no_of_buffers); std::vector wrapped_buffers; for (uint32_t i = 0; i < no_of_buffers; i++) { PP_PictureBuffer_Dev in_buf = buffers[i]; DCHECK_GE(in_buf.id, 0); media::PictureBuffer buffer( in_buf.id, gfx::Size(in_buf.size.width, in_buf.size.height), in_buf.texture_id); wrapped_buffers.push_back(buffer); UMA_HISTOGRAM_COUNTS_10000("Media.PepperVideoDecoderPictureHeight", in_buf.size.height); } FlushCommandBuffer(); decoder_->AssignPictureBuffers(wrapped_buffers); } void PPB_VideoDecoder_Impl::ReusePictureBuffer(int32_t picture_buffer_id) { if (!decoder_) return; FlushCommandBuffer(); decoder_->ReusePictureBuffer(picture_buffer_id); } int32_t PPB_VideoDecoder_Impl::Flush(scoped_refptr callback) { if (!decoder_) return PP_ERROR_BADRESOURCE; if (!SetFlushCallback(callback)) return PP_ERROR_INPROGRESS; FlushCommandBuffer(); decoder_->Flush(); return PP_OK_COMPLETIONPENDING; } int32_t PPB_VideoDecoder_Impl::Reset(scoped_refptr callback) { if (!decoder_) return PP_ERROR_BADRESOURCE; if (!SetResetCallback(callback)) return PP_ERROR_INPROGRESS; FlushCommandBuffer(); decoder_->Reset(); return PP_OK_COMPLETIONPENDING; } void PPB_VideoDecoder_Impl::Destroy() { FlushCommandBuffer(); decoder_.reset(); ppp_videodecoder_ = NULL; ::ppapi::PPB_VideoDecoder_Shared::Destroy(); } void PPB_VideoDecoder_Impl::ProvidePictureBuffers( uint32_t requested_num_of_buffers, const gfx::Size& dimensions, uint32_t texture_target) { DCHECK(RenderThreadImpl::current()); if (!GetPPP()) return; PP_Size out_dim = PP_MakeSize(dimensions.width(), dimensions.height()); GetPPP()->ProvidePictureBuffers(pp_instance(), pp_resource(), requested_num_of_buffers, &out_dim, texture_target); } void PPB_VideoDecoder_Impl::PictureReady(const media::Picture& picture) { // So far picture.visible_rect is not used. If used, visible_rect should // be validated since it comes from GPU process and may not be trustworthy. DCHECK(RenderThreadImpl::current()); if (!GetPPP()) return; PP_Picture_Dev output; output.picture_buffer_id = picture.picture_buffer_id(); output.bitstream_buffer_id = picture.bitstream_buffer_id(); GetPPP()->PictureReady(pp_instance(), pp_resource(), &output); } void PPB_VideoDecoder_Impl::DismissPictureBuffer(int32_t picture_buffer_id) { DCHECK(RenderThreadImpl::current()); if (!GetPPP()) return; GetPPP()->DismissPictureBuffer(pp_instance(), pp_resource(), picture_buffer_id); } void PPB_VideoDecoder_Impl::NotifyError( media::VideoDecodeAccelerator::Error error) { DCHECK(RenderThreadImpl::current()); if (!GetPPP()) return; PP_VideoDecodeError_Dev pp_error = MediaToPPError(error); GetPPP()->NotifyError(pp_instance(), pp_resource(), pp_error); UMA_HISTOGRAM_ENUMERATION("Media.PepperVideoDecoderError", error, media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM); } void PPB_VideoDecoder_Impl::NotifyResetDone() { DCHECK(RenderThreadImpl::current()); RunResetCallback(PP_OK); } void PPB_VideoDecoder_Impl::NotifyEndOfBitstreamBuffer( int32_t bitstream_buffer_id) { DCHECK(RenderThreadImpl::current()); RunBitstreamBufferCallback(bitstream_buffer_id, PP_OK); } void PPB_VideoDecoder_Impl::NotifyFlushDone() { DCHECK(RenderThreadImpl::current()); RunFlushCallback(PP_OK); } } // namespace content