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.
5 #include "media/filters/vpx_video_decoder.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/command_line.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/sys_byteorder.h"
18 #include "media/base/bind_to_loop.h"
19 #include "media/base/decoder_buffer.h"
20 #include "media/base/demuxer_stream.h"
21 #include "media/base/media_switches.h"
22 #include "media/base/pipeline.h"
23 #include "media/base/video_decoder_config.h"
24 #include "media/base/video_frame.h"
25 #include "media/base/video_util.h"
27 // Include libvpx header files.
28 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
29 // backwards compatibility for legacy applications using the library.
30 #define VPX_CODEC_DISABLE_COMPAT 1
32 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
33 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
38 // Always try to use three threads for video decoding. There is little reason
39 // not to since current day CPUs tend to be multi-core and we measured
40 // performance benefits on older machines such as P4s with hyperthreading.
41 static const int kDecodeThreads
= 2;
42 static const int kMaxDecodeThreads
= 16;
44 // Returns the number of threads.
45 static int GetThreadCount() {
46 // TODO(scherkus): De-duplicate this function and the one used by
47 // FFmpegVideoDecoder.
49 // Refer to http://crbug.com/93932 for tsan suppressions on decoding.
50 int decode_threads
= kDecodeThreads
;
52 const CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
53 std::string
threads(cmd_line
->GetSwitchValueASCII(switches::kVideoThreads
));
54 if (threads
.empty() || !base::StringToInt(threads
, &decode_threads
))
55 return decode_threads
;
57 decode_threads
= std::max(decode_threads
, 0);
58 decode_threads
= std::min(decode_threads
, kMaxDecodeThreads
);
59 return decode_threads
;
62 VpxVideoDecoder::VpxVideoDecoder(
63 const scoped_refptr
<base::MessageLoopProxy
>& message_loop
)
64 : message_loop_(message_loop
),
66 state_(kUninitialized
),
68 vpx_codec_alpha_(NULL
) {
71 VpxVideoDecoder::~VpxVideoDecoder() {
72 DCHECK_EQ(kUninitialized
, state_
);
76 void VpxVideoDecoder::Initialize(const VideoDecoderConfig
& config
,
77 const PipelineStatusCB
& status_cb
) {
78 DCHECK(message_loop_
->BelongsToCurrentThread());
79 DCHECK(config
.IsValidConfig());
80 DCHECK(!config
.is_encrypted());
81 DCHECK(decode_cb_
.is_null());
82 DCHECK(reset_cb_
.is_null());
84 weak_this_
= weak_factory_
.GetWeakPtr();
86 if (!ConfigureDecoder(config
)) {
87 status_cb
.Run(DECODER_ERROR_NOT_SUPPORTED
);
94 status_cb
.Run(PIPELINE_OK
);
97 static vpx_codec_ctx
* InitializeVpxContext(vpx_codec_ctx
* context
,
98 const VideoDecoderConfig
& config
) {
99 context
= new vpx_codec_ctx();
100 vpx_codec_dec_cfg_t vpx_config
= {0};
101 vpx_config
.w
= config
.coded_size().width();
102 vpx_config
.h
= config
.coded_size().height();
103 vpx_config
.threads
= GetThreadCount();
105 vpx_codec_err_t status
= vpx_codec_dec_init(context
,
106 config
.codec() == kCodecVP9
?
111 if (status
!= VPX_CODEC_OK
) {
112 LOG(ERROR
) << "vpx_codec_dec_init failed, status=" << status
;
119 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig
& config
) {
120 const CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
121 bool can_handle
= false;
122 if (config
.codec() == kCodecVP9
)
124 if (!cmd_line
->HasSwitch(switches::kDisableVp8AlphaPlayback
) &&
125 config
.codec() == kCodecVP8
&& config
.format() == VideoFrame::YV12A
) {
133 vpx_codec_
= InitializeVpxContext(vpx_codec_
, config
);
137 if (config
.format() == VideoFrame::YV12A
) {
138 vpx_codec_alpha_
= InitializeVpxContext(vpx_codec_alpha_
, config
);
139 if (!vpx_codec_alpha_
)
146 void VpxVideoDecoder::CloseDecoder() {
148 vpx_codec_destroy(vpx_codec_
);
152 if (vpx_codec_alpha_
) {
153 vpx_codec_destroy(vpx_codec_alpha_
);
154 delete vpx_codec_alpha_
;
155 vpx_codec_alpha_
= NULL
;
159 void VpxVideoDecoder::Decode(const scoped_refptr
<DecoderBuffer
>& buffer
,
160 const DecodeCB
& decode_cb
) {
161 DCHECK(message_loop_
->BelongsToCurrentThread());
162 DCHECK(!decode_cb
.is_null());
163 CHECK_NE(state_
, kUninitialized
);
164 CHECK(decode_cb_
.is_null()) << "Overlapping decodes are not supported.";
166 decode_cb_
= BindToCurrentLoop(decode_cb
);
168 if (state_
== kError
) {
169 base::ResetAndReturn(&decode_cb_
).Run(kDecodeError
, NULL
);
173 // Return empty frames if decoding has finished.
174 if (state_
== kDecodeFinished
) {
175 base::ResetAndReturn(&decode_cb_
).Run(kOk
, VideoFrame::CreateEmptyFrame());
179 DecodeBuffer(buffer
);
182 void VpxVideoDecoder::Reset(const base::Closure
& closure
) {
183 DCHECK(message_loop_
->BelongsToCurrentThread());
184 DCHECK(reset_cb_
.is_null());
185 reset_cb_
= BindToCurrentLoop(closure
);
187 // Defer the reset if a decode is pending.
188 if (!decode_cb_
.is_null())
194 void VpxVideoDecoder::Stop(const base::Closure
& closure
) {
195 DCHECK(message_loop_
->BelongsToCurrentThread());
196 base::ScopedClosureRunner
runner(BindToCurrentLoop(closure
));
198 if (state_
== kUninitialized
)
201 if (!decode_cb_
.is_null()) {
202 base::ResetAndReturn(&decode_cb_
).Run(kOk
, NULL
);
203 // Reset is pending only when decode is pending.
204 if (!reset_cb_
.is_null())
205 base::ResetAndReturn(&reset_cb_
).Run();
208 state_
= kUninitialized
;
211 bool VpxVideoDecoder::HasAlpha() const {
212 return vpx_codec_alpha_
!= NULL
;
215 void VpxVideoDecoder::DecodeBuffer(const scoped_refptr
<DecoderBuffer
>& buffer
) {
216 DCHECK(message_loop_
->BelongsToCurrentThread());
217 DCHECK_NE(state_
, kUninitialized
);
218 DCHECK_NE(state_
, kDecodeFinished
);
219 DCHECK_NE(state_
, kError
);
220 DCHECK(reset_cb_
.is_null());
221 DCHECK(!decode_cb_
.is_null());
224 // Transition to kDecodeFinished on the first end of stream buffer.
225 if (state_
== kNormal
&& buffer
->end_of_stream()) {
226 state_
= kDecodeFinished
;
227 base::ResetAndReturn(&decode_cb_
).Run(kOk
, VideoFrame::CreateEmptyFrame());
231 scoped_refptr
<VideoFrame
> video_frame
;
232 if (!VpxDecode(buffer
, &video_frame
)) {
234 base::ResetAndReturn(&decode_cb_
).Run(kDecodeError
, NULL
);
238 // If we didn't get a frame we need more data.
239 if (!video_frame
.get()) {
240 base::ResetAndReturn(&decode_cb_
).Run(kNotEnoughData
, NULL
);
244 base::ResetAndReturn(&decode_cb_
).Run(kOk
, video_frame
);
247 bool VpxVideoDecoder::VpxDecode(const scoped_refptr
<DecoderBuffer
>& buffer
,
248 scoped_refptr
<VideoFrame
>* video_frame
) {
250 DCHECK(!buffer
->end_of_stream());
252 // Pass |buffer| to libvpx.
253 int64 timestamp
= buffer
->timestamp().InMicroseconds();
254 void* user_priv
= reinterpret_cast<void*>(×tamp
);
255 vpx_codec_err_t status
= vpx_codec_decode(vpx_codec_
,
260 if (status
!= VPX_CODEC_OK
) {
261 LOG(ERROR
) << "vpx_codec_decode() failed, status=" << status
;
265 // Gets pointer to decoded data.
266 vpx_codec_iter_t iter
= NULL
;
267 const vpx_image_t
* vpx_image
= vpx_codec_get_frame(vpx_codec_
, &iter
);
273 if (vpx_image
->user_priv
!= reinterpret_cast<void*>(×tamp
)) {
274 LOG(ERROR
) << "Invalid output timestamp.";
278 const vpx_image_t
* vpx_image_alpha
= NULL
;
279 if (vpx_codec_alpha_
&& buffer
->side_data_size() >= 8) {
280 // Pass alpha data to libvpx.
281 int64 timestamp_alpha
= buffer
->timestamp().InMicroseconds();
282 void* user_priv_alpha
= reinterpret_cast<void*>(×tamp_alpha
);
284 // First 8 bytes of side data is side_data_id in big endian.
285 const uint64 side_data_id
= base::NetToHost64(
286 *(reinterpret_cast<const uint64
*>(buffer
->side_data())));
287 if (side_data_id
== 1) {
288 status
= vpx_codec_decode(vpx_codec_alpha_
,
289 buffer
->side_data() + 8,
290 buffer
->side_data_size() - 8,
294 if (status
!= VPX_CODEC_OK
) {
295 LOG(ERROR
) << "vpx_codec_decode() failed on alpha, status=" << status
;
299 // Gets pointer to decoded data.
300 vpx_codec_iter_t iter_alpha
= NULL
;
301 vpx_image_alpha
= vpx_codec_get_frame(vpx_codec_alpha_
, &iter_alpha
);
302 if (!vpx_image_alpha
) {
307 if (vpx_image_alpha
->user_priv
!=
308 reinterpret_cast<void*>(×tamp_alpha
)) {
309 LOG(ERROR
) << "Invalid output timestamp on alpha.";
315 CopyVpxImageTo(vpx_image
, vpx_image_alpha
, video_frame
);
316 (*video_frame
)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp
));
320 void VpxVideoDecoder::DoReset() {
321 DCHECK(decode_cb_
.is_null());
328 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image
* vpx_image
,
329 const struct vpx_image
* vpx_image_alpha
,
330 scoped_refptr
<VideoFrame
>* video_frame
) {
332 CHECK_EQ(vpx_image
->d_w
% 2, 0U);
333 CHECK_EQ(vpx_image
->d_h
% 2, 0U);
334 CHECK(vpx_image
->fmt
== VPX_IMG_FMT_I420
||
335 vpx_image
->fmt
== VPX_IMG_FMT_YV12
);
337 gfx::Size
size(vpx_image
->d_w
, vpx_image
->d_h
);
339 *video_frame
= VideoFrame::CreateFrame(
340 vpx_codec_alpha_
? VideoFrame::YV12A
: VideoFrame::YV12
,
343 config_
.natural_size(),
346 CopyYPlane(vpx_image
->planes
[VPX_PLANE_Y
],
347 vpx_image
->stride
[VPX_PLANE_Y
],
350 CopyUPlane(vpx_image
->planes
[VPX_PLANE_U
],
351 vpx_image
->stride
[VPX_PLANE_U
],
354 CopyVPlane(vpx_image
->planes
[VPX_PLANE_V
],
355 vpx_image
->stride
[VPX_PLANE_V
],
358 if (!vpx_codec_alpha_
)
360 if (!vpx_image_alpha
) {
362 vpx_image
->stride
[VPX_PLANE_Y
], vpx_image
->d_h
, video_frame
->get());
365 CopyAPlane(vpx_image_alpha
->planes
[VPX_PLANE_Y
],
366 vpx_image
->stride
[VPX_PLANE_Y
],