vpx_video_decoder.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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. 4 5#include "media/filters/vpx_video_decoder.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/callback_helpers.h" 13#include "base/command_line.h" 14#include "base/location.h" 15#include "base/logging.h" 16#include "base/single_thread_task_runner.h" 17#include "base/stl_util.h" 18#include "base/strings/string_number_conversions.h" 19#include "base/sys_byteorder.h" 20#include "media/base/bind_to_current_loop.h" 21#include "media/base/decoder_buffer.h" 22#include "media/base/demuxer_stream.h" 23#include "media/base/limits.h" 24#include "media/base/media_switches.h" 25#include "media/base/pipeline.h" 26#include "media/base/video_decoder_config.h" 27#include "media/base/video_frame.h" 28#include "media/base/video_util.h" 29 30// Include libvpx header files. 31// VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide 32// backwards compatibility for legacy applications using the library. 33#define VPX_CODEC_DISABLE_COMPAT 1 34extern "C" { 35#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" 36#include "third_party/libvpx/source/libvpx/vpx/vpx_frame_buffer.h" 37#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" 38} 39 40namespace media { 41 42// Always try to use three threads for video decoding. There is little reason 43// not to since current day CPUs tend to be multi-core and we measured 44// performance benefits on older machines such as P4s with hyperthreading. 45static const int kDecodeThreads = 2; 46static const int kMaxDecodeThreads = 16; 47 48// Returns the number of threads. 49static int GetThreadCount(const VideoDecoderConfig& config) { 50 // Refer to http://crbug.com/93932 for tsan suppressions on decoding. 51 int decode_threads = kDecodeThreads; 52 53 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 54 std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads)); 55 if (threads.empty() || !base::StringToInt(threads, &decode_threads)) { 56 if (config.codec() == kCodecVP9) { 57 // For VP9 decode when using the default thread count, increase the number 58 // of decode threads to equal the maximum number of tiles possible for 59 // higher resolution streams. 60 if (config.coded_size().width() >= 2048) 61 decode_threads = 8; 62 else if (config.coded_size().width() >= 1024) 63 decode_threads = 4; 64 } 65 66 return decode_threads; 67 } 68 69 decode_threads = std::max(decode_threads, 0); 70 decode_threads = std::min(decode_threads, kMaxDecodeThreads); 71 return decode_threads; 72} 73 74// Maximum number of frame buffers that can be used (by both chromium and libvpx 75// combined) for VP9 Decoding. 76// TODO(vigneshv): Investigate if this can be relaxed to a higher number. 77static const uint32 kVP9MaxFrameBuffers = VP9_MAXIMUM_REF_BUFFERS + 78 VPX_MAXIMUM_WORK_BUFFERS + 79 limits::kMaxVideoFrames; 80 81class VpxVideoDecoder::MemoryPool 82 : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> { 83 public: 84 MemoryPool(); 85 86 // Callback that will be called by libvpx when it needs a frame buffer. 87 // Parameters: 88 // |user_priv| Private data passed to libvpx (pointer to memory pool). 89 // |min_size| Minimum size needed by libvpx to decompress the next frame. 90 // |fb| Pointer to the frame buffer to update. 91 // Returns 0 on success. Returns < 0 on failure. 92 static int32 GetVP9FrameBuffer(void* user_priv, size_t min_size, 93 vpx_codec_frame_buffer* fb); 94 95 // Callback that will be called by libvpx when the frame buffer is no longer 96 // being used by libvpx. Parameters: 97 // |user_priv| Private data passed to libvpx (pointer to memory pool). 98 // |fb| Pointer to the frame buffer that's being released. 99 static int32 ReleaseVP9FrameBuffer(void *user_priv, 100 vpx_codec_frame_buffer *fb); 101 102 // Generates a "no_longer_needed" closure that holds a reference 103 // to this pool. 104 base::Closure CreateFrameCallback(void* fb_priv_data); 105 106 private: 107 friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>; 108 ~MemoryPool(); 109 110 // Reference counted frame buffers used for VP9 decoding. Reference counting 111 // is done manually because both chromium and libvpx has to release this 112 // before a buffer can be re-used. 113 struct VP9FrameBuffer { 114 VP9FrameBuffer() : ref_cnt(0) {} 115 std::vector<uint8> data; 116 uint32 ref_cnt; 117 }; 118 119 // Gets the next available frame buffer for use by libvpx. 120 VP9FrameBuffer* GetFreeFrameBuffer(size_t min_size); 121 122 // Method that gets called when a VideoFrame that references this pool gets 123 // destroyed. 124 void OnVideoFrameDestroyed(VP9FrameBuffer* frame_buffer); 125 126 // Frame buffers to be used by libvpx for VP9 Decoding. 127 std::vector<VP9FrameBuffer*> frame_buffers_; 128 129 DISALLOW_COPY_AND_ASSIGN(MemoryPool); 130}; 131 132VpxVideoDecoder::MemoryPool::MemoryPool() {} 133 134VpxVideoDecoder::MemoryPool::~MemoryPool() { 135 STLDeleteElements(&frame_buffers_); 136} 137 138VpxVideoDecoder::MemoryPool::VP9FrameBuffer* 139 VpxVideoDecoder::MemoryPool::GetFreeFrameBuffer(size_t min_size) { 140 // Check if a free frame buffer exists. 141 size_t i = 0; 142 for (; i < frame_buffers_.size(); ++i) { 143 if (frame_buffers_[i]->ref_cnt == 0) 144 break; 145 } 146 147 if (i == frame_buffers_.size()) { 148 // Maximum number of frame buffers reached. 149 if (i == kVP9MaxFrameBuffers) 150 return NULL; 151 152 // Create a new frame buffer. 153 frame_buffers_.push_back(new VP9FrameBuffer()); 154 } 155 156 // Resize the frame buffer if necessary. 157 if (frame_buffers_[i]->data.size() < min_size) 158 frame_buffers_[i]->data.resize(min_size); 159 return frame_buffers_[i]; 160} 161 162int32 VpxVideoDecoder::MemoryPool::GetVP9FrameBuffer( 163 void* user_priv, size_t min_size, vpx_codec_frame_buffer* fb) { 164 DCHECK(user_priv); 165 DCHECK(fb); 166 167 VpxVideoDecoder::MemoryPool* memory_pool = 168 static_cast<VpxVideoDecoder::MemoryPool*>(user_priv); 169 170 VP9FrameBuffer* fb_to_use = memory_pool->GetFreeFrameBuffer(min_size); 171 if (fb_to_use == NULL) 172 return -1; 173 174 fb->data = &fb_to_use->data[0]; 175 fb->size = fb_to_use->data.size(); 176 ++fb_to_use->ref_cnt; 177 178 // Set the frame buffer's private data to point at the external frame buffer. 179 fb->priv = static_cast<void*>(fb_to_use); 180 return 0; 181} 182 183int32 VpxVideoDecoder::MemoryPool::ReleaseVP9FrameBuffer( 184 void *user_priv, vpx_codec_frame_buffer *fb) { 185 VP9FrameBuffer* frame_buffer = static_cast<VP9FrameBuffer*>(fb->priv); 186 --frame_buffer->ref_cnt; 187 return 0; 188} 189 190base::Closure VpxVideoDecoder::MemoryPool::CreateFrameCallback( 191 void* fb_priv_data) { 192 VP9FrameBuffer* frame_buffer = static_cast<VP9FrameBuffer*>(fb_priv_data); 193 ++frame_buffer->ref_cnt; 194 return BindToCurrentLoop( 195 base::Bind(&MemoryPool::OnVideoFrameDestroyed, this, 196 frame_buffer)); 197} 198 199void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed( 200 VP9FrameBuffer* frame_buffer) { 201 --frame_buffer->ref_cnt; 202} 203 204VpxVideoDecoder::VpxVideoDecoder( 205 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) 206 : task_runner_(task_runner), 207 state_(kUninitialized), 208 vpx_codec_(NULL), 209 vpx_codec_alpha_(NULL) {} 210 211VpxVideoDecoder::~VpxVideoDecoder() { 212 DCHECK(task_runner_->BelongsToCurrentThread()); 213 CloseDecoder(); 214} 215 216void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config, 217 bool low_delay, 218 const PipelineStatusCB& status_cb, 219 const OutputCB& output_cb) { 220 DCHECK(task_runner_->BelongsToCurrentThread()); 221 DCHECK(config.IsValidConfig()); 222 DCHECK(!config.is_encrypted()); 223 DCHECK(decode_cb_.is_null()); 224 225 if (!ConfigureDecoder(config)) { 226 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); 227 return; 228 } 229 230 // Success! 231 config_ = config; 232 state_ = kNormal; 233 output_cb_ = BindToCurrentLoop(output_cb); 234 status_cb.Run(PIPELINE_OK); 235} 236 237static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context, 238 const VideoDecoderConfig& config) { 239 context = new vpx_codec_ctx(); 240 vpx_codec_dec_cfg_t vpx_config = {0}; 241 vpx_config.w = config.coded_size().width(); 242 vpx_config.h = config.coded_size().height(); 243 vpx_config.threads = GetThreadCount(config); 244 245 vpx_codec_err_t status = vpx_codec_dec_init(context, 246 config.codec() == kCodecVP9 ? 247 vpx_codec_vp9_dx() : 248 vpx_codec_vp8_dx(), 249 &vpx_config, 250 0); 251 if (status != VPX_CODEC_OK) { 252 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status; 253 delete context; 254 return NULL; 255 } 256 return context; 257} 258 259bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) { 260 if (config.codec() != kCodecVP8 && config.codec() != kCodecVP9) 261 return false; 262 263 // In VP8 videos, only those with alpha are handled by VpxVideoDecoder. All 264 // other VP8 videos go to FFmpegVideoDecoder. 265 if (config.codec() == kCodecVP8 && config.format() != VideoFrame::YV12A) 266 return false; 267 268 CloseDecoder(); 269 270 vpx_codec_ = InitializeVpxContext(vpx_codec_, config); 271 if (!vpx_codec_) 272 return false; 273 274 // We use our own buffers for VP9 so that there is no need to copy data after 275 // decoding. 276 if (config.codec() == kCodecVP9) { 277 memory_pool_ = new MemoryPool(); 278 if (vpx_codec_set_frame_buffer_functions(vpx_codec_, 279 &MemoryPool::GetVP9FrameBuffer, 280 &MemoryPool::ReleaseVP9FrameBuffer, 281 memory_pool_)) { 282 LOG(ERROR) << "Failed to configure external buffers."; 283 return false; 284 } 285 } 286 287 if (config.format() == VideoFrame::YV12A) { 288 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); 289 if (!vpx_codec_alpha_) 290 return false; 291 } 292 293 return true; 294} 295 296void VpxVideoDecoder::CloseDecoder() { 297 if (vpx_codec_) { 298 vpx_codec_destroy(vpx_codec_); 299 delete vpx_codec_; 300 vpx_codec_ = NULL; 301 memory_pool_ = NULL; 302 } 303 if (vpx_codec_alpha_) { 304 vpx_codec_destroy(vpx_codec_alpha_); 305 delete vpx_codec_alpha_; 306 vpx_codec_alpha_ = NULL; 307 } 308} 309 310void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, 311 const DecodeCB& decode_cb) { 312 DCHECK(task_runner_->BelongsToCurrentThread()); 313 DCHECK(!decode_cb.is_null()); 314 CHECK_NE(state_, kUninitialized); 315 CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; 316 317 decode_cb_ = BindToCurrentLoop(decode_cb); 318 319 if (state_ == kError) { 320 base::ResetAndReturn(&decode_cb_).Run(kDecodeError); 321 return; 322 } 323 324 // Return empty frames if decoding has finished. 325 if (state_ == kDecodeFinished) { 326 base::ResetAndReturn(&decode_cb_).Run(kOk); 327 return; 328 } 329 330 DecodeBuffer(buffer); 331} 332 333void VpxVideoDecoder::Reset(const base::Closure& closure) { 334 DCHECK(task_runner_->BelongsToCurrentThread()); 335 DCHECK(decode_cb_.is_null()); 336 337 state_ = kNormal; 338 task_runner_->PostTask(FROM_HERE, closure); 339} 340 341void VpxVideoDecoder::DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer) { 342 DCHECK(task_runner_->BelongsToCurrentThread()); 343 DCHECK_NE(state_, kUninitialized); 344 DCHECK_NE(state_, kDecodeFinished); 345 DCHECK_NE(state_, kError); 346 DCHECK(!decode_cb_.is_null()); 347 DCHECK(buffer); 348 349 // Transition to kDecodeFinished on the first end of stream buffer. 350 if (state_ == kNormal && buffer->end_of_stream()) { 351 state_ = kDecodeFinished; 352 base::ResetAndReturn(&decode_cb_).Run(kOk); 353 return; 354 } 355 356 scoped_refptr<VideoFrame> video_frame; 357 if (!VpxDecode(buffer, &video_frame)) { 358 state_ = kError; 359 base::ResetAndReturn(&decode_cb_).Run(kDecodeError); 360 return; 361 } 362 363 base::ResetAndReturn(&decode_cb_).Run(kOk); 364 365 if (video_frame) 366 output_cb_.Run(video_frame); 367} 368 369bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer, 370 scoped_refptr<VideoFrame>* video_frame) { 371 DCHECK(video_frame); 372 DCHECK(!buffer->end_of_stream()); 373 374 // Pass |buffer| to libvpx. 375 int64 timestamp = buffer->timestamp().InMicroseconds(); 376 void* user_priv = reinterpret_cast<void*>(×tamp); 377 vpx_codec_err_t status = vpx_codec_decode(vpx_codec_, 378 buffer->data(), 379 buffer->data_size(), 380 user_priv, 381 0); 382 if (status != VPX_CODEC_OK) { 383 LOG(ERROR) << "vpx_codec_decode() failed, status=" << status; 384 return false; 385 } 386 387 // Gets pointer to decoded data. 388 vpx_codec_iter_t iter = NULL; 389 const vpx_image_t* vpx_image = vpx_codec_get_frame(vpx_codec_, &iter); 390 if (!vpx_image) { 391 *video_frame = NULL; 392 return true; 393 } 394 395 if (vpx_image->user_priv != reinterpret_cast<void*>(×tamp)) { 396 LOG(ERROR) << "Invalid output timestamp."; 397 return false; 398 } 399 400 const vpx_image_t* vpx_image_alpha = NULL; 401 if (vpx_codec_alpha_ && buffer->side_data_size() >= 8) { 402 // Pass alpha data to libvpx. 403 int64 timestamp_alpha = buffer->timestamp().InMicroseconds(); 404 void* user_priv_alpha = reinterpret_cast<void*>(×tamp_alpha); 405 406 // First 8 bytes of side data is side_data_id in big endian. 407 const uint64 side_data_id = base::NetToHost64( 408 *(reinterpret_cast<const uint64*>(buffer->side_data()))); 409 if (side_data_id == 1) { 410 status = vpx_codec_decode(vpx_codec_alpha_, 411 buffer->side_data() + 8, 412 buffer->side_data_size() - 8, 413 user_priv_alpha, 414 0); 415 416 if (status != VPX_CODEC_OK) { 417 LOG(ERROR) << "vpx_codec_decode() failed on alpha, status=" << status; 418 return false; 419 } 420 421 // Gets pointer to decoded data. 422 vpx_codec_iter_t iter_alpha = NULL; 423 vpx_image_alpha = vpx_codec_get_frame(vpx_codec_alpha_, &iter_alpha); 424 if (!vpx_image_alpha) { 425 *video_frame = NULL; 426 return true; 427 } 428 429 if (vpx_image_alpha->user_priv != 430 reinterpret_cast<void*>(×tamp_alpha)) { 431 LOG(ERROR) << "Invalid output timestamp on alpha."; 432 return false; 433 } 434 } 435 } 436 437 CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame); 438 (*video_frame)->set_timestamp(base::TimeDelta::FromMicroseconds(timestamp)); 439 return true; 440} 441 442void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, 443 const struct vpx_image* vpx_image_alpha, 444 scoped_refptr<VideoFrame>* video_frame) { 445 CHECK(vpx_image); 446 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || 447 vpx_image->fmt == VPX_IMG_FMT_YV12 || 448 vpx_image->fmt == VPX_IMG_FMT_I444); 449 450 VideoFrame::Format codec_format = VideoFrame::YV12; 451 int uv_rows = (vpx_image->d_h + 1) / 2; 452 453 if (vpx_image->fmt == VPX_IMG_FMT_I444) { 454 CHECK(!vpx_codec_alpha_); 455 codec_format = VideoFrame::YV24; 456 uv_rows = vpx_image->d_h; 457 } else if (vpx_codec_alpha_) { 458 codec_format = VideoFrame::YV12A; 459 } 460 461 gfx::Size size(vpx_image->d_w, vpx_image->d_h); 462 463 if (!vpx_codec_alpha_ && memory_pool_) { 464 *video_frame = VideoFrame::WrapExternalYuvData( 465 codec_format, 466 size, gfx::Rect(size), config_.natural_size(), 467 vpx_image->stride[VPX_PLANE_Y], 468 vpx_image->stride[VPX_PLANE_U], 469 vpx_image->stride[VPX_PLANE_V], 470 vpx_image->planes[VPX_PLANE_Y], 471 vpx_image->planes[VPX_PLANE_U], 472 vpx_image->planes[VPX_PLANE_V], 473 kNoTimestamp(), 474 memory_pool_->CreateFrameCallback(vpx_image->fb_priv)); 475 return; 476 } 477 478 *video_frame = frame_pool_.CreateFrame( 479 codec_format, 480 size, 481 gfx::Rect(size), 482 config_.natural_size(), 483 kNoTimestamp()); 484 485 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], 486 vpx_image->stride[VPX_PLANE_Y], 487 vpx_image->d_h, 488 video_frame->get()); 489 CopyUPlane(vpx_image->planes[VPX_PLANE_U], 490 vpx_image->stride[VPX_PLANE_U], 491 uv_rows, 492 video_frame->get()); 493 CopyVPlane(vpx_image->planes[VPX_PLANE_V], 494 vpx_image->stride[VPX_PLANE_V], 495 uv_rows, 496 video_frame->get()); 497 if (!vpx_codec_alpha_) 498 return; 499 if (!vpx_image_alpha) { 500 MakeOpaqueAPlane( 501 vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get()); 502 return; 503 } 504 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y], 505 vpx_image->stride[VPX_PLANE_Y], 506 vpx_image->d_h, 507 video_frame->get()); 508} 509 510} // namespace media 511