Home | History | Annotate | Download | only in filters
      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 
     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"
     26 
     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"
     34 }
     35 
     36 namespace media {
     37 
     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;
     43 
     44 // Returns the number of threads.
     45 static int GetThreadCount() {
     46   // TODO(scherkus): De-duplicate this function and the one used by
     47   // FFmpegVideoDecoder.
     48 
     49   // Refer to http://crbug.com/93932 for tsan suppressions on decoding.
     50   int decode_threads = kDecodeThreads;
     51 
     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;
     56 
     57   decode_threads = std::max(decode_threads, 0);
     58   decode_threads = std::min(decode_threads, kMaxDecodeThreads);
     59   return decode_threads;
     60 }
     61 
     62 VpxVideoDecoder::VpxVideoDecoder(
     63     const scoped_refptr<base::MessageLoopProxy>& message_loop)
     64     : message_loop_(message_loop),
     65       weak_factory_(this),
     66       state_(kUninitialized),
     67       vpx_codec_(NULL),
     68       vpx_codec_alpha_(NULL) {
     69 }
     70 
     71 VpxVideoDecoder::~VpxVideoDecoder() {
     72   DCHECK_EQ(kUninitialized, state_);
     73   CloseDecoder();
     74 }
     75 
     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());
     83 
     84   weak_this_ = weak_factory_.GetWeakPtr();
     85 
     86   if (!ConfigureDecoder(config)) {
     87     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
     88     return;
     89   }
     90 
     91   // Success!
     92   config_ = config;
     93   state_ = kNormal;
     94   status_cb.Run(PIPELINE_OK);
     95 }
     96 
     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();
    104 
    105   vpx_codec_err_t status = vpx_codec_dec_init(context,
    106                                               config.codec() == kCodecVP9 ?
    107                                                   vpx_codec_vp9_dx() :
    108                                                   vpx_codec_vp8_dx(),
    109                                               &vpx_config,
    110                                               0);
    111   if (status != VPX_CODEC_OK) {
    112     LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
    113     delete context;
    114     return NULL;
    115   }
    116   return context;
    117 }
    118 
    119 bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
    120   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
    121   bool can_handle = false;
    122   if (config.codec() == kCodecVP9)
    123     can_handle = true;
    124   if (cmd_line->HasSwitch(switches::kEnableVp8AlphaPlayback) &&
    125       config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) {
    126     can_handle = true;
    127   }
    128   if (!can_handle)
    129     return false;
    130 
    131   CloseDecoder();
    132 
    133   vpx_codec_ = InitializeVpxContext(vpx_codec_, config);
    134   if (!vpx_codec_)
    135     return false;
    136 
    137   if (config.format() == VideoFrame::YV12A) {
    138     vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config);
    139     if (!vpx_codec_alpha_)
    140       return false;
    141   }
    142 
    143   return true;
    144 }
    145 
    146 void VpxVideoDecoder::CloseDecoder() {
    147   if (vpx_codec_) {
    148     vpx_codec_destroy(vpx_codec_);
    149     delete vpx_codec_;
    150     vpx_codec_ = NULL;
    151   }
    152   if (vpx_codec_alpha_) {
    153     vpx_codec_destroy(vpx_codec_alpha_);
    154     delete vpx_codec_alpha_;
    155     vpx_codec_alpha_ = NULL;
    156   }
    157 }
    158 
    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.";
    165 
    166   decode_cb_ = BindToCurrentLoop(decode_cb);
    167 
    168   if (state_ == kError) {
    169     base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL);
    170     return;
    171   }
    172 
    173   // Return empty frames if decoding has finished.
    174   if (state_ == kDecodeFinished) {
    175     base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEmptyFrame());
    176     return;
    177   }
    178 
    179   DecodeBuffer(buffer);
    180 }
    181 
    182 void VpxVideoDecoder::Reset(const base::Closure& closure) {
    183   DCHECK(message_loop_->BelongsToCurrentThread());
    184   DCHECK(reset_cb_.is_null());
    185   reset_cb_ = BindToCurrentLoop(closure);
    186 
    187   // Defer the reset if a decode is pending.
    188   if (!decode_cb_.is_null())
    189     return;
    190 
    191   DoReset();
    192 }
    193 
    194 void VpxVideoDecoder::Stop(const base::Closure& closure) {
    195   DCHECK(message_loop_->BelongsToCurrentThread());
    196   base::ScopedClosureRunner runner(BindToCurrentLoop(closure));
    197 
    198   if (state_ == kUninitialized)
    199     return;
    200 
    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();
    206   }
    207 
    208   state_ = kUninitialized;
    209 }
    210 
    211 bool VpxVideoDecoder::HasAlpha() const {
    212   return vpx_codec_alpha_ != NULL;
    213 }
    214 
    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());
    222   DCHECK(buffer);
    223 
    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());
    228     return;
    229   }
    230 
    231   scoped_refptr<VideoFrame> video_frame;
    232   if (!VpxDecode(buffer, &video_frame)) {
    233     state_ = kError;
    234     base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL);
    235     return;
    236   }
    237 
    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);
    241     return;
    242   }
    243 
    244   base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame);
    245 }
    246 
    247 bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
    248                                 scoped_refptr<VideoFrame>* video_frame) {
    249   DCHECK(video_frame);
    250   DCHECK(!buffer->end_of_stream());
    251 
    252   // Pass |buffer| to libvpx.
    253   int64 timestamp = buffer->timestamp().InMicroseconds();
    254   void* user_priv = reinterpret_cast<void*>(&timestamp);
    255   vpx_codec_err_t status = vpx_codec_decode(vpx_codec_,
    256                                             buffer->data(),
    257                                             buffer->data_size(),
    258                                             user_priv,
    259                                             0);
    260   if (status != VPX_CODEC_OK) {
    261     LOG(ERROR) << "vpx_codec_decode() failed, status=" << status;
    262     return false;
    263   }
    264 
    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);
    268   if (!vpx_image) {
    269     *video_frame = NULL;
    270     return true;
    271   }
    272 
    273   if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) {
    274     LOG(ERROR) << "Invalid output timestamp.";
    275     return false;
    276   }
    277 
    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*>(&timestamp_alpha);
    283 
    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,
    291                                 user_priv_alpha,
    292                                 0);
    293 
    294       if (status != VPX_CODEC_OK) {
    295         LOG(ERROR) << "vpx_codec_decode() failed on alpha, status=" << status;
    296         return false;
    297       }
    298 
    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) {
    303         *video_frame = NULL;
    304         return true;
    305       }
    306 
    307       if (vpx_image_alpha->user_priv !=
    308           reinterpret_cast<void*>(&timestamp_alpha)) {
    309         LOG(ERROR) << "Invalid output timestamp on alpha.";
    310         return false;
    311       }
    312     }
    313   }
    314 
    315   CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame);
    316   (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp));
    317   return true;
    318 }
    319 
    320 void VpxVideoDecoder::DoReset() {
    321   DCHECK(decode_cb_.is_null());
    322 
    323   state_ = kNormal;
    324   reset_cb_.Run();
    325   reset_cb_.Reset();
    326 }
    327 
    328 void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image,
    329                                      const struct vpx_image* vpx_image_alpha,
    330                                      scoped_refptr<VideoFrame>* video_frame) {
    331   CHECK(vpx_image);
    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);
    336 
    337   gfx::Size size(vpx_image->d_w, vpx_image->d_h);
    338 
    339   *video_frame = VideoFrame::CreateFrame(
    340       vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12,
    341       size,
    342       gfx::Rect(size),
    343       config_.natural_size(),
    344       kNoTimestamp());
    345 
    346   CopyYPlane(vpx_image->planes[VPX_PLANE_Y],
    347              vpx_image->stride[VPX_PLANE_Y],
    348              vpx_image->d_h,
    349              video_frame->get());
    350   CopyUPlane(vpx_image->planes[VPX_PLANE_U],
    351              vpx_image->stride[VPX_PLANE_U],
    352              vpx_image->d_h / 2,
    353              video_frame->get());
    354   CopyVPlane(vpx_image->planes[VPX_PLANE_V],
    355              vpx_image->stride[VPX_PLANE_V],
    356              vpx_image->d_h / 2,
    357              video_frame->get());
    358   if (!vpx_codec_alpha_)
    359     return;
    360   if (!vpx_image_alpha) {
    361     MakeOpaqueAPlane(
    362         vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get());
    363     return;
    364   }
    365   CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
    366              vpx_image->stride[VPX_PLANE_Y],
    367              vpx_image->d_h,
    368              video_frame->get());
    369 }
    370 
    371 }  // namespace media
    372