Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(302)

Unified Diff: webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.cc

Issue 11316045: Add a libvpx video decoder to ClearKeyCdm and move the fake video decoder to its own class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.h ('k') | webkit/media/webkit_media.gypi » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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*>(&timestamp);
+ 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*>(&timestamp)) {
+ 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
« no previous file with comments | « webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.h ('k') | webkit/media/webkit_media.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698