diff options
Diffstat (limited to 'chromium/media/filters/vpx_video_decoder.cc')
-rw-r--r-- | chromium/media/filters/vpx_video_decoder.cc | 272 |
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; |