summaryrefslogtreecommitdiffstats
path: root/chromium/media/filters/vpx_video_decoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/filters/vpx_video_decoder.cc')
-rw-r--r--chromium/media/filters/vpx_video_decoder.cc272
1 files changed, 200 insertions, 72 deletions
diff --git a/chromium/media/filters/vpx_video_decoder.cc b/chromium/media/filters/vpx_video_decoder.cc
index e270335504a..b3d3dfd555f 100644
--- a/chromium/media/filters/vpx_video_decoder.cc
+++ b/chromium/media/filters/vpx_video_decoder.cc
@@ -6,18 +6,21 @@
#include <algorithm>
#include <string>
+#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_byteorder.h"
-#include "media/base/bind_to_loop.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/demuxer_stream.h"
+#include "media/base/limits.h"
#include "media/base/media_switches.h"
#include "media/base/pipeline.h"
#include "media/base/video_decoder_config.h"
@@ -30,6 +33,7 @@
#define VPX_CODEC_DISABLE_COMPAT 1
extern "C" {
#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
+#include "third_party/libvpx/source/libvpx/vpx/vpx_frame_buffer.h"
#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
}
@@ -67,14 +71,131 @@ static int GetThreadCount(const VideoDecoderConfig& config) {
return decode_threads;
}
+class VpxVideoDecoder::MemoryPool
+ : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> {
+ public:
+ MemoryPool();
+
+ // Callback that will be called by libvpx when it needs a frame buffer.
+ // Parameters:
+ // |user_priv| Private data passed to libvpx (pointer to memory pool).
+ // |min_size| Minimum size needed by libvpx to decompress the next frame.
+ // |fb| Pointer to the frame buffer to update.
+ // Returns 0 on success. Returns < 0 on failure.
+ static int32 GetVP9FrameBuffer(void* user_priv, size_t min_size,
+ vpx_codec_frame_buffer* fb);
+
+ // Callback that will be called by libvpx when the frame buffer is no longer
+ // being used by libvpx. Parameters:
+ // |user_priv| Private data passed to libvpx (pointer to memory pool).
+ // |fb| Pointer to the frame buffer that's being released.
+ static int32 ReleaseVP9FrameBuffer(void *user_priv,
+ vpx_codec_frame_buffer *fb);
+
+ // Generates a "no_longer_needed" closure that holds a reference
+ // to this pool.
+ base::Closure CreateFrameCallback(void* fb_priv_data);
+
+ private:
+ friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>;
+ ~MemoryPool();
+
+ // Reference counted frame buffers used for VP9 decoding. Reference counting
+ // is done manually because both chromium and libvpx has to release this
+ // before a buffer can be re-used.
+ struct VP9FrameBuffer {
+ VP9FrameBuffer() : ref_cnt(0) {}
+ std::vector<uint8> data;
+ uint32 ref_cnt;
+ };
+
+ // Gets the next available frame buffer for use by libvpx.
+ VP9FrameBuffer* GetFreeFrameBuffer(size_t min_size);
+
+ // Method that gets called when a VideoFrame that references this pool gets
+ // destroyed.
+ void OnVideoFrameDestroyed(VP9FrameBuffer* frame_buffer);
+
+ // Frame buffers to be used by libvpx for VP9 Decoding.
+ std::vector<VP9FrameBuffer*> frame_buffers_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryPool);
+};
+
+VpxVideoDecoder::MemoryPool::MemoryPool() {}
+
+VpxVideoDecoder::MemoryPool::~MemoryPool() {
+ STLDeleteElements(&frame_buffers_);
+}
+
+VpxVideoDecoder::MemoryPool::VP9FrameBuffer*
+ VpxVideoDecoder::MemoryPool::GetFreeFrameBuffer(size_t min_size) {
+ // Check if a free frame buffer exists.
+ size_t i = 0;
+ for (; i < frame_buffers_.size(); ++i) {
+ if (frame_buffers_[i]->ref_cnt == 0)
+ break;
+ }
+
+ if (i == frame_buffers_.size()) {
+ // Create a new frame buffer.
+ frame_buffers_.push_back(new VP9FrameBuffer());
+ }
+
+ // Resize the frame buffer if necessary.
+ if (frame_buffers_[i]->data.size() < min_size)
+ frame_buffers_[i]->data.resize(min_size);
+ return frame_buffers_[i];
+}
+
+int32 VpxVideoDecoder::MemoryPool::GetVP9FrameBuffer(
+ void* user_priv, size_t min_size, vpx_codec_frame_buffer* fb) {
+ DCHECK(user_priv);
+ DCHECK(fb);
+
+ VpxVideoDecoder::MemoryPool* memory_pool =
+ static_cast<VpxVideoDecoder::MemoryPool*>(user_priv);
+
+ VP9FrameBuffer* fb_to_use = memory_pool->GetFreeFrameBuffer(min_size);
+ if (fb_to_use == NULL)
+ return -1;
+
+ fb->data = &fb_to_use->data[0];
+ fb->size = fb_to_use->data.size();
+ ++fb_to_use->ref_cnt;
+
+ // Set the frame buffer's private data to point at the external frame buffer.
+ fb->priv = static_cast<void*>(fb_to_use);
+ return 0;
+}
+
+int32 VpxVideoDecoder::MemoryPool::ReleaseVP9FrameBuffer(
+ void *user_priv, vpx_codec_frame_buffer *fb) {
+ VP9FrameBuffer* frame_buffer = static_cast<VP9FrameBuffer*>(fb->priv);
+ --frame_buffer->ref_cnt;
+ return 0;
+}
+
+base::Closure VpxVideoDecoder::MemoryPool::CreateFrameCallback(
+ void* fb_priv_data) {
+ VP9FrameBuffer* frame_buffer = static_cast<VP9FrameBuffer*>(fb_priv_data);
+ ++frame_buffer->ref_cnt;
+ return BindToCurrentLoop(
+ base::Bind(&MemoryPool::OnVideoFrameDestroyed, this,
+ frame_buffer));
+}
+
+void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed(
+ VP9FrameBuffer* frame_buffer) {
+ --frame_buffer->ref_cnt;
+}
+
VpxVideoDecoder::VpxVideoDecoder(
- const scoped_refptr<base::MessageLoopProxy>& message_loop)
- : message_loop_(message_loop),
- weak_factory_(this),
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner),
state_(kUninitialized),
vpx_codec_(NULL),
- vpx_codec_alpha_(NULL) {
-}
+ vpx_codec_alpha_(NULL) {}
VpxVideoDecoder::~VpxVideoDecoder() {
DCHECK_EQ(kUninitialized, state_);
@@ -82,14 +203,13 @@ VpxVideoDecoder::~VpxVideoDecoder() {
}
void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config,
- const PipelineStatusCB& status_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ bool low_delay,
+ const PipelineStatusCB& status_cb,
+ const OutputCB& output_cb) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(config.IsValidConfig());
DCHECK(!config.is_encrypted());
DCHECK(decode_cb_.is_null());
- DCHECK(reset_cb_.is_null());
-
- weak_this_ = weak_factory_.GetWeakPtr();
if (!ConfigureDecoder(config)) {
status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
@@ -99,6 +219,7 @@ void VpxVideoDecoder::Initialize(const VideoDecoderConfig& config,
// Success!
config_ = config;
state_ = kNormal;
+ output_cb_ = BindToCurrentLoop(output_cb);
status_cb.Run(PIPELINE_OK);
}
@@ -125,15 +246,12 @@ static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context,
}
bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
- const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
- bool can_handle = false;
- if (config.codec() == kCodecVP9)
- can_handle = true;
- if (!cmd_line->HasSwitch(switches::kDisableVp8AlphaPlayback) &&
- config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) {
- can_handle = true;
- }
- if (!can_handle)
+ if (config.codec() != kCodecVP8 && config.codec() != kCodecVP9)
+ return false;
+
+ // In VP8 videos, only those with alpha are handled by VpxVideoDecoder. All
+ // other VP8 videos go to FFmpegVideoDecoder.
+ if (config.codec() == kCodecVP8 && config.format() != VideoFrame::YV12A)
return false;
CloseDecoder();
@@ -142,6 +260,19 @@ bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
if (!vpx_codec_)
return false;
+ // We use our own buffers for VP9 so that there is no need to copy data after
+ // decoding.
+ if (config.codec() == kCodecVP9) {
+ memory_pool_ = new MemoryPool();
+ if (vpx_codec_set_frame_buffer_functions(vpx_codec_,
+ &MemoryPool::GetVP9FrameBuffer,
+ &MemoryPool::ReleaseVP9FrameBuffer,
+ memory_pool_)) {
+ LOG(ERROR) << "Failed to configure external buffers.";
+ return false;
+ }
+ }
+
if (config.format() == VideoFrame::YV12A) {
vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config);
if (!vpx_codec_alpha_)
@@ -156,6 +287,7 @@ void VpxVideoDecoder::CloseDecoder() {
vpx_codec_destroy(vpx_codec_);
delete vpx_codec_;
vpx_codec_ = NULL;
+ memory_pool_ = NULL;
}
if (vpx_codec_alpha_) {
vpx_codec_destroy(vpx_codec_alpha_);
@@ -166,7 +298,7 @@ void VpxVideoDecoder::CloseDecoder() {
void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB& decode_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!decode_cb.is_null());
CHECK_NE(state_, kUninitialized);
CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported.";
@@ -174,13 +306,13 @@ void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
decode_cb_ = BindToCurrentLoop(decode_cb);
if (state_ == kError) {
- base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL);
+ base::ResetAndReturn(&decode_cb_).Run(kDecodeError);
return;
}
// Return empty frames if decoding has finished.
if (state_ == kDecodeFinished) {
- base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame());
+ base::ResetAndReturn(&decode_cb_).Run(kOk);
return;
}
@@ -188,68 +320,45 @@ void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
}
void VpxVideoDecoder::Reset(const base::Closure& closure) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(reset_cb_.is_null());
- reset_cb_ = BindToCurrentLoop(closure);
-
- // Defer the reset if a decode is pending.
- if (!decode_cb_.is_null())
- return;
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(decode_cb_.is_null());
- DoReset();
+ state_ = kNormal;
+ task_runner_->PostTask(FROM_HERE, closure);
}
-void VpxVideoDecoder::Stop(const base::Closure& closure) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- base::ScopedClosureRunner runner(BindToCurrentLoop(closure));
-
- if (state_ == kUninitialized)
- return;
-
- if (!decode_cb_.is_null()) {
- base::ResetAndReturn(&decode_cb_).Run(kOk, NULL);
- // Reset is pending only when decode is pending.
- if (!reset_cb_.is_null())
- base::ResetAndReturn(&reset_cb_).Run();
- }
+void VpxVideoDecoder::Stop() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
state_ = kUninitialized;
}
-bool VpxVideoDecoder::HasAlpha() const {
- return vpx_codec_alpha_ != NULL;
-}
-
void VpxVideoDecoder::DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer) {
- DCHECK(message_loop_->BelongsToCurrentThread());
+ DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_NE(state_, kUninitialized);
DCHECK_NE(state_, kDecodeFinished);
DCHECK_NE(state_, kError);
- DCHECK(reset_cb_.is_null());
DCHECK(!decode_cb_.is_null());
DCHECK(buffer);
// Transition to kDecodeFinished on the first end of stream buffer.
if (state_ == kNormal && buffer->end_of_stream()) {
state_ = kDecodeFinished;
- base::ResetAndReturn(&decode_cb_).Run(kOk, VideoFrame::CreateEOSFrame());
+ base::ResetAndReturn(&decode_cb_).Run(kOk);
return;
}
scoped_refptr<VideoFrame> video_frame;
if (!VpxDecode(buffer, &video_frame)) {
state_ = kError;
- base::ResetAndReturn(&decode_cb_).Run(kDecodeError, NULL);
+ base::ResetAndReturn(&decode_cb_).Run(kDecodeError);
return;
}
- // If we didn't get a frame we need more data.
- if (!video_frame.get()) {
- base::ResetAndReturn(&decode_cb_).Run(kNotEnoughData, NULL);
- return;
- }
+ base::ResetAndReturn(&decode_cb_).Run(kOk);
- base::ResetAndReturn(&decode_cb_).Run(kOk, video_frame);
+ if (video_frame)
+ output_cb_.Run(video_frame);
}
bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
@@ -321,29 +430,48 @@ bool VpxVideoDecoder::VpxDecode(const scoped_refptr<DecoderBuffer>& buffer,
}
CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame);
- (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp));
+ (*video_frame)->set_timestamp(base::TimeDelta::FromMicroseconds(timestamp));
return true;
}
-void VpxVideoDecoder::DoReset() {
- DCHECK(decode_cb_.is_null());
-
- state_ = kNormal;
- reset_cb_.Run();
- reset_cb_.Reset();
-}
-
void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image,
const struct vpx_image* vpx_image_alpha,
scoped_refptr<VideoFrame>* video_frame) {
CHECK(vpx_image);
CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 ||
- vpx_image->fmt == VPX_IMG_FMT_YV12);
+ vpx_image->fmt == VPX_IMG_FMT_YV12 ||
+ vpx_image->fmt == VPX_IMG_FMT_I444);
+
+ VideoFrame::Format codec_format = VideoFrame::YV12;
+ int uv_rows = (vpx_image->d_h + 1) / 2;
+
+ if (vpx_image->fmt == VPX_IMG_FMT_I444) {
+ CHECK(!vpx_codec_alpha_);
+ codec_format = VideoFrame::YV24;
+ uv_rows = vpx_image->d_h;
+ } else if (vpx_codec_alpha_) {
+ codec_format = VideoFrame::YV12A;
+ }
gfx::Size size(vpx_image->d_w, vpx_image->d_h);
+ if (!vpx_codec_alpha_ && memory_pool_) {
+ *video_frame = VideoFrame::WrapExternalYuvData(
+ codec_format,
+ size, gfx::Rect(size), config_.natural_size(),
+ vpx_image->stride[VPX_PLANE_Y],
+ vpx_image->stride[VPX_PLANE_U],
+ vpx_image->stride[VPX_PLANE_V],
+ vpx_image->planes[VPX_PLANE_Y],
+ vpx_image->planes[VPX_PLANE_U],
+ vpx_image->planes[VPX_PLANE_V],
+ kNoTimestamp(),
+ memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
+ return;
+ }
+
*video_frame = frame_pool_.CreateFrame(
- vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12,
+ codec_format,
size,
gfx::Rect(size),
config_.natural_size(),
@@ -355,11 +483,11 @@ void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image,
video_frame->get());
CopyUPlane(vpx_image->planes[VPX_PLANE_U],
vpx_image->stride[VPX_PLANE_U],
- (vpx_image->d_h + 1) / 2,
+ uv_rows,
video_frame->get());
CopyVPlane(vpx_image->planes[VPX_PLANE_V],
vpx_image->stride[VPX_PLANE_V],
- (vpx_image->d_h + 1) / 2,
+ uv_rows,
video_frame->get());
if (!vpx_codec_alpha_)
return;