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/ffmpeg_cdm_video_decoder.h"
7 #include "base/logging.h"
8 #include "media/base/buffers.h"
9 #include "media/base/limits.h"
10 #include "media/ffmpeg/ffmpeg_common.h"
12 // Include FFmpeg header files.
14 // Temporarily disable possible loss of data warning.
15 MSVC_PUSH_DISABLE_WARNING(4244);
16 #include <libavcodec/avcodec.h>
22 static const int kDecodeThreads
= 1;
24 static cdm::VideoFormat
PixelFormatToCdmVideoFormat(PixelFormat pixel_format
) {
25 switch (pixel_format
) {
29 DVLOG(1) << "Unsupported PixelFormat: " << pixel_format
;
31 return cdm::kUnknownVideoFormat
;
34 static PixelFormat
CdmVideoFormatToPixelFormat(cdm::VideoFormat video_format
) {
35 switch (video_format
) {
38 return PIX_FMT_YUV420P
;
39 case cdm::kUnknownVideoFormat
:
41 DVLOG(1) << "Unsupported cdm::VideoFormat: " << video_format
;
46 static AVCodecID
CdmVideoCodecToCodecID(
47 cdm::VideoDecoderConfig::VideoCodec video_codec
) {
48 switch (video_codec
) {
49 case cdm::VideoDecoderConfig::kCodecVp8
:
50 return AV_CODEC_ID_VP8
;
51 case cdm::VideoDecoderConfig::kCodecH264
:
52 return AV_CODEC_ID_H264
;
53 case cdm::VideoDecoderConfig::kCodecVp9
:
54 return AV_CODEC_ID_VP9
;
55 case cdm::VideoDecoderConfig::kUnknownVideoCodec
:
57 NOTREACHED() << "Unsupported cdm::VideoCodec: " << video_codec
;
58 return AV_CODEC_ID_NONE
;
62 static int CdmVideoCodecProfileToProfileID(
63 cdm::VideoDecoderConfig::VideoCodecProfile profile
) {
65 case cdm::VideoDecoderConfig::kProfileNotNeeded
:
66 // For codecs that do not need a profile (e.g. VP8/VP9), does not define
68 return FF_PROFILE_UNKNOWN
;
69 case cdm::VideoDecoderConfig::kH264ProfileBaseline
:
70 return FF_PROFILE_H264_BASELINE
;
71 case cdm::VideoDecoderConfig::kH264ProfileMain
:
72 return FF_PROFILE_H264_MAIN
;
73 case cdm::VideoDecoderConfig::kH264ProfileExtended
:
74 return FF_PROFILE_H264_EXTENDED
;
75 case cdm::VideoDecoderConfig::kH264ProfileHigh
:
76 return FF_PROFILE_H264_HIGH
;
77 case cdm::VideoDecoderConfig::kH264ProfileHigh10
:
78 return FF_PROFILE_H264_HIGH_10
;
79 case cdm::VideoDecoderConfig::kH264ProfileHigh422
:
80 return FF_PROFILE_H264_HIGH_422
;
81 case cdm::VideoDecoderConfig::kH264ProfileHigh444Predictive
:
82 return FF_PROFILE_H264_HIGH_444_PREDICTIVE
;
83 case cdm::VideoDecoderConfig::kUnknownVideoCodecProfile
:
85 NOTREACHED() << "Unknown cdm::VideoCodecProfile: " << profile
;
86 return FF_PROFILE_UNKNOWN
;
90 static void CdmVideoDecoderConfigToAVCodecContext(
91 const cdm::VideoDecoderConfig
& config
,
92 AVCodecContext
* codec_context
) {
93 codec_context
->codec_type
= AVMEDIA_TYPE_VIDEO
;
94 codec_context
->codec_id
= CdmVideoCodecToCodecID(config
.codec
);
95 codec_context
->profile
= CdmVideoCodecProfileToProfileID(config
.profile
);
96 codec_context
->coded_width
= config
.coded_size
.width
;
97 codec_context
->coded_height
= config
.coded_size
.height
;
98 codec_context
->pix_fmt
= CdmVideoFormatToPixelFormat(config
.format
);
100 if (config
.extra_data
) {
101 codec_context
->extradata_size
= config
.extra_data_size
;
102 codec_context
->extradata
= reinterpret_cast<uint8_t*>(
103 av_malloc(config
.extra_data_size
+ FF_INPUT_BUFFER_PADDING_SIZE
));
104 memcpy(codec_context
->extradata
, config
.extra_data
,
105 config
.extra_data_size
);
106 memset(codec_context
->extradata
+ config
.extra_data_size
, 0,
107 FF_INPUT_BUFFER_PADDING_SIZE
);
109 codec_context
->extradata
= NULL
;
110 codec_context
->extradata_size
= 0;
114 static void CopyPlane(const uint8_t* source
,
115 int32_t source_stride
,
116 int32_t target_stride
,
118 int32_t copy_bytes_per_row
,
122 DCHECK_LE(copy_bytes_per_row
, source_stride
);
123 DCHECK_LE(copy_bytes_per_row
, target_stride
);
125 for (int i
= 0; i
< rows
; ++i
) {
126 const int source_offset
= i
* source_stride
;
127 const int target_offset
= i
* target_stride
;
128 memcpy(target
+ target_offset
,
129 source
+ source_offset
,
134 FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(ClearKeyCdmHost
* host
)
135 : is_initialized_(false),
139 FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() {
140 ReleaseFFmpegResources();
143 bool FFmpegCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig
& config
) {
144 DVLOG(1) << "Initialize()";
146 if (!IsValidOutputConfig(config
.format
, config
.coded_size
)) {
147 LOG(ERROR
) << "Initialize(): invalid video decoder configuration.";
151 if (is_initialized_
) {
152 LOG(ERROR
) << "Initialize(): Already initialized.";
156 // Initialize AVCodecContext structure.
157 codec_context_
.reset(avcodec_alloc_context3(NULL
));
158 CdmVideoDecoderConfigToAVCodecContext(config
, codec_context_
.get());
160 // Enable motion vector search (potentially slow), strong deblocking filter
161 // for damaged macroblocks, and set our error detection sensitivity.
162 codec_context_
->error_concealment
= FF_EC_GUESS_MVS
| FF_EC_DEBLOCK
;
163 codec_context_
->err_recognition
= AV_EF_CAREFUL
;
164 codec_context_
->thread_count
= kDecodeThreads
;
165 codec_context_
->opaque
= this;
166 codec_context_
->flags
|= CODEC_FLAG_EMU_EDGE
;
168 AVCodec
* codec
= avcodec_find_decoder(codec_context_
->codec_id
);
170 LOG(ERROR
) << "Initialize(): avcodec_find_decoder failed.";
175 if ((status
= avcodec_open2(codec_context_
.get(), codec
, NULL
)) < 0) {
176 LOG(ERROR
) << "Initialize(): avcodec_open2 failed: " << status
;
180 av_frame_
.reset(av_frame_alloc());
181 is_initialized_
= true;
186 void FFmpegCdmVideoDecoder::Deinitialize() {
187 DVLOG(1) << "Deinitialize()";
188 ReleaseFFmpegResources();
189 is_initialized_
= false;
192 void FFmpegCdmVideoDecoder::Reset() {
193 DVLOG(1) << "Reset()";
194 avcodec_flush_buffers(codec_context_
.get());
198 bool FFmpegCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format
,
199 const cdm::Size
& data_size
) {
200 return ((format
== cdm::kYv12
|| format
== cdm::kI420
) &&
201 (data_size
.width
% 2) == 0 && (data_size
.height
% 2) == 0 &&
202 data_size
.width
> 0 && data_size
.height
> 0 &&
203 data_size
.width
<= limits::kMaxDimension
&&
204 data_size
.height
<= limits::kMaxDimension
&&
205 data_size
.width
* data_size
.height
<= limits::kMaxCanvas
);
208 cdm::Status
FFmpegCdmVideoDecoder::DecodeFrame(
209 const uint8_t* compressed_frame
,
210 int32_t compressed_frame_size
,
212 cdm::VideoFrame
* decoded_frame
) {
213 DVLOG(1) << "DecodeFrame()";
214 DCHECK(decoded_frame
);
216 // Create a packet for input data.
218 av_init_packet(&packet
);
220 // The FFmpeg API does not allow us to have const read-only pointers.
221 packet
.data
= const_cast<uint8_t*>(compressed_frame
);
222 packet
.size
= compressed_frame_size
;
224 // Let FFmpeg handle presentation timestamp reordering.
225 codec_context_
->reordered_opaque
= timestamp
;
227 // Reset frame to default values.
228 av_frame_unref(av_frame_
.get());
230 // This is for codecs not using get_buffer to initialize
231 // |av_frame_->reordered_opaque|
232 av_frame_
->reordered_opaque
= codec_context_
->reordered_opaque
;
234 int frame_decoded
= 0;
235 int result
= avcodec_decode_video2(codec_context_
.get(),
239 // Log the problem when we can't decode a video frame and exit early.
241 LOG(ERROR
) << "DecodeFrame(): Error decoding video frame with timestamp: "
242 << timestamp
<< " us, packet size: " << packet
.size
<< " bytes";
243 return cdm::kDecodeError
;
246 // If no frame was produced then signal that more data is required to produce
248 if (frame_decoded
== 0)
249 return cdm::kNeedMoreData
;
251 // The decoder is in a bad state and not decoding correctly.
252 // Checking for NULL avoids a crash.
253 if (!av_frame_
->data
[cdm::VideoFrame::kYPlane
] ||
254 !av_frame_
->data
[cdm::VideoFrame::kUPlane
] ||
255 !av_frame_
->data
[cdm::VideoFrame::kVPlane
]) {
256 LOG(ERROR
) << "DecodeFrame(): Video frame has invalid frame data.";
257 return cdm::kDecodeError
;
260 if (!CopyAvFrameTo(decoded_frame
)) {
261 LOG(ERROR
) << "DecodeFrame() could not copy video frame to output buffer.";
262 return cdm::kDecodeError
;
265 return cdm::kSuccess
;
268 bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame
* cdm_video_frame
) {
269 DCHECK(cdm_video_frame
);
270 DCHECK_EQ(av_frame_
->format
, PIX_FMT_YUV420P
);
271 DCHECK_EQ(av_frame_
->width
% 2, 0);
272 DCHECK_EQ(av_frame_
->height
% 2, 0);
274 const int y_size
= av_frame_
->width
* av_frame_
->height
;
275 const int uv_size
= y_size
/ 2;
276 const int space_required
= y_size
+ (uv_size
* 2);
278 DCHECK(!cdm_video_frame
->FrameBuffer());
279 cdm_video_frame
->SetFrameBuffer(host_
->Allocate(space_required
));
280 if (!cdm_video_frame
->FrameBuffer()) {
281 LOG(ERROR
) << "CopyAvFrameTo() ClearKeyCdmHost::Allocate failed.";
284 cdm_video_frame
->FrameBuffer()->SetSize(space_required
);
286 CopyPlane(av_frame_
->data
[cdm::VideoFrame::kYPlane
],
287 av_frame_
->linesize
[cdm::VideoFrame::kYPlane
],
291 cdm_video_frame
->FrameBuffer()->Data());
293 const int uv_stride
= av_frame_
->width
/ 2;
294 const int uv_rows
= av_frame_
->height
/ 2;
295 CopyPlane(av_frame_
->data
[cdm::VideoFrame::kUPlane
],
296 av_frame_
->linesize
[cdm::VideoFrame::kUPlane
],
300 cdm_video_frame
->FrameBuffer()->Data() + y_size
);
302 CopyPlane(av_frame_
->data
[cdm::VideoFrame::kVPlane
],
303 av_frame_
->linesize
[cdm::VideoFrame::kVPlane
],
307 cdm_video_frame
->FrameBuffer()->Data() + y_size
+ uv_size
);
309 PixelFormat format
= static_cast<PixelFormat
>(av_frame_
->format
);
310 cdm_video_frame
->SetFormat(PixelFormatToCdmVideoFormat(format
));
312 cdm::Size video_frame_size
;
313 video_frame_size
.width
= av_frame_
->width
;
314 video_frame_size
.height
= av_frame_
->height
;
315 cdm_video_frame
->SetSize(video_frame_size
);
317 cdm_video_frame
->SetPlaneOffset(cdm::VideoFrame::kYPlane
, 0);
318 cdm_video_frame
->SetPlaneOffset(cdm::VideoFrame::kUPlane
, y_size
);
319 cdm_video_frame
->SetPlaneOffset(cdm::VideoFrame::kVPlane
,
322 cdm_video_frame
->SetStride(cdm::VideoFrame::kYPlane
, av_frame_
->width
);
323 cdm_video_frame
->SetStride(cdm::VideoFrame::kUPlane
, uv_stride
);
324 cdm_video_frame
->SetStride(cdm::VideoFrame::kVPlane
, uv_stride
);
326 cdm_video_frame
->SetTimestamp(av_frame_
->reordered_opaque
);
331 void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() {
332 DVLOG(1) << "ReleaseFFmpegResources()";
334 codec_context_
.reset();