1// Copyright (c) 2012 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/ppb_video_decoder_impl.h"
6
7#include <string>
8
9#include "base/logging.h"
10#include "base/message_loop/message_loop.h"
11#include "base/metrics/histogram.h"
12#include "content/common/gpu/client/gpu_channel_host.h"
13#include "content/renderer/pepper/host_globals.h"
14#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
15#include "content/renderer/pepper/plugin_module.h"
16#include "content/renderer/pepper/ppb_buffer_impl.h"
17#include "content/renderer/pepper/ppb_graphics_3d_impl.h"
18#include "content/renderer/render_thread_impl.h"
19#include "gpu/command_buffer/client/gles2_implementation.h"
20#include "media/video/picture.h"
21#include "media/video/video_decode_accelerator.h"
22#include "ppapi/c/dev/pp_video_dev.h"
23#include "ppapi/c/dev/ppb_video_decoder_dev.h"
24#include "ppapi/c/dev/ppp_video_decoder_dev.h"
25#include "ppapi/c/pp_completion_callback.h"
26#include "ppapi/c/pp_errors.h"
27#include "ppapi/shared_impl/resource_tracker.h"
28#include "ppapi/thunk/enter.h"
29
30using ppapi::TrackedCallback;
31using ppapi::thunk::EnterResourceNoLock;
32using ppapi::thunk::PPB_Buffer_API;
33using ppapi::thunk::PPB_Graphics3D_API;
34
35namespace {
36
37// Convert PP_VideoDecoder_Profile to media::VideoCodecProfile.
38media::VideoCodecProfile PPToMediaProfile(
39    const PP_VideoDecoder_Profile pp_profile) {
40  switch (pp_profile) {
41    case PP_VIDEODECODER_H264PROFILE_NONE:
42    // HACK: PPAPI contains a bogus "none" h264 profile that doesn't
43    // correspond to anything in h.264; but a number of released chromium
44    // versions silently promoted this to Baseline profile, so we retain that
45    // behavior here.  Fall through.
46    case PP_VIDEODECODER_H264PROFILE_BASELINE:
47      return media::H264PROFILE_BASELINE;
48    case PP_VIDEODECODER_H264PROFILE_MAIN:
49      return media::H264PROFILE_MAIN;
50    case PP_VIDEODECODER_H264PROFILE_EXTENDED:
51      return media::H264PROFILE_EXTENDED;
52    case PP_VIDEODECODER_H264PROFILE_HIGH:
53      return media::H264PROFILE_HIGH;
54    case PP_VIDEODECODER_H264PROFILE_HIGH10PROFILE:
55      return media::H264PROFILE_HIGH10PROFILE;
56    case PP_VIDEODECODER_H264PROFILE_HIGH422PROFILE:
57      return media::H264PROFILE_HIGH422PROFILE;
58    case PP_VIDEODECODER_H264PROFILE_HIGH444PREDICTIVEPROFILE:
59      return media::H264PROFILE_HIGH444PREDICTIVEPROFILE;
60    case PP_VIDEODECODER_H264PROFILE_SCALABLEBASELINE:
61      return media::H264PROFILE_SCALABLEBASELINE;
62    case PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH:
63      return media::H264PROFILE_SCALABLEHIGH;
64    case PP_VIDEODECODER_H264PROFILE_STEREOHIGH:
65      return media::H264PROFILE_STEREOHIGH;
66    case PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH:
67      return media::H264PROFILE_MULTIVIEWHIGH;
68    case PP_VIDEODECODER_VP8PROFILE_ANY:
69      return media::VP8PROFILE_ANY;
70    default:
71      return media::VIDEO_CODEC_PROFILE_UNKNOWN;
72  }
73}
74
75PP_VideoDecodeError_Dev MediaToPPError(
76    media::VideoDecodeAccelerator::Error error) {
77  switch (error) {
78    case media::VideoDecodeAccelerator::ILLEGAL_STATE:
79      return PP_VIDEODECODERERROR_ILLEGAL_STATE;
80    case media::VideoDecodeAccelerator::INVALID_ARGUMENT:
81      return PP_VIDEODECODERERROR_INVALID_ARGUMENT;
82    case media::VideoDecodeAccelerator::UNREADABLE_INPUT:
83      return PP_VIDEODECODERERROR_UNREADABLE_INPUT;
84    case media::VideoDecodeAccelerator::PLATFORM_FAILURE:
85      return PP_VIDEODECODERERROR_PLATFORM_FAILURE;
86    default:
87      NOTREACHED();
88      return PP_VIDEODECODERERROR_ILLEGAL_STATE;
89  }
90}
91
92}  // namespace
93
94namespace content {
95
96PPB_VideoDecoder_Impl::PPB_VideoDecoder_Impl(PP_Instance instance)
97    : PPB_VideoDecoder_Shared(instance), ppp_videodecoder_(NULL) {
98  PluginModule* plugin_module =
99      HostGlobals::Get()->GetInstance(pp_instance())->module();
100  if (plugin_module) {
101    ppp_videodecoder_ = static_cast<const PPP_VideoDecoder_Dev*>(
102        plugin_module->GetPluginInterface(PPP_VIDEODECODER_DEV_INTERFACE));
103  }
104}
105
106PPB_VideoDecoder_Impl::~PPB_VideoDecoder_Impl() { Destroy(); }
107
108// static
109PP_Resource PPB_VideoDecoder_Impl::Create(PP_Instance instance,
110                                          PP_Resource graphics_context,
111                                          PP_VideoDecoder_Profile profile) {
112  scoped_refptr<PPB_VideoDecoder_Impl> decoder(
113      new PPB_VideoDecoder_Impl(instance));
114  if (decoder->Init(graphics_context, profile))
115    return decoder->GetReference();
116  return 0;
117}
118
119bool PPB_VideoDecoder_Impl::Init(PP_Resource graphics_context,
120                                 PP_VideoDecoder_Profile profile) {
121  EnterResourceNoLock<PPB_Graphics3D_API> enter_context(graphics_context, true);
122  if (enter_context.failed())
123    return false;
124
125  PPB_Graphics3D_Impl* graphics_3d =
126      static_cast<PPB_Graphics3D_Impl*>(enter_context.object());
127
128  int command_buffer_route_id = graphics_3d->GetCommandBufferRouteId();
129  if (command_buffer_route_id == 0)
130    return false;
131
132  InitCommon(graphics_context, graphics_3d->gles2_impl());
133  FlushCommandBuffer();
134
135  // This is not synchronous, but subsequent IPC messages will be buffered, so
136  // it is okay to immediately send IPC messages through the returned channel.
137  GpuChannelHost* channel = graphics_3d->channel();
138  DCHECK(channel);
139  decoder_ = channel->CreateVideoDecoder(command_buffer_route_id);
140  return (decoder_ && decoder_->Initialize(PPToMediaProfile(profile), this));
141}
142
143int32_t PPB_VideoDecoder_Impl::Decode(
144    const PP_VideoBitstreamBuffer_Dev* bitstream_buffer,
145    scoped_refptr<TrackedCallback> callback) {
146  if (!decoder_)
147    return PP_ERROR_BADRESOURCE;
148
149  EnterResourceNoLock<PPB_Buffer_API> enter(bitstream_buffer->data, true);
150  if (enter.failed())
151    return PP_ERROR_FAILED;
152
153  PPB_Buffer_Impl* buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
154  DCHECK_GE(bitstream_buffer->id, 0);
155  media::BitstreamBuffer decode_buffer(bitstream_buffer->id,
156                                       buffer->shared_memory()->handle(),
157                                       bitstream_buffer->size);
158  if (!SetBitstreamBufferCallback(bitstream_buffer->id, callback))
159    return PP_ERROR_BADARGUMENT;
160
161  FlushCommandBuffer();
162  decoder_->Decode(decode_buffer);
163  return PP_OK_COMPLETIONPENDING;
164}
165
166void PPB_VideoDecoder_Impl::AssignPictureBuffers(
167    uint32_t no_of_buffers,
168    const PP_PictureBuffer_Dev* buffers) {
169  if (!decoder_)
170    return;
171  UMA_HISTOGRAM_COUNTS_100("Media.PepperVideoDecoderPictureCount",
172                           no_of_buffers);
173
174  std::vector<media::PictureBuffer> wrapped_buffers;
175  for (uint32 i = 0; i < no_of_buffers; i++) {
176    PP_PictureBuffer_Dev in_buf = buffers[i];
177    DCHECK_GE(in_buf.id, 0);
178    media::PictureBuffer buffer(
179        in_buf.id,
180        gfx::Size(in_buf.size.width, in_buf.size.height),
181        in_buf.texture_id);
182    wrapped_buffers.push_back(buffer);
183    UMA_HISTOGRAM_COUNTS_10000("Media.PepperVideoDecoderPictureHeight",
184                               in_buf.size.height);
185  }
186
187  FlushCommandBuffer();
188  decoder_->AssignPictureBuffers(wrapped_buffers);
189}
190
191void PPB_VideoDecoder_Impl::ReusePictureBuffer(int32_t picture_buffer_id) {
192  if (!decoder_)
193    return;
194
195  FlushCommandBuffer();
196  decoder_->ReusePictureBuffer(picture_buffer_id);
197}
198
199int32_t PPB_VideoDecoder_Impl::Flush(scoped_refptr<TrackedCallback> callback) {
200  if (!decoder_)
201    return PP_ERROR_BADRESOURCE;
202
203  if (!SetFlushCallback(callback))
204    return PP_ERROR_INPROGRESS;
205
206  FlushCommandBuffer();
207  decoder_->Flush();
208  return PP_OK_COMPLETIONPENDING;
209}
210
211int32_t PPB_VideoDecoder_Impl::Reset(scoped_refptr<TrackedCallback> callback) {
212  if (!decoder_)
213    return PP_ERROR_BADRESOURCE;
214
215  if (!SetResetCallback(callback))
216    return PP_ERROR_INPROGRESS;
217
218  FlushCommandBuffer();
219  decoder_->Reset();
220  return PP_OK_COMPLETIONPENDING;
221}
222
223void PPB_VideoDecoder_Impl::Destroy() {
224  FlushCommandBuffer();
225
226  decoder_.reset();
227  ppp_videodecoder_ = NULL;
228
229  ::ppapi::PPB_VideoDecoder_Shared::Destroy();
230}
231
232void PPB_VideoDecoder_Impl::ProvidePictureBuffers(
233    uint32 requested_num_of_buffers,
234    const gfx::Size& dimensions,
235    uint32 texture_target) {
236  DCHECK(RenderThreadImpl::current());
237  if (!ppp_videodecoder_)
238    return;
239
240  PP_Size out_dim = PP_MakeSize(dimensions.width(), dimensions.height());
241  ppp_videodecoder_->ProvidePictureBuffers(pp_instance(),
242                                           pp_resource(),
243                                           requested_num_of_buffers,
244                                           &out_dim,
245                                           texture_target);
246}
247
248void PPB_VideoDecoder_Impl::PictureReady(const media::Picture& picture) {
249  // So far picture.visible_rect is not used. If used, visible_rect should
250  // be validated since it comes from GPU process and may not be trustworthy.
251  DCHECK(RenderThreadImpl::current());
252  if (!ppp_videodecoder_)
253    return;
254
255  PP_Picture_Dev output;
256  output.picture_buffer_id = picture.picture_buffer_id();
257  output.bitstream_buffer_id = picture.bitstream_buffer_id();
258  ppp_videodecoder_->PictureReady(pp_instance(), pp_resource(), &output);
259}
260
261void PPB_VideoDecoder_Impl::DismissPictureBuffer(int32 picture_buffer_id) {
262  DCHECK(RenderThreadImpl::current());
263  if (!ppp_videodecoder_)
264    return;
265  ppp_videodecoder_->DismissPictureBuffer(
266      pp_instance(), pp_resource(), picture_buffer_id);
267}
268
269void PPB_VideoDecoder_Impl::NotifyError(
270    media::VideoDecodeAccelerator::Error error) {
271  DCHECK(RenderThreadImpl::current());
272  if (!ppp_videodecoder_)
273    return;
274
275  PP_VideoDecodeError_Dev pp_error = MediaToPPError(error);
276  ppp_videodecoder_->NotifyError(pp_instance(), pp_resource(), pp_error);
277  UMA_HISTOGRAM_ENUMERATION("Media.PepperVideoDecoderError",
278                            error,
279                            media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM);
280}
281
282void PPB_VideoDecoder_Impl::NotifyResetDone() {
283  DCHECK(RenderThreadImpl::current());
284  RunResetCallback(PP_OK);
285}
286
287void PPB_VideoDecoder_Impl::NotifyEndOfBitstreamBuffer(
288    int32 bitstream_buffer_id) {
289  DCHECK(RenderThreadImpl::current());
290  RunBitstreamBufferCallback(bitstream_buffer_id, PP_OK);
291}
292
293void PPB_VideoDecoder_Impl::NotifyFlushDone() {
294  DCHECK(RenderThreadImpl::current());
295  RunFlushCallback(PP_OK);
296}
297
298}  // namespace content
299