Home | History | Annotate | Download | only in ppapi
      1 // Copyright 2013 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/cdm/ppapi/libvpx_cdm_video_decoder.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "media/base/buffers.h"
     10 #include "media/base/limits.h"
     11 
     12 // Include libvpx header files.
     13 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
     14 // backwards compatibility for legacy applications using the library.
     15 #define VPX_CODEC_DISABLE_COMPAT 1
     16 extern "C" {
     17 // Note: vpx_decoder.h must be first or compile will fail.
     18 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"  // NOLINT
     19 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
     20 }
     21 
     22 // Enable USE_COPYPLANE_WITH_LIBVPX to use |CopyPlane()| instead of memcpy to
     23 // copy video frame data.
     24 // #define USE_COPYPLANE_WITH_LIBVPX 1
     25 
     26 namespace media {
     27 
     28 static const int kDecodeThreads = 2;
     29 
     30 LibvpxCdmVideoDecoder::LibvpxCdmVideoDecoder(CdmHost* host)
     31     : is_initialized_(false),
     32       host_(host),
     33       vpx_codec_(NULL),
     34       vpx_image_(NULL) {
     35 }
     36 
     37 LibvpxCdmVideoDecoder::~LibvpxCdmVideoDecoder() {
     38   Deinitialize();
     39 }
     40 
     41 bool LibvpxCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) {
     42   DVLOG(1) << "Initialize()";
     43 
     44   if (!IsValidOutputConfig(config.format, config.coded_size)) {
     45     LOG(ERROR) << "Initialize(): invalid video decoder configuration.";
     46     return false;
     47   }
     48 
     49   if (is_initialized_) {
     50     LOG(ERROR) << "Initialize(): Already initialized.";
     51     return false;
     52   }
     53 
     54   vpx_codec_ = new vpx_codec_ctx();
     55   vpx_codec_dec_cfg_t vpx_config = {0};
     56   vpx_config.w = config.coded_size.width;
     57   vpx_config.h = config.coded_size.height;
     58   vpx_config.threads = kDecodeThreads;
     59 
     60   vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_,
     61                                               vpx_codec_vp8_dx(),
     62                                               &vpx_config,
     63                                               0);
     64   if (status != VPX_CODEC_OK) {
     65     LOG(ERROR) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret="
     66                << status;
     67     delete vpx_codec_;
     68     vpx_codec_ = NULL;
     69   }
     70 
     71   is_initialized_ = true;
     72   return true;
     73 }
     74 
     75 void LibvpxCdmVideoDecoder::Deinitialize() {
     76   DVLOG(1) << "Deinitialize()";
     77 
     78   if (vpx_codec_) {
     79     vpx_codec_destroy(vpx_codec_);
     80     vpx_codec_ = NULL;
     81   }
     82 
     83   is_initialized_ = false;
     84 }
     85 
     86 void LibvpxCdmVideoDecoder::Reset() {
     87   DVLOG(1) << "Reset()";
     88 }
     89 
     90 // static
     91 bool LibvpxCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format,
     92                                                 const cdm::Size& data_size) {
     93   return ((format == cdm::kYv12 || format == cdm::kI420) &&
     94           (data_size.width % 2) == 0 && (data_size.height % 2) == 0 &&
     95           data_size.width > 0 && data_size.height > 0 &&
     96           data_size.width <= limits::kMaxDimension &&
     97           data_size.height <= limits::kMaxDimension &&
     98           data_size.width * data_size.height <= limits::kMaxCanvas);
     99 }
    100 
    101 cdm::Status LibvpxCdmVideoDecoder::DecodeFrame(
    102     const uint8_t* compressed_frame,
    103     int32_t compressed_frame_size,
    104     int64_t timestamp,
    105     cdm::VideoFrame* decoded_frame) {
    106   DVLOG(1) << "DecodeFrame()";
    107   DCHECK(decoded_frame);
    108 
    109   // Pass |compressed_frame| to libvpx.
    110   void* user_priv = reinterpret_cast<void*>(&timestamp);
    111   vpx_codec_err_t status = vpx_codec_decode(vpx_codec_,
    112                                             compressed_frame,
    113                                             compressed_frame_size,
    114                                             user_priv,
    115                                             0);
    116   if (status != VPX_CODEC_OK) {
    117     LOG(ERROR) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status="
    118                << status;
    119     return cdm::kDecodeError;
    120   }
    121 
    122   // Gets pointer to decoded data.
    123   vpx_codec_iter_t iter = NULL;
    124   vpx_image_ = vpx_codec_get_frame(vpx_codec_, &iter);
    125   if (!vpx_image_)
    126     return cdm::kNeedMoreData;
    127 
    128   if (vpx_image_->user_priv != reinterpret_cast<void*>(&timestamp)) {
    129     LOG(ERROR) << "DecodeFrameLibvpx() invalid output timestamp.";
    130     return cdm::kDecodeError;
    131   }
    132   decoded_frame->SetTimestamp(timestamp);
    133 
    134   if (!CopyVpxImageTo(decoded_frame)) {
    135     LOG(ERROR) << "DecodeFrameLibvpx() could not copy vpx image to output "
    136                << "buffer.";
    137     return cdm::kDecodeError;
    138   }
    139 
    140   return cdm::kSuccess;
    141 }
    142 
    143 bool LibvpxCdmVideoDecoder::CopyVpxImageTo(cdm::VideoFrame* cdm_video_frame) {
    144   DCHECK(cdm_video_frame);
    145   DCHECK_EQ(vpx_image_->fmt, VPX_IMG_FMT_I420);
    146   DCHECK_EQ(vpx_image_->d_w % 2, 0U);
    147   DCHECK_EQ(vpx_image_->d_h % 2, 0U);
    148 
    149   const int y_size = vpx_image_->stride[VPX_PLANE_Y] * vpx_image_->d_h;
    150   const int uv_rows = vpx_image_->d_h / 2;
    151   const int u_size = vpx_image_->stride[VPX_PLANE_U] * uv_rows;
    152   const int v_size = vpx_image_->stride[VPX_PLANE_V] * uv_rows;
    153   const int space_required = y_size + u_size + v_size;
    154 
    155   DCHECK(!cdm_video_frame->FrameBuffer());
    156   cdm_video_frame->SetFrameBuffer(host_->Allocate(space_required));
    157   if (!cdm_video_frame->FrameBuffer()) {
    158     LOG(ERROR) << "CopyVpxImageTo() CdmHost::Allocate failed.";
    159     return false;
    160   }
    161   cdm_video_frame->FrameBuffer()->SetSize(space_required);
    162 
    163   memcpy(cdm_video_frame->FrameBuffer()->Data(),
    164          vpx_image_->planes[VPX_PLANE_Y],
    165          y_size);
    166   memcpy(cdm_video_frame->FrameBuffer()->Data() + y_size,
    167          vpx_image_->planes[VPX_PLANE_U],
    168          u_size);
    169   memcpy(cdm_video_frame->FrameBuffer()->Data() + y_size + u_size,
    170          vpx_image_->planes[VPX_PLANE_V],
    171          v_size);
    172 
    173   cdm_video_frame->SetFormat(cdm::kYv12);
    174 
    175   cdm::Size video_frame_size;
    176   video_frame_size.width = vpx_image_->d_w;
    177   video_frame_size.height = vpx_image_->d_h;
    178   cdm_video_frame->SetSize(video_frame_size);
    179 
    180   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kYPlane, 0);
    181   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kUPlane, y_size);
    182   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kVPlane,
    183                                     y_size + u_size);
    184 
    185   cdm_video_frame->SetStride(cdm::VideoFrame::kYPlane,
    186                               vpx_image_->stride[VPX_PLANE_Y]);
    187   cdm_video_frame->SetStride(cdm::VideoFrame::kUPlane,
    188                               vpx_image_->stride[VPX_PLANE_U]);
    189   cdm_video_frame->SetStride(cdm::VideoFrame::kVPlane,
    190                               vpx_image_->stride[VPX_PLANE_V]);
    191 
    192   return true;
    193 }
    194 
    195 }  // namespace media
    196