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