Disable view source for Developer Tools.
[chromium-blink-merge.git] / media / filters / vpx_video_decoder.cc
blob7d71d787d56f3f192ed8ca3eeb3fe117bc953f8b
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"
7 #include <algorithm>
8 #include <string>
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
31 extern "C" {
32 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
33 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
36 namespace media {
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)
57 decode_threads = 8;
58 else if (config.coded_size().width() >= 1024)
59 decode_threads = 4;
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),
73 weak_factory_(this),
74 state_(kUninitialized),
75 vpx_codec_(NULL),
76 vpx_codec_alpha_(NULL) {
79 VpxVideoDecoder::~VpxVideoDecoder() {
80 DCHECK_EQ(kUninitialized, state_);
81 CloseDecoder();
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);
96 return;
99 // Success!
100 config_ = config;
101 state_ = kNormal;
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 ?
115 vpx_codec_vp9_dx() :
116 vpx_codec_vp8_dx(),
117 &vpx_config,
119 if (status != VPX_CODEC_OK) {
120 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
121 delete context;
122 return NULL;
124 return context;
127 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
128 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
129 bool can_handle = false;
130 if (config.codec() == kCodecVP9)
131 can_handle = true;
132 if (!cmd_line->HasSwitch(switches::kDisableVp8AlphaPlayback) &&
133 config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) {
134 can_handle = true;
136 if (!can_handle)
137 return false;
139 CloseDecoder();
141 vpx_codec_ = InitializeVpxContext(vpx_codec_, config);
142 if (!vpx_codec_)
143 return false;
145 if (config.format() == VideoFrame::YV12A) {
146 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config);
147 if (!vpx_codec_alpha_)
148 return false;
151 return true;
154 void VpxVideoDecoder::CloseDecoder() {
155 if (vpx_codec_) {
156 vpx_codec_destroy(vpx_codec_);
157 delete vpx_codec_;
158 vpx_codec_ = NULL;
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);
178 return;
181 // Return empty frames if decoding has finished.
182 if (state_ == kDecodeFinished) {
183 base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame());
184 return;
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())
197 return;
199 DoReset();
202 void VpxVideoDecoder::Stop(const base::Closure& closure) {
203 DCHECK(task_runner_->BelongsToCurrentThread());
204 base::ScopedClosureRunner runner(BindToCurrentLoop(closure));
206 if (state_ == kUninitialized)
207 return;
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());
230 DCHECK(buffer);
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());
236 return;
239 scoped_refptr<VideoFrame> video_frame;
240 if (!VpxDecode(buffer, &video_frame)) {
241 state_ = kError;
242 base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL);
243 return;
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);
249 return;
252 base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame);
255 bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
256 scoped_refptr<VideoFrame>* video_frame) {
257 DCHECK(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*>(&timestamp);
263 vpx_codec_err_t status = vpx_codec_decode(vpx_codec_,
264 buffer->data(),
265 buffer->data_size(),
266 user_priv,
268 if (status != VPX_CODEC_OK) {
269 LOG(ERROR) << "vpx_codec_decode() failed, status=" << status;
270 return false;
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);
276 if (!vpx_image) {
277 *video_frame = NULL;
278 return true;
281 if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) {
282 LOG(ERROR) << "Invalid output timestamp.";
283 return false;
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*>(&timestamp_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,
299 user_priv_alpha,
302 if (status != VPX_CODEC_OK) {
303 LOG(ERROR) << "vpx_codec_decode() failed on alpha, status=" << status;
304 return false;
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) {
311 *video_frame = NULL;
312 return true;
315 if (vpx_image_alpha->user_priv !=
316 reinterpret_cast<void*>(&timestamp_alpha)) {
317 LOG(ERROR) << "Invalid output timestamp on alpha.";
318 return false;
323 CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame);
324 (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp));
325 return true;
328 void VpxVideoDecoder::DoReset() {
329 DCHECK(decode_cb_.is_null());
331 state_ = kNormal;
332 reset_cb_.Run();
333 reset_cb_.Reset();
336 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image,
337 const struct vpx_image* vpx_image_alpha,
338 scoped_refptr<VideoFrame>* video_frame) {
339 CHECK(vpx_image);
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,
347 size,
348 gfx::Rect(size),
349 config_.natural_size(),
350 kNoTimestamp());
352 CopyYPlane(vpx_image->planes[VPX_PLANE_Y],
353 vpx_image->stride[VPX_PLANE_Y],
354 vpx_image->d_h,
355 video_frame->get());
356 CopyUPlane(vpx_image->planes[VPX_PLANE_U],
357 vpx_image->stride[VPX_PLANE_U],
358 (vpx_image->d_h + 1) / 2,
359 video_frame->get());
360 CopyVPlane(vpx_image->planes[VPX_PLANE_V],
361 vpx_image->stride[VPX_PLANE_V],
362 (vpx_image->d_h + 1) / 2,
363 video_frame->get());
364 if (!vpx_codec_alpha_)
365 return;
366 if (!vpx_image_alpha) {
367 MakeOpaqueAPlane(
368 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get());
369 return;
371 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
372 vpx_image->stride[VPX_PLANE_Y],
373 vpx_image->d_h,
374 video_frame->get());
377 } // namespace media