Index: webkit/media/crypto/ppapi/libvpx_cdm_video_decoder.cc |
diff --git a/webkit/media/crypto/ppapi/libvpx_cdm_video_decoder.cc b/webkit/media/crypto/ppapi/libvpx_cdm_video_decoder.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d513de7cd8e9cccc06e194934f226f408078591f |
--- /dev/null |
+++ b/webkit/media/crypto/ppapi/libvpx_cdm_video_decoder.cc |
@@ -0,0 +1,267 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "webkit/media/crypto/ppapi/libvpx_cdm_video_decoder.h" |
+ |
+#include "base/logging.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "media/base/buffers.h" |
+#include "media/base/limits.h" |
+ |
+// Include libvpx header files. |
+// VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
+// backwards compatibility for legacy applications using the library. |
+#define VPX_CODEC_DISABLE_COMPAT 1 |
+extern "C" { |
+#include "third_party/libvpx/libvpx.h" |
+} |
+ |
+#include "webkit/media/crypto/ppapi/content_decryption_module.h" |
+ |
+// Enable USE_COPYPLANE_WITH_LIBVPX to use |CopyPlane()| instead of memcpy to |
+// copy video frame data. |
+// #define USE_COPYPLANE_WITH_LIBVPX 1 |
+ |
+namespace webkit_media { |
+ |
+static const int kDecodeThreads = 1; |
+ |
+static void CopyPlane(const uint8_t* source, |
+ int32_t source_stride, |
+ int32_t target_stride, |
+ int32_t rows, |
+ int32_t copy_bytes_per_row, |
+ uint8_t* target) { |
+ DCHECK(source); |
+ DCHECK(target); |
+ DCHECK_LE(copy_bytes_per_row, source_stride); |
+ DCHECK_LE(copy_bytes_per_row, target_stride); |
+ |
+ for (int i = 0; i < rows; ++i) { |
+ const int source_offset = i * source_stride; |
+ const int target_offset = i * target_stride; |
+ memcpy(target + target_offset, |
+ source + source_offset, |
+ copy_bytes_per_row); |
+ } |
+} |
+ |
+LibvpxCdmVideoDecoder::LibvpxCdmVideoDecoder(cdm::Allocator* allocator) |
+ : is_initialized_(false), |
+ allocator_(allocator), |
+ vpx_codec_(NULL), |
+ vpx_image_(NULL) { |
+} |
+ |
+LibvpxCdmVideoDecoder::~LibvpxCdmVideoDecoder() { |
+ Deinitialize(); |
+} |
+ |
+bool LibvpxCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) { |
+ DVLOG(1) << "Initialize()"; |
+ |
+ if (!IsValidOutputConfig(config.format, config.coded_size)) { |
+ LOG(ERROR) << "Initialize(): invalid video decoder configuration."; |
+ return false; |
+ } |
+ |
+ if (is_initialized_) { |
+ LOG(ERROR) << "Initialize(): Already initialized."; |
+ return false; |
+ } |
+ |
+ vpx_codec_ = new vpx_codec_ctx(); |
+ 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; |
+} |
+ |
+void LibvpxCdmVideoDecoder::Deinitialize() { |
+ DVLOG(1) << "Deinitialize()"; |
+ |
+ if (vpx_codec_) { |
+ vpx_codec_destroy(vpx_codec_); |
+ vpx_codec_ = NULL; |
+ } |
+ |
+ is_initialized_ = false; |
+} |
+ |
+void LibvpxCdmVideoDecoder::Reset() { |
+ DVLOG(1) << "Reset()"; |
+} |
+ |
+// static |
+bool LibvpxCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format, |
+ const cdm::Size& data_size) { |
+ return ((format == cdm::kYv12 || format == cdm::kI420) && |
+ (data_size.width % 2) == 0 && (data_size.height % 2) == 0 && |
+ data_size.width > 0 && data_size.height > 0 && |
+ data_size.width <= media::limits::kMaxDimension && |
+ data_size.height <= media::limits::kMaxDimension && |
+ data_size.width * data_size.height <= media::limits::kMaxCanvas); |
+} |
+ |
+cdm::Status LibvpxCdmVideoDecoder::DecodeFrame( |
+ const uint8_t* compressed_frame, |
+ int32_t compressed_frame_size, |
+ int64_t timestamp, |
+ cdm::VideoFrame* decoded_frame) { |
+ DVLOG(1) << "DecodeFrame()"; |
+ DCHECK(decoded_frame); |
+ |
+ // 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; |
+ } |
+ |
+ return cdm::kSuccess; |
+ |
+} |
+ |
+bool LibvpxCdmVideoDecoder::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; |
+} |
+ |
+} // namespace webkit_media |