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.
5 #include "media/cdm/ppapi/external_clear_key/libvpx_cdm_video_decoder.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "media/base/buffers.h"
10 #include "media/base/limits.h"
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
17 // Note: vpx_decoder.h must be first or compile will fail.
18 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
19 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" // NOLINT
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
28 static const int kDecodeThreads
= 2;
30 LibvpxCdmVideoDecoder::LibvpxCdmVideoDecoder(CdmHost
* host
)
31 : is_initialized_(false),
37 LibvpxCdmVideoDecoder::~LibvpxCdmVideoDecoder() {
41 bool LibvpxCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig
& config
) {
42 DVLOG(1) << "Initialize()";
44 if (!IsValidOutputConfig(config
.format
, config
.coded_size
)) {
45 LOG(ERROR
) << "Initialize(): invalid video decoder configuration.";
49 if (is_initialized_
) {
50 LOG(ERROR
) << "Initialize(): Already initialized.";
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
;
60 vpx_codec_err_t status
= vpx_codec_dec_init(vpx_codec_
,
64 if (status
!= VPX_CODEC_OK
) {
65 LOG(ERROR
) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret="
71 is_initialized_
= true;
75 void LibvpxCdmVideoDecoder::Deinitialize() {
76 DVLOG(1) << "Deinitialize()";
79 vpx_codec_destroy(vpx_codec_
);
83 is_initialized_
= false;
86 void LibvpxCdmVideoDecoder::Reset() {
87 DVLOG(1) << "Reset()";
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
);
101 cdm::Status
LibvpxCdmVideoDecoder::DecodeFrame(
102 const uint8_t* compressed_frame
,
103 int32_t compressed_frame_size
,
105 cdm::VideoFrame
* decoded_frame
) {
106 DVLOG(1) << "DecodeFrame()";
107 DCHECK(decoded_frame
);
109 // Pass |compressed_frame| to libvpx.
110 void* user_priv
= reinterpret_cast<void*>(×tamp
);
111 vpx_codec_err_t status
= vpx_codec_decode(vpx_codec_
,
113 compressed_frame_size
,
116 if (status
!= VPX_CODEC_OK
) {
117 LOG(ERROR
) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status="
119 return cdm::kDecodeError
;
122 // Gets pointer to decoded data.
123 vpx_codec_iter_t iter
= NULL
;
124 vpx_image_
= vpx_codec_get_frame(vpx_codec_
, &iter
);
126 return cdm::kNeedMoreData
;
128 if (vpx_image_
->user_priv
!= reinterpret_cast<void*>(×tamp
)) {
129 LOG(ERROR
) << "DecodeFrameLibvpx() invalid output timestamp.";
130 return cdm::kDecodeError
;
132 decoded_frame
->SetTimestamp(timestamp
);
134 if (!CopyVpxImageTo(decoded_frame
)) {
135 LOG(ERROR
) << "DecodeFrameLibvpx() could not copy vpx image to output "
137 return cdm::kDecodeError
;
140 return cdm::kSuccess
;
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);
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
;
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.";
161 cdm_video_frame
->FrameBuffer()->SetSize(space_required
);
163 memcpy(cdm_video_frame
->FrameBuffer()->Data(),
164 vpx_image_
->planes
[VPX_PLANE_Y
],
166 memcpy(cdm_video_frame
->FrameBuffer()->Data() + y_size
,
167 vpx_image_
->planes
[VPX_PLANE_U
],
169 memcpy(cdm_video_frame
->FrameBuffer()->Data() + y_size
+ u_size
,
170 vpx_image_
->planes
[VPX_PLANE_V
],
173 cdm_video_frame
->SetFormat(cdm::kYv12
);
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
);
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
,
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
]);