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/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/sys_byteorder.h"
18 #include "media/base/bind_to_current_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(const VideoDecoderConfig
& config
) {
46 // Refer to http://crbug.com/93932 for tsan suppressions on decoding.
47 int decode_threads
= kDecodeThreads
;
49 const CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
50 std::string
threads(cmd_line
->GetSwitchValueASCII(switches::kVideoThreads
));
51 if (threads
.empty() || !base::StringToInt(threads
, &decode_threads
)) {
52 if (config
.codec() == kCodecVP9
) {
53 // For VP9 decode when using the default thread count, increase the number
54 // of decode threads to equal the maximum number of tiles possible for
55 // higher resolution streams.
56 if (config
.coded_size().width() >= 2048)
58 else if (config
.coded_size().width() >= 1024)
62 return decode_threads
;
65 decode_threads
= std::max(decode_threads
, 0);
66 decode_threads
= std::min(decode_threads
, kMaxDecodeThreads
);
67 return decode_threads
;
70 VpxVideoDecoder::VpxVideoDecoder(
71 const scoped_refptr
<base::SingleThreadTaskRunner
>& task_runner
)
72 : task_runner_(task_runner
),
74 state_(kUninitialized
),
76 vpx_codec_alpha_(NULL
) {
79 VpxVideoDecoder::~VpxVideoDecoder() {
80 DCHECK_EQ(kUninitialized
, state_
);
84 void VpxVideoDecoder::Initialize(const VideoDecoderConfig
& config
,
85 const PipelineStatusCB
& status_cb
) {
86 DCHECK(task_runner_
->BelongsToCurrentThread());
87 DCHECK(config
.IsValidConfig());
88 DCHECK(!config
.is_encrypted());
89 DCHECK(decode_cb_
.is_null());
90 DCHECK(reset_cb_
.is_null());
92 weak_this_
= weak_factory_
.GetWeakPtr();
94 if (!ConfigureDecoder(config
)) {
95 status_cb
.Run(DECODER_ERROR_NOT_SUPPORTED
);
102 status_cb
.Run(PIPELINE_OK
);
105 static vpx_codec_ctx
* InitializeVpxContext(vpx_codec_ctx
* context
,
106 const VideoDecoderConfig
& config
) {
107 context
= new vpx_codec_ctx();
108 vpx_codec_dec_cfg_t vpx_config
= {0};
109 vpx_config
.w
= config
.coded_size().width();
110 vpx_config
.h
= config
.coded_size().height();
111 vpx_config
.threads
= GetThreadCount(config
);
113 vpx_codec_err_t status
= vpx_codec_dec_init(context
,
114 config
.codec() == kCodecVP9
?
119 if (status
!= VPX_CODEC_OK
) {
120 LOG(ERROR
) << "vpx_codec_dec_init failed, status=" << status
;
127 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig
& config
) {
128 const CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
129 bool can_handle
= false;
130 if (config
.codec() == kCodecVP9
)
132 if (!cmd_line
->HasSwitch(switches::kDisableVp8AlphaPlayback
) &&
133 config
.codec() == kCodecVP8
&& config
.format() == VideoFrame::YV12A
) {
141 vpx_codec_
= InitializeVpxContext(vpx_codec_
, config
);
145 if (config
.format() == VideoFrame::YV12A
) {
146 vpx_codec_alpha_
= InitializeVpxContext(vpx_codec_alpha_
, config
);
147 if (!vpx_codec_alpha_
)
154 void VpxVideoDecoder::CloseDecoder() {
156 vpx_codec_destroy(vpx_codec_
);
160 if (vpx_codec_alpha_
) {
161 vpx_codec_destroy(vpx_codec_alpha_
);
162 delete vpx_codec_alpha_
;
163 vpx_codec_alpha_
= NULL
;
167 void VpxVideoDecoder::Decode(const scoped_refptr
<DecoderBuffer
>& buffer
,
168 const DecodeCB
& decode_cb
) {
169 DCHECK(task_runner_
->BelongsToCurrentThread());
170 DCHECK(!decode_cb
.is_null());
171 CHECK_NE(state_
, kUninitialized
);
172 CHECK(decode_cb_
.is_null()) << "Overlapping decodes are not supported.";
174 decode_cb_
= BindToCurrentLoop(decode_cb
);
176 if (state_
== kError
) {
177 base::ResetAndReturn(&decode_cb_
).Run(kDecodeError
, NULL
);
181 // Return empty frames if decoding has finished.
182 if (state_
== kDecodeFinished
) {
183 base::ResetAndReturn(&decode_cb_
).Run(kOk
, VideoFrame::CreateEOSFrame());
187 DecodeBuffer(buffer
);
190 void VpxVideoDecoder::Reset(const base::Closure
& closure
) {
191 DCHECK(task_runner_
->BelongsToCurrentThread());
192 DCHECK(reset_cb_
.is_null());
193 reset_cb_
= BindToCurrentLoop(closure
);
195 // Defer the reset if a decode is pending.
196 if (!decode_cb_
.is_null())
202 void VpxVideoDecoder::Stop(const base::Closure
& closure
) {
203 DCHECK(task_runner_
->BelongsToCurrentThread());
204 base::ScopedClosureRunner
runner(BindToCurrentLoop(closure
));
206 if (state_
== kUninitialized
)
209 if (!decode_cb_
.is_null()) {
210 base::ResetAndReturn(&decode_cb_
).Run(kAborted
, NULL
);
211 // Reset is pending only when decode is pending.
212 if (!reset_cb_
.is_null())
213 base::ResetAndReturn(&reset_cb_
).Run();
216 state_
= kUninitialized
;
219 bool VpxVideoDecoder::HasAlpha() const {
220 return vpx_codec_alpha_
!= NULL
;
223 void VpxVideoDecoder::DecodeBuffer(const scoped_refptr
<DecoderBuffer
>& buffer
) {
224 DCHECK(task_runner_
->BelongsToCurrentThread());
225 DCHECK_NE(state_
, kUninitialized
);
226 DCHECK_NE(state_
, kDecodeFinished
);
227 DCHECK_NE(state_
, kError
);
228 DCHECK(reset_cb_
.is_null());
229 DCHECK(!decode_cb_
.is_null());
232 // Transition to kDecodeFinished on the first end of stream buffer.
233 if (state_
== kNormal
&& buffer
->end_of_stream()) {
234 state_
= kDecodeFinished
;
235 base::ResetAndReturn(&decode_cb_
).Run(kOk
, VideoFrame::CreateEOSFrame());
239 scoped_refptr
<VideoFrame
> video_frame
;
240 if (!VpxDecode(buffer
, &video_frame
)) {
242 base::ResetAndReturn(&decode_cb_
).Run(kDecodeError
, NULL
);
246 // If we didn't get a frame we need more data.
247 if (!video_frame
.get()) {
248 base::ResetAndReturn(&decode_cb_
).Run(kNotEnoughData
, NULL
);
252 base::ResetAndReturn(&decode_cb_
).Run(kOk
, video_frame
);
255 bool VpxVideoDecoder::VpxDecode(const scoped_refptr
<DecoderBuffer
>& buffer
,
256 scoped_refptr
<VideoFrame
>* video_frame
) {
258 DCHECK(!buffer
->end_of_stream());
260 // Pass |buffer| to libvpx.
261 int64 timestamp
= buffer
->timestamp().InMicroseconds();
262 void* user_priv
= reinterpret_cast<void*>(×tamp
);
263 vpx_codec_err_t status
= vpx_codec_decode(vpx_codec_
,
268 if (status
!= VPX_CODEC_OK
) {
269 LOG(ERROR
) << "vpx_codec_decode() failed, status=" << status
;
273 // Gets pointer to decoded data.
274 vpx_codec_iter_t iter
= NULL
;
275 const vpx_image_t
* vpx_image
= vpx_codec_get_frame(vpx_codec_
, &iter
);
281 if (vpx_image
->user_priv
!= reinterpret_cast<void*>(×tamp
)) {
282 LOG(ERROR
) << "Invalid output timestamp.";
286 const vpx_image_t
* vpx_image_alpha
= NULL
;
287 if (vpx_codec_alpha_
&& buffer
->side_data_size() >= 8) {
288 // Pass alpha data to libvpx.
289 int64 timestamp_alpha
= buffer
->timestamp().InMicroseconds();
290 void* user_priv_alpha
= reinterpret_cast<void*>(×tamp_alpha
);
292 // First 8 bytes of side data is side_data_id in big endian.
293 const uint64 side_data_id
= base::NetToHost64(
294 *(reinterpret_cast<const uint64
*>(buffer
->side_data())));
295 if (side_data_id
== 1) {
296 status
= vpx_codec_decode(vpx_codec_alpha_
,
297 buffer
->side_data() + 8,
298 buffer
->side_data_size() - 8,
302 if (status
!= VPX_CODEC_OK
) {
303 LOG(ERROR
) << "vpx_codec_decode() failed on alpha, status=" << status
;
307 // Gets pointer to decoded data.
308 vpx_codec_iter_t iter_alpha
= NULL
;
309 vpx_image_alpha
= vpx_codec_get_frame(vpx_codec_alpha_
, &iter_alpha
);
310 if (!vpx_image_alpha
) {
315 if (vpx_image_alpha
->user_priv
!=
316 reinterpret_cast<void*>(×tamp_alpha
)) {
317 LOG(ERROR
) << "Invalid output timestamp on alpha.";
323 CopyVpxImageTo(vpx_image
, vpx_image_alpha
, video_frame
);
324 (*video_frame
)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp
));
328 void VpxVideoDecoder::DoReset() {
329 DCHECK(decode_cb_
.is_null());
336 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image
* vpx_image
,
337 const struct vpx_image
* vpx_image_alpha
,
338 scoped_refptr
<VideoFrame
>* video_frame
) {
340 CHECK(vpx_image
->fmt
== VPX_IMG_FMT_I420
||
341 vpx_image
->fmt
== VPX_IMG_FMT_YV12
);
343 gfx::Size
size(vpx_image
->d_w
, vpx_image
->d_h
);
345 *video_frame
= frame_pool_
.CreateFrame(
346 vpx_codec_alpha_
? VideoFrame::YV12A
: VideoFrame::YV12
,
349 config_
.natural_size(),
352 CopyYPlane(vpx_image
->planes
[VPX_PLANE_Y
],
353 vpx_image
->stride
[VPX_PLANE_Y
],
356 CopyUPlane(vpx_image
->planes
[VPX_PLANE_U
],
357 vpx_image
->stride
[VPX_PLANE_U
],
358 (vpx_image
->d_h
+ 1) / 2,
360 CopyVPlane(vpx_image
->planes
[VPX_PLANE_V
],
361 vpx_image
->stride
[VPX_PLANE_V
],
362 (vpx_image
->d_h
+ 1) / 2,
364 if (!vpx_codec_alpha_
)
366 if (!vpx_image_alpha
) {
368 vpx_image
->stride
[VPX_PLANE_Y
], vpx_image
->d_h
, video_frame
->get());
371 CopyAPlane(vpx_image_alpha
->planes
[VPX_PLANE_Y
],
372 vpx_image
->stride
[VPX_PLANE_Y
],