• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "media/cdm/ppapi/libvpx_cdm_video_decoder.h"
6 
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "media/base/buffers.h"
10 #include "media/base/limits.h"
11 
12 // Include libvpx header files.
13 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
14 // backwards compatibility for legacy applications using the library.
15 #define VPX_CODEC_DISABLE_COMPAT 1
16 extern "C" {
17 // Note: vpx_decoder.h must be first or compile will fail.
18 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"  // NOLINT
19 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
20 }
21 
22 // Enable USE_COPYPLANE_WITH_LIBVPX to use |CopyPlane()| instead of memcpy to
23 // copy video frame data.
24 // #define USE_COPYPLANE_WITH_LIBVPX 1
25 
26 namespace media {
27 
28 static const int kDecodeThreads = 2;
29 
LibvpxCdmVideoDecoder(CdmHost * host)30 LibvpxCdmVideoDecoder::LibvpxCdmVideoDecoder(CdmHost* host)
31     : is_initialized_(false),
32       host_(host),
33       vpx_codec_(NULL),
34       vpx_image_(NULL) {
35 }
36 
~LibvpxCdmVideoDecoder()37 LibvpxCdmVideoDecoder::~LibvpxCdmVideoDecoder() {
38   Deinitialize();
39 }
40 
Initialize(const cdm::VideoDecoderConfig & config)41 bool LibvpxCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) {
42   DVLOG(1) << "Initialize()";
43 
44   if (!IsValidOutputConfig(config.format, config.coded_size)) {
45     LOG(ERROR) << "Initialize(): invalid video decoder configuration.";
46     return false;
47   }
48 
49   if (is_initialized_) {
50     LOG(ERROR) << "Initialize(): Already initialized.";
51     return false;
52   }
53 
54   vpx_codec_ = new vpx_codec_ctx();
55   vpx_codec_dec_cfg_t vpx_config = {0};
56   vpx_config.w = config.coded_size.width;
57   vpx_config.h = config.coded_size.height;
58   vpx_config.threads = kDecodeThreads;
59 
60   vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_,
61                                               vpx_codec_vp8_dx(),
62                                               &vpx_config,
63                                               0);
64   if (status != VPX_CODEC_OK) {
65     LOG(ERROR) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret="
66                << status;
67     delete vpx_codec_;
68     vpx_codec_ = NULL;
69   }
70 
71   is_initialized_ = true;
72   return true;
73 }
74 
Deinitialize()75 void LibvpxCdmVideoDecoder::Deinitialize() {
76   DVLOG(1) << "Deinitialize()";
77 
78   if (vpx_codec_) {
79     vpx_codec_destroy(vpx_codec_);
80     vpx_codec_ = NULL;
81   }
82 
83   is_initialized_ = false;
84 }
85 
Reset()86 void LibvpxCdmVideoDecoder::Reset() {
87   DVLOG(1) << "Reset()";
88 }
89 
90 // static
IsValidOutputConfig(cdm::VideoFormat format,const cdm::Size & data_size)91 bool LibvpxCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format,
92                                                 const cdm::Size& data_size) {
93   return ((format == cdm::kYv12 || format == cdm::kI420) &&
94           (data_size.width % 2) == 0 && (data_size.height % 2) == 0 &&
95           data_size.width > 0 && data_size.height > 0 &&
96           data_size.width <= limits::kMaxDimension &&
97           data_size.height <= limits::kMaxDimension &&
98           data_size.width * data_size.height <= limits::kMaxCanvas);
99 }
100 
DecodeFrame(const uint8_t * compressed_frame,int32_t compressed_frame_size,int64_t timestamp,cdm::VideoFrame * decoded_frame)101 cdm::Status LibvpxCdmVideoDecoder::DecodeFrame(
102     const uint8_t* compressed_frame,
103     int32_t compressed_frame_size,
104     int64_t timestamp,
105     cdm::VideoFrame* decoded_frame) {
106   DVLOG(1) << "DecodeFrame()";
107   DCHECK(decoded_frame);
108 
109   // Pass |compressed_frame| to libvpx.
110   void* user_priv = reinterpret_cast<void*>(&timestamp);
111   vpx_codec_err_t status = vpx_codec_decode(vpx_codec_,
112                                             compressed_frame,
113                                             compressed_frame_size,
114                                             user_priv,
115                                             0);
116   if (status != VPX_CODEC_OK) {
117     LOG(ERROR) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status="
118                << status;
119     return cdm::kDecodeError;
120   }
121 
122   // Gets pointer to decoded data.
123   vpx_codec_iter_t iter = NULL;
124   vpx_image_ = vpx_codec_get_frame(vpx_codec_, &iter);
125   if (!vpx_image_)
126     return cdm::kNeedMoreData;
127 
128   if (vpx_image_->user_priv != reinterpret_cast<void*>(&timestamp)) {
129     LOG(ERROR) << "DecodeFrameLibvpx() invalid output timestamp.";
130     return cdm::kDecodeError;
131   }
132   decoded_frame->SetTimestamp(timestamp);
133 
134   if (!CopyVpxImageTo(decoded_frame)) {
135     LOG(ERROR) << "DecodeFrameLibvpx() could not copy vpx image to output "
136                << "buffer.";
137     return cdm::kDecodeError;
138   }
139 
140   return cdm::kSuccess;
141 }
142 
CopyVpxImageTo(cdm::VideoFrame * cdm_video_frame)143 bool LibvpxCdmVideoDecoder::CopyVpxImageTo(cdm::VideoFrame* cdm_video_frame) {
144   DCHECK(cdm_video_frame);
145   DCHECK_EQ(vpx_image_->fmt, VPX_IMG_FMT_I420);
146   DCHECK_EQ(vpx_image_->d_w % 2, 0U);
147   DCHECK_EQ(vpx_image_->d_h % 2, 0U);
148 
149   const int y_size = vpx_image_->stride[VPX_PLANE_Y] * vpx_image_->d_h;
150   const int uv_rows = vpx_image_->d_h / 2;
151   const int u_size = vpx_image_->stride[VPX_PLANE_U] * uv_rows;
152   const int v_size = vpx_image_->stride[VPX_PLANE_V] * uv_rows;
153   const int space_required = y_size + u_size + v_size;
154 
155   DCHECK(!cdm_video_frame->FrameBuffer());
156   cdm_video_frame->SetFrameBuffer(host_->Allocate(space_required));
157   if (!cdm_video_frame->FrameBuffer()) {
158     LOG(ERROR) << "CopyVpxImageTo() CdmHost::Allocate failed.";
159     return false;
160   }
161   cdm_video_frame->FrameBuffer()->SetSize(space_required);
162 
163   memcpy(cdm_video_frame->FrameBuffer()->Data(),
164          vpx_image_->planes[VPX_PLANE_Y],
165          y_size);
166   memcpy(cdm_video_frame->FrameBuffer()->Data() + y_size,
167          vpx_image_->planes[VPX_PLANE_U],
168          u_size);
169   memcpy(cdm_video_frame->FrameBuffer()->Data() + y_size + u_size,
170          vpx_image_->planes[VPX_PLANE_V],
171          v_size);
172 
173   cdm_video_frame->SetFormat(cdm::kYv12);
174 
175   cdm::Size video_frame_size;
176   video_frame_size.width = vpx_image_->d_w;
177   video_frame_size.height = vpx_image_->d_h;
178   cdm_video_frame->SetSize(video_frame_size);
179 
180   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kYPlane, 0);
181   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kUPlane, y_size);
182   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kVPlane,
183                                     y_size + u_size);
184 
185   cdm_video_frame->SetStride(cdm::VideoFrame::kYPlane,
186                               vpx_image_->stride[VPX_PLANE_Y]);
187   cdm_video_frame->SetStride(cdm::VideoFrame::kUPlane,
188                               vpx_image_->stride[VPX_PLANE_U]);
189   cdm_video_frame->SetStride(cdm::VideoFrame::kVPlane,
190                               vpx_image_->stride[VPX_PLANE_V]);
191 
192   return true;
193 }
194 
195 }  // namespace media
196