Chromium Code Reviews| Index: webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc |
| diff --git a/webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc b/webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc |
| index 2abe2a6b0f7cb65ee7ad2f2f06a4d812c427af9a..e1d888fd9144782aa9c7303568921858af7ab687 100644 |
| --- a/webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc |
| +++ b/webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc |
| @@ -18,6 +18,25 @@ MSVC_PUSH_DISABLE_WARNING(4244); |
| MSVC_POP_WARNING(); |
| } // extern "C" |
| +// TODO(tomfinegan): Move libvpx decode support somewhere else. Another ifdef |
| +// is a crime against humanity etc etc... |
| +#define USE_LIBVPX 1 |
| + |
| +#if defined (USE_LIBVPX) |
| +// Include libvpx header files. |
| +extern "C" { |
| +#define VPX_CODEC_DISABLE_COMPAT 1 |
| +#include "third_party/libvpx/libvpx.h" |
| +} |
| +#endif |
| + |
| +// TODO(tomfinegan): I'm seeing bad video output, so I put in code w/memcpy's |
| +// instead of using |CopyPlane()| to try to figure out what was going on. Turns |
| +// out video output is bad with or without libvpx in the mix. I've left this |
| +// mess here because full plane copies are what the stuff we're comparing |
| +// against does, so maybe it's of some use... |
|
xhwang
2012/11/16 17:33:02
Is this what you are seeing? http://code.google.co
Tom Finegan
2012/11/16 19:36:51
Yeah, that's the problem. Things look fine with --
|
| +// #define USE_COPYPLANE_WITH_LIBVPX 1 |
| + |
| namespace webkit_media { |
| static const int kDecodeThreads = 1; |
| @@ -133,7 +152,9 @@ FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(cdm::Allocator* allocator) |
| : codec_context_(NULL), |
| av_frame_(NULL), |
| is_initialized_(false), |
| - allocator_(allocator) { |
| + allocator_(allocator), |
| + vpx_codec_(NULL), |
| + vpx_image_(NULL) { |
| } |
| FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() { |
| @@ -153,45 +174,33 @@ bool FFmpegCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) { |
| return false; |
| } |
| - // Initialize AVCodecContext structure. |
| - codec_context_ = avcodec_alloc_context3(NULL); |
| - CdmVideoDecoderConfigToAVCodecContext(config, codec_context_); |
| - |
| - // Enable motion vector search (potentially slow), strong deblocking filter |
| - // for damaged macroblocks, and set our error detection sensitivity. |
| - codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; |
| - codec_context_->err_recognition = AV_EF_CAREFUL; |
| - codec_context_->thread_count = kDecodeThreads; |
| - codec_context_->opaque = this; |
| - codec_context_->flags |= CODEC_FLAG_EMU_EDGE; |
| - |
| - AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
| - if (!codec) { |
| - LOG(ERROR) << "Initialize(): avcodec_find_decoder failed."; |
| - return false; |
| - } |
| - |
| - int status; |
| - if ((status = avcodec_open2(codec_context_, codec, NULL)) < 0) { |
| - LOG(ERROR) << "Initialize(): avcodec_open2 failed: " << status; |
| - return false; |
| +// TODO(tomfinegan): Move libvpx decode support somewhere else. |
| +#if defined(USE_LIBVPX) |
| + if (config.codec == cdm::VideoDecoderConfig::kCodecVp8) { |
| + return InitializeLibvpx(config); |
| } |
| +#endif |
| - av_frame_ = avcodec_alloc_frame(); |
| - is_initialized_ = true; |
| - |
| - return true; |
| + return InitializeFFmpeg(config); |
| } |
| void FFmpegCdmVideoDecoder::Deinitialize() { |
| DVLOG(1) << "Deinitialize()"; |
| ReleaseFFmpegResources(); |
| + |
| + if (vpx_codec_) { |
| + vpx_codec_destroy(vpx_codec_); |
| + vpx_codec_ = NULL; |
| + } |
| + |
| is_initialized_ = false; |
| } |
| void FFmpegCdmVideoDecoder::Reset() { |
| DVLOG(1) << "Reset()"; |
| - avcodec_flush_buffers(codec_context_); |
| + |
| + if (codec_context_) |
| + avcodec_flush_buffers(codec_context_); |
| } |
| // static |
| @@ -210,59 +219,19 @@ cdm::Status FFmpegCdmVideoDecoder::DecodeFrame( |
| int32_t compressed_frame_size, |
| int64_t timestamp, |
| cdm::VideoFrame* decoded_frame) { |
| - DVLOG(1) << "DecodeFrame()"; |
| DCHECK(decoded_frame); |
| - // Create a packet for input data. |
| - AVPacket packet; |
| - av_init_packet(&packet); |
| - |
| - // The FFmpeg API does not allow us to have const read-only pointers. |
| - packet.data = const_cast<uint8_t*>(compressed_frame); |
| - packet.size = compressed_frame_size; |
| - |
| - // Let FFmpeg handle presentation timestamp reordering. |
| - codec_context_->reordered_opaque = timestamp; |
| - |
| - // Reset frame to default values. |
| - avcodec_get_frame_defaults(av_frame_); |
| - |
| - // This is for codecs not using get_buffer to initialize |
| - // |av_frame_->reordered_opaque| |
| - av_frame_->reordered_opaque = codec_context_->reordered_opaque; |
| - |
| - int frame_decoded = 0; |
| - int result = avcodec_decode_video2(codec_context_, |
| - av_frame_, |
| - &frame_decoded, |
| - &packet); |
| - // Log the problem when we can't decode a video frame and exit early. |
| - if (result < 0) { |
| - LOG(ERROR) << "DecodeFrame(): Error decoding video frame with timestamp: " |
| - << timestamp << " us, packet size: " << packet.size << " bytes"; |
| - return cdm::kDecodeError; |
| - } |
| - |
| - // If no frame was produced then signal that more data is required to produce |
| - // more frames. |
| - if (frame_decoded == 0) |
| - return cdm::kNeedMoreData; |
| - |
| - // The decoder is in a bad state and not decoding correctly. |
| - // Checking for NULL avoids a crash. |
| - if (!av_frame_->data[cdm::VideoFrame::kYPlane] || |
| - !av_frame_->data[cdm::VideoFrame::kUPlane] || |
| - !av_frame_->data[cdm::VideoFrame::kVPlane]) { |
| - LOG(ERROR) << "DecodeFrame(): Video frame has invalid frame data."; |
| - return cdm::kDecodeError; |
| - } |
| - |
| - if (!CopyAvFrameTo(decoded_frame)) { |
| - LOG(ERROR) << "DecodeFrame() could not copy video frame to output buffer."; |
| - return cdm::kDecodeError; |
| +// TODO(tomfinegan): Move libvpx decode support somewhere else. |
| +#if defined(USE_LIBVPX) |
| + if (vpx_codec_) { |
| + return DecodeFrameLibvpx(compressed_frame, compressed_frame_size, |
| + timestamp, |
| + decoded_frame); |
| } |
| +#endif |
| - return cdm::kSuccess; |
| + return DecodeFrameFFmpeg(compressed_frame, compressed_frame_size, timestamp, |
| + decoded_frame); |
| } |
| bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame* cdm_video_frame) { |
| @@ -327,6 +296,109 @@ bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame* cdm_video_frame) { |
| return true; |
| } |
| +bool FFmpegCdmVideoDecoder::CopyVpxImageTo(cdm::VideoFrame* cdm_video_frame) { |
| + DCHECK(cdm_video_frame); |
| + DCHECK_EQ(vpx_image_->fmt, VPX_IMG_FMT_I420); |
| + DCHECK_EQ(vpx_image_->d_w % 2, 0U); |
| + DCHECK_EQ(vpx_image_->d_h % 2, 0U); |
| + |
| +#if defined(USE_COPYPLANE_WITH_LIBVPX) |
| + const int y_size = vpx_image_->d_w * vpx_image_->d_h; |
| + const int uv_size = y_size / 2; |
| + const int space_required = y_size + (uv_size * 2); |
| + |
| + DCHECK(!cdm_video_frame->frame_buffer()); |
| + cdm_video_frame->set_frame_buffer(allocator_->Allocate(space_required)); |
| + if (!cdm_video_frame->frame_buffer()) { |
| + LOG(ERROR) << "CopyVpxImageTo() cdm::Allocator::Allocate failed."; |
| + return false; |
| + } |
| + |
| + CopyPlane(vpx_image_->planes[VPX_PLANE_Y], |
| + vpx_image_->stride[VPX_PLANE_Y], |
| + vpx_image_->d_w, |
| + vpx_image_->d_h, |
| + vpx_image_->d_w, |
| + cdm_video_frame->frame_buffer()->data()); |
| + |
| + const int uv_stride = vpx_image_->d_w / 2; |
| + const int uv_rows = vpx_image_->d_h / 2; |
| + CopyPlane(vpx_image_->planes[VPX_PLANE_U], |
| + vpx_image_->stride[VPX_PLANE_U], |
| + uv_stride, |
| + uv_rows, |
| + uv_stride, |
| + cdm_video_frame->frame_buffer()->data() + y_size); |
| + |
| + CopyPlane(vpx_image_->planes[VPX_PLANE_V], |
| + vpx_image_->stride[VPX_PLANE_V], |
| + uv_stride, |
| + uv_rows, |
| + uv_stride, |
| + cdm_video_frame->frame_buffer()->data() + y_size + uv_size); |
| + |
| + cdm_video_frame->set_format(cdm::kYv12); |
| + |
| + cdm::Size video_frame_size; |
| + video_frame_size.width = vpx_image_->d_w; |
| + video_frame_size.height = vpx_image_->d_h; |
| + cdm_video_frame->set_size(video_frame_size); |
| + |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kYPlane, 0); |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kUPlane, y_size); |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kVPlane, |
| + y_size + uv_size); |
| + |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, vpx_image_->d_w); |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, uv_stride); |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, uv_stride); |
| +#else |
| + const int y_size = vpx_image_->stride[VPX_PLANE_Y] * vpx_image_->d_h; |
| + const int uv_rows = vpx_image_->d_h / 2; |
| + const int u_size = vpx_image_->stride[VPX_PLANE_U] * uv_rows; |
| + const int v_size = vpx_image_->stride[VPX_PLANE_V] * uv_rows; |
| + const int space_required = y_size + u_size + v_size; |
| + |
| + DCHECK(!cdm_video_frame->frame_buffer()); |
| + cdm_video_frame->set_frame_buffer(allocator_->Allocate(space_required)); |
| + if (!cdm_video_frame->frame_buffer()) { |
| + LOG(ERROR) << "CopyVpxImageTo() cdm::Allocator::Allocate failed."; |
| + return false; |
| + } |
| + |
| + memcpy(cdm_video_frame->frame_buffer()->data(), |
| + vpx_image_->planes[VPX_PLANE_Y], |
| + y_size); |
| + memcpy(cdm_video_frame->frame_buffer()->data() + y_size, |
| + vpx_image_->planes[VPX_PLANE_U], |
| + u_size); |
| + memcpy(cdm_video_frame->frame_buffer()->data() + y_size + u_size, |
| + vpx_image_->planes[VPX_PLANE_V], |
| + v_size); |
| + |
| + cdm_video_frame->set_format(cdm::kYv12); |
| + |
| + cdm::Size video_frame_size; |
| + video_frame_size.width = vpx_image_->d_w; |
| + video_frame_size.height = vpx_image_->d_h; |
| + cdm_video_frame->set_size(video_frame_size); |
| + |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kYPlane, 0); |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kUPlane, y_size); |
| + cdm_video_frame->set_plane_offset(cdm::VideoFrame::kVPlane, |
| + y_size + u_size); |
| + |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, |
| + vpx_image_->stride[VPX_PLANE_Y]); |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, |
| + vpx_image_->stride[VPX_PLANE_U]); |
| + cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, |
| + vpx_image_->stride[VPX_PLANE_V]); |
| +#endif // USE_COPYPLANE_WITH_LIBVPX |
| + |
| + return true; |
| +} |
| + |
| void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() { |
| DVLOG(1) << "ReleaseFFmpegResources()"; |
| @@ -342,4 +414,165 @@ void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() { |
| } |
| } |
| +bool FFmpegCdmVideoDecoder::InitializeFFmpeg( |
| + const cdm::VideoDecoderConfig& config) { |
| + // Initialize AVCodecContext structure. |
| + codec_context_ = avcodec_alloc_context3(NULL); |
| + CdmVideoDecoderConfigToAVCodecContext(config, codec_context_); |
| + |
| + // Enable motion vector search (potentially slow), strong deblocking filter |
| + // for damaged macroblocks, and set our error detection sensitivity. |
| + codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; |
| + codec_context_->err_recognition = AV_EF_CAREFUL; |
| + codec_context_->thread_count = kDecodeThreads; |
| + codec_context_->opaque = this; |
| + codec_context_->flags |= CODEC_FLAG_EMU_EDGE; |
| + |
| + AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
| + if (!codec) { |
| + LOG(ERROR) << "InitializeFFmpeg(): avcodec_find_decoder failed."; |
| + return false; |
| + } |
| + |
| + int status; |
| + if ((status = avcodec_open2(codec_context_, codec, NULL)) < 0) { |
| + LOG(ERROR) << "InitializeFFmpeg(): avcodec_open2 failed: " << status; |
| + return false; |
| + } |
| + |
| + av_frame_ = avcodec_alloc_frame(); |
| + is_initialized_ = true; |
| + |
| + return true; |
| +} |
| + |
| +bool FFmpegCdmVideoDecoder::InitializeLibvpx( |
| + const cdm::VideoDecoderConfig& config) { |
| + vpx_codec_ = new vpx_codec_ctx_t(); |
| + vpx_codec_dec_cfg_t vpx_config = {0}; |
| + vpx_config.w = config.coded_size.width; |
| + vpx_config.h = config.coded_size.height; |
| + vpx_config.threads = kDecodeThreads; |
| + |
| + vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_, |
| + vpx_codec_vp8_dx(), |
| + &vpx_config, |
| + 0); |
| + if (status != VPX_CODEC_OK) { |
| + LOG(ERROR) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret=" |
| + << status; |
| + delete vpx_codec_; |
| + vpx_codec_ = NULL; |
| + } |
| + |
| + is_initialized_ = true; |
| + return true; |
| +} |
| + |
| +cdm::Status FFmpegCdmVideoDecoder::DecodeFrameFFmpeg( |
| + const uint8_t* compressed_frame, |
| + int32_t compressed_frame_size, |
| + int64_t timestamp, |
| + cdm::VideoFrame* decoded_frame) { |
| + DVLOG(1) << "DecodeFrameFFmpeg()"; |
| + |
| + // Create a packet for input data. |
| + AVPacket packet; |
| + av_init_packet(&packet); |
| + |
| + // The FFmpeg API does not allow us to have const read-only pointers. |
| + packet.data = const_cast<uint8_t*>(compressed_frame); |
| + packet.size = compressed_frame_size; |
| + |
| + // Let FFmpeg handle presentation timestamp reordering. |
| + codec_context_->reordered_opaque = timestamp; |
| + |
| + // Reset frame to default values. |
| + avcodec_get_frame_defaults(av_frame_); |
| + |
| + // This is for codecs not using get_buffer to initialize |
| + // |av_frame_->reordered_opaque| |
| + av_frame_->reordered_opaque = codec_context_->reordered_opaque; |
| + |
| + int frame_decoded = 0; |
| + int result = avcodec_decode_video2(codec_context_, |
| + av_frame_, |
| + &frame_decoded, |
| + &packet); |
| + // Log the problem when we can't decode a video frame and exit early. |
| + if (result < 0) { |
| + LOG(ERROR) << "DecodeFrameFFmpeg(): Error decoding video frame with " |
| + << "timestamp: " << timestamp << " us, packet size: " |
| + << packet.size << " bytes"; |
| + return cdm::kDecodeError; |
| + } |
| + |
| + // If no frame was produced then signal that more data is required to produce |
| + // more frames. |
| + if (frame_decoded == 0) |
| + return cdm::kNeedMoreData; |
| + |
| + // The decoder is in a bad state and not decoding correctly. |
| + // Checking for NULL avoids a crash. |
| + if (!av_frame_->data[cdm::VideoFrame::kYPlane] || |
| + !av_frame_->data[cdm::VideoFrame::kUPlane] || |
| + !av_frame_->data[cdm::VideoFrame::kVPlane]) { |
| + LOG(ERROR) << "DecodeFrameFFmpeg(): Video frame has invalid frame data."; |
| + return cdm::kDecodeError; |
| + } |
| + |
| + if (!CopyAvFrameTo(decoded_frame)) { |
| + LOG(ERROR) << "DecodeFrameFFmpeg() could not copy video frame to output " |
| + << "buffer."; |
| + return cdm::kDecodeError; |
| + } |
| + |
| + return cdm::kSuccess; |
| +} |
| + |
| +cdm::Status FFmpegCdmVideoDecoder::DecodeFrameLibvpx( |
| + const uint8_t* compressed_frame, |
| + int32_t compressed_frame_size, |
| + int64_t timestamp, |
| + cdm::VideoFrame* decoded_frame) { |
| + DVLOG(1) << "DecodeFrameLibvpx()"; |
| + |
| + // Pass |compressed_frame| to libvpx. |
| + void* user_priv = reinterpret_cast<void*>(×tamp); |
| + vpx_codec_err_t status = vpx_codec_decode(vpx_codec_, |
| + compressed_frame, |
| + compressed_frame_size, |
| + user_priv, |
| + 0); |
| + if (status != VPX_CODEC_OK) { |
| + LOG(ERROR) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status=" |
| + << status; |
| + return cdm::kDecodeError; |
| + } |
| + |
| + // Gets pointer to decoded data. |
| + vpx_codec_iter_t iter = NULL; |
| + vpx_image_ = vpx_codec_get_frame(vpx_codec_, &iter); |
| + if (!vpx_image_) |
| + return cdm::kNeedMoreData; |
| + |
| + if (vpx_image_->user_priv != reinterpret_cast<void*>(×tamp)) { |
| + LOG(ERROR) << "DecodeFrameLibvpx() invalid output timestamp."; |
| + return cdm::kDecodeError; |
| + } |
| + decoded_frame->set_timestamp(timestamp); |
| + |
| + if (!CopyVpxImageTo(decoded_frame)) { |
| + LOG(ERROR) << "DecodeFrameLibvpx() could not copy vpx image to output " |
| + << "buffer."; |
| + return cdm::kDecodeError; |
| + } |
| + |
|
Tom Finegan
2012/11/16 10:29:54
Oops... I'll fix this whitespace.
|
| + |
| + |
| + |
| + |
| + return cdm::kSuccess; |
| +} |
| + |
| } // namespace webkit_media |