Home | History | Annotate | Download | only in external_clear_key
      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/external_clear_key/ffmpeg_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 #include "media/ffmpeg/ffmpeg_common.h"
     12 
     13 // Include FFmpeg header files.
     14 extern "C" {
     15 // Temporarily disable possible loss of data warning.
     16 MSVC_PUSH_DISABLE_WARNING(4244);
     17 #include <libavcodec/avcodec.h>
     18 MSVC_POP_WARNING();
     19 }  // extern "C"
     20 
     21 namespace media {
     22 
     23 static const int kDecodeThreads = 1;
     24 
     25 static cdm::VideoFormat PixelFormatToCdmVideoFormat(PixelFormat pixel_format) {
     26   switch (pixel_format) {
     27     case PIX_FMT_YUV420P:
     28       return cdm::kYv12;
     29     default:
     30       DVLOG(1) << "Unsupported PixelFormat: " << pixel_format;
     31   }
     32   return cdm::kUnknownVideoFormat;
     33 }
     34 
     35 static PixelFormat CdmVideoFormatToPixelFormat(cdm::VideoFormat video_format) {
     36   switch (video_format) {
     37     case cdm::kYv12:
     38     case cdm::kI420:
     39       return PIX_FMT_YUV420P;
     40     case cdm::kUnknownVideoFormat:
     41     default:
     42       DVLOG(1) << "Unsupported cdm::VideoFormat: " << video_format;
     43   }
     44   return PIX_FMT_NONE;
     45 }
     46 
     47 static AVCodecID CdmVideoCodecToCodecID(
     48     cdm::VideoDecoderConfig::VideoCodec video_codec) {
     49   switch (video_codec) {
     50     case cdm::VideoDecoderConfig::kCodecVp8:
     51       return AV_CODEC_ID_VP8;
     52     case cdm::VideoDecoderConfig::kCodecH264:
     53       return AV_CODEC_ID_H264;
     54     case cdm::VideoDecoderConfig::kCodecVp9:
     55       return AV_CODEC_ID_VP9;
     56     case cdm::VideoDecoderConfig::kUnknownVideoCodec:
     57     default:
     58       NOTREACHED() << "Unsupported cdm::VideoCodec: " << video_codec;
     59       return AV_CODEC_ID_NONE;
     60   }
     61 }
     62 
     63 static int CdmVideoCodecProfileToProfileID(
     64     cdm::VideoDecoderConfig::VideoCodecProfile profile) {
     65   switch (profile) {
     66     case cdm::VideoDecoderConfig::kProfileNotNeeded:
     67       // For codecs that do not need a profile (e.g. VP8/VP9), does not define
     68       // an FFmpeg profile.
     69       return FF_PROFILE_UNKNOWN;
     70     case cdm::VideoDecoderConfig::kH264ProfileBaseline:
     71       return FF_PROFILE_H264_BASELINE;
     72     case cdm::VideoDecoderConfig::kH264ProfileMain:
     73       return FF_PROFILE_H264_MAIN;
     74     case cdm::VideoDecoderConfig::kH264ProfileExtended:
     75       return FF_PROFILE_H264_EXTENDED;
     76     case cdm::VideoDecoderConfig::kH264ProfileHigh:
     77       return FF_PROFILE_H264_HIGH;
     78     case cdm::VideoDecoderConfig::kH264ProfileHigh10:
     79       return FF_PROFILE_H264_HIGH_10;
     80     case cdm::VideoDecoderConfig::kH264ProfileHigh422:
     81       return FF_PROFILE_H264_HIGH_422;
     82     case cdm::VideoDecoderConfig::kH264ProfileHigh444Predictive:
     83       return FF_PROFILE_H264_HIGH_444_PREDICTIVE;
     84     case cdm::VideoDecoderConfig::kUnknownVideoCodecProfile:
     85     default:
     86       NOTREACHED() << "Unknown cdm::VideoCodecProfile: " << profile;
     87       return FF_PROFILE_UNKNOWN;
     88   }
     89 }
     90 
     91 static void CdmVideoDecoderConfigToAVCodecContext(
     92     const cdm::VideoDecoderConfig& config,
     93     AVCodecContext* codec_context) {
     94   codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
     95   codec_context->codec_id = CdmVideoCodecToCodecID(config.codec);
     96   codec_context->profile = CdmVideoCodecProfileToProfileID(config.profile);
     97   codec_context->coded_width = config.coded_size.width;
     98   codec_context->coded_height = config.coded_size.height;
     99   codec_context->pix_fmt = CdmVideoFormatToPixelFormat(config.format);
    100 
    101   if (config.extra_data) {
    102     codec_context->extradata_size = config.extra_data_size;
    103     codec_context->extradata = reinterpret_cast<uint8_t*>(
    104         av_malloc(config.extra_data_size + FF_INPUT_BUFFER_PADDING_SIZE));
    105     memcpy(codec_context->extradata, config.extra_data,
    106            config.extra_data_size);
    107     memset(codec_context->extradata + config.extra_data_size, 0,
    108            FF_INPUT_BUFFER_PADDING_SIZE);
    109   } else {
    110     codec_context->extradata = NULL;
    111     codec_context->extradata_size = 0;
    112   }
    113 }
    114 
    115 static void CopyPlane(const uint8_t* source,
    116                       int32_t source_stride,
    117                       int32_t target_stride,
    118                       int32_t rows,
    119                       int32_t copy_bytes_per_row,
    120                       uint8_t* target) {
    121   DCHECK(source);
    122   DCHECK(target);
    123   DCHECK_LE(copy_bytes_per_row, source_stride);
    124   DCHECK_LE(copy_bytes_per_row, target_stride);
    125 
    126   for (int i = 0; i < rows; ++i) {
    127     const int source_offset = i * source_stride;
    128     const int target_offset = i * target_stride;
    129     memcpy(target + target_offset,
    130            source + source_offset,
    131            copy_bytes_per_row);
    132   }
    133 }
    134 
    135 FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(ClearKeyCdmHost* host)
    136     : is_initialized_(false),
    137       host_(host) {
    138 }
    139 
    140 FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() {
    141   ReleaseFFmpegResources();
    142 }
    143 
    144 bool FFmpegCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) {
    145   DVLOG(1) << "Initialize()";
    146 
    147   if (!IsValidOutputConfig(config.format, config.coded_size)) {
    148     LOG(ERROR) << "Initialize(): invalid video decoder configuration.";
    149     return false;
    150   }
    151 
    152   if (is_initialized_) {
    153     LOG(ERROR) << "Initialize(): Already initialized.";
    154     return false;
    155   }
    156 
    157   // Initialize AVCodecContext structure.
    158   codec_context_.reset(avcodec_alloc_context3(NULL));
    159   CdmVideoDecoderConfigToAVCodecContext(config, codec_context_.get());
    160 
    161   // Enable motion vector search (potentially slow), strong deblocking filter
    162   // for damaged macroblocks, and set our error detection sensitivity.
    163   codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
    164   codec_context_->err_recognition = AV_EF_CAREFUL;
    165   codec_context_->thread_count = kDecodeThreads;
    166   codec_context_->opaque = this;
    167   codec_context_->flags |= CODEC_FLAG_EMU_EDGE;
    168 
    169   AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
    170   if (!codec) {
    171     LOG(ERROR) << "Initialize(): avcodec_find_decoder failed.";
    172     return false;
    173   }
    174 
    175   int status;
    176   if ((status = avcodec_open2(codec_context_.get(), codec, NULL)) < 0) {
    177     LOG(ERROR) << "Initialize(): avcodec_open2 failed: " << status;
    178     return false;
    179   }
    180 
    181   av_frame_.reset(av_frame_alloc());
    182   is_initialized_ = true;
    183 
    184   return true;
    185 }
    186 
    187 void FFmpegCdmVideoDecoder::Deinitialize() {
    188   DVLOG(1) << "Deinitialize()";
    189   ReleaseFFmpegResources();
    190   is_initialized_ = false;
    191 }
    192 
    193 void FFmpegCdmVideoDecoder::Reset() {
    194   DVLOG(1) << "Reset()";
    195   avcodec_flush_buffers(codec_context_.get());
    196 }
    197 
    198 // static
    199 bool FFmpegCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format,
    200                                                 const cdm::Size& data_size) {
    201   return ((format == cdm::kYv12 || format == cdm::kI420) &&
    202           (data_size.width % 2) == 0 && (data_size.height % 2) == 0 &&
    203           data_size.width > 0 && data_size.height > 0 &&
    204           data_size.width <= limits::kMaxDimension &&
    205           data_size.height <= limits::kMaxDimension &&
    206           data_size.width * data_size.height <= limits::kMaxCanvas);
    207 }
    208 
    209 cdm::Status FFmpegCdmVideoDecoder::DecodeFrame(
    210     const uint8_t* compressed_frame,
    211     int32_t compressed_frame_size,
    212     int64_t timestamp,
    213     cdm::VideoFrame* decoded_frame) {
    214   DVLOG(1) << "DecodeFrame()";
    215   DCHECK(decoded_frame);
    216 
    217   // Create a packet for input data.
    218   AVPacket packet;
    219   av_init_packet(&packet);
    220 
    221   // The FFmpeg API does not allow us to have const read-only pointers.
    222   packet.data = const_cast<uint8_t*>(compressed_frame);
    223   packet.size = compressed_frame_size;
    224 
    225   // Let FFmpeg handle presentation timestamp reordering.
    226   codec_context_->reordered_opaque = timestamp;
    227 
    228   // Reset frame to default values.
    229   av_frame_unref(av_frame_.get());
    230 
    231   // This is for codecs not using get_buffer to initialize
    232   // |av_frame_->reordered_opaque|
    233   av_frame_->reordered_opaque = codec_context_->reordered_opaque;
    234 
    235   int frame_decoded = 0;
    236   int result = avcodec_decode_video2(codec_context_.get(),
    237                                      av_frame_.get(),
    238                                      &frame_decoded,
    239                                      &packet);
    240   // Log the problem when we can't decode a video frame and exit early.
    241   if (result < 0) {
    242     LOG(ERROR) << "DecodeFrame(): Error decoding video frame with timestamp: "
    243                << timestamp << " us, packet size: " << packet.size  << " bytes";
    244     return cdm::kDecodeError;
    245   }
    246 
    247   // If no frame was produced then signal that more data is required to produce
    248   // more frames.
    249   if (frame_decoded == 0)
    250     return cdm::kNeedMoreData;
    251 
    252   // The decoder is in a bad state and not decoding correctly.
    253   // Checking for NULL avoids a crash.
    254   if (!av_frame_->data[cdm::VideoFrame::kYPlane] ||
    255       !av_frame_->data[cdm::VideoFrame::kUPlane] ||
    256       !av_frame_->data[cdm::VideoFrame::kVPlane]) {
    257     LOG(ERROR) << "DecodeFrame(): Video frame has invalid frame data.";
    258     return cdm::kDecodeError;
    259   }
    260 
    261   if (!CopyAvFrameTo(decoded_frame)) {
    262     LOG(ERROR) << "DecodeFrame() could not copy video frame to output buffer.";
    263     return cdm::kDecodeError;
    264   }
    265 
    266   return cdm::kSuccess;
    267 }
    268 
    269 bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame* cdm_video_frame) {
    270   DCHECK(cdm_video_frame);
    271   DCHECK_EQ(av_frame_->format, PIX_FMT_YUV420P);
    272   DCHECK_EQ(av_frame_->width % 2, 0);
    273   DCHECK_EQ(av_frame_->height % 2, 0);
    274 
    275   const int y_size = av_frame_->width * av_frame_->height;
    276   const int uv_size = y_size / 2;
    277   const int space_required = y_size + (uv_size * 2);
    278 
    279   DCHECK(!cdm_video_frame->FrameBuffer());
    280   cdm_video_frame->SetFrameBuffer(host_->Allocate(space_required));
    281   if (!cdm_video_frame->FrameBuffer()) {
    282     LOG(ERROR) << "CopyAvFrameTo() ClearKeyCdmHost::Allocate failed.";
    283     return false;
    284   }
    285   cdm_video_frame->FrameBuffer()->SetSize(space_required);
    286 
    287   CopyPlane(av_frame_->data[cdm::VideoFrame::kYPlane],
    288             av_frame_->linesize[cdm::VideoFrame::kYPlane],
    289             av_frame_->width,
    290             av_frame_->height,
    291             av_frame_->width,
    292             cdm_video_frame->FrameBuffer()->Data());
    293 
    294   const int uv_stride = av_frame_->width / 2;
    295   const int uv_rows = av_frame_->height / 2;
    296   CopyPlane(av_frame_->data[cdm::VideoFrame::kUPlane],
    297             av_frame_->linesize[cdm::VideoFrame::kUPlane],
    298             uv_stride,
    299             uv_rows,
    300             uv_stride,
    301             cdm_video_frame->FrameBuffer()->Data() + y_size);
    302 
    303   CopyPlane(av_frame_->data[cdm::VideoFrame::kVPlane],
    304             av_frame_->linesize[cdm::VideoFrame::kVPlane],
    305             uv_stride,
    306             uv_rows,
    307             uv_stride,
    308             cdm_video_frame->FrameBuffer()->Data() + y_size + uv_size);
    309 
    310   PixelFormat format = static_cast<PixelFormat>(av_frame_->format);
    311   cdm_video_frame->SetFormat(PixelFormatToCdmVideoFormat(format));
    312 
    313   cdm::Size video_frame_size;
    314   video_frame_size.width = av_frame_->width;
    315   video_frame_size.height = av_frame_->height;
    316   cdm_video_frame->SetSize(video_frame_size);
    317 
    318   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kYPlane, 0);
    319   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kUPlane, y_size);
    320   cdm_video_frame->SetPlaneOffset(cdm::VideoFrame::kVPlane,
    321                                     y_size + uv_size);
    322 
    323   cdm_video_frame->SetStride(cdm::VideoFrame::kYPlane, av_frame_->width);
    324   cdm_video_frame->SetStride(cdm::VideoFrame::kUPlane, uv_stride);
    325   cdm_video_frame->SetStride(cdm::VideoFrame::kVPlane, uv_stride);
    326 
    327   cdm_video_frame->SetTimestamp(av_frame_->reordered_opaque);
    328 
    329   return true;
    330 }
    331 
    332 void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() {
    333   DVLOG(1) << "ReleaseFFmpegResources()";
    334 
    335   codec_context_.reset();
    336   av_frame_.reset();
    337 }
    338 
    339 }  // namespace media
    340