Chromium Code Reviews| Index: media/filters/vpx_video_decoder.cc |
| diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc |
| index 7d71d787d56f3f192ed8ca3eeb3fe117bc953f8b..1302765d855a35cc5b1df0c77241aa21fe841aab 100644 |
| --- a/media/filters/vpx_video_decoder.cc |
| +++ b/media/filters/vpx_video_decoder.cc |
| @@ -6,6 +6,7 @@ |
| #include <algorithm> |
| #include <string> |
| +#include <vector> |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| @@ -18,6 +19,7 @@ |
| #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 +32,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_external_frame_buffer.h" |
| #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" |
| } |
| @@ -67,6 +70,137 @@ static int GetThreadCount(const VideoDecoderConfig& config) { |
| return decode_threads; |
| } |
| +// Maximum number of frame buffers that can be used (by both chromium and libvpx |
| +// combined) for VP9 Decoding. |
| +// TODO(vigneshv): Investigate if this can be relaxed to a higher number. |
| +static const int kVP9MaxFrameBuffers = VP9_MAXIMUM_REF_BUFFERS + |
| + VPX_MAXIMUM_WORK_BUFFERS + |
| + limits::kMaxVideoFrames; |
| + |
| +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 { |
| + 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() { |
| + for (size_t i = 0; i < frame_buffers_.size(); ++i) { |
|
acolwell GONE FROM CHROMIUM
2014/02/04 17:47:50
nit: Use STLDeleteElements() from base/stl_util.h
vignesh
2014/02/04 19:01:09
Done.
|
| + delete frame_buffers_[i]; |
| + } |
| +} |
| + |
| +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()) { |
| + // Maximum number of frame buffers reached. |
| + if (i == kVP9MaxFrameBuffers) |
| + return NULL; |
| + |
| + // 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 != NULL); |
|
acolwell GONE FROM CHROMIUM
2014/02/04 17:47:50
nit: Remove the != NULL here and below.
vignesh
2014/02/04 19:01:09
Done.
|
| + DCHECK(fb != NULL); |
| + |
| + 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->frame_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->frame_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::SingleThreadTaskRunner>& task_runner) |
| : task_runner_(task_runner), |
| @@ -142,6 +276,20 @@ 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_external_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 +304,8 @@ void VpxVideoDecoder::CloseDecoder() { |
| vpx_codec_destroy(vpx_codec_); |
| delete vpx_codec_; |
| vpx_codec_ = NULL; |
| + if (memory_pool_) |
|
acolwell GONE FROM CHROMIUM
2014/02/04 17:47:50
nit: remove condition since blindly clearing this
vignesh
2014/02/04 19:01:09
Done.
|
| + memory_pool_ = NULL; |
| } |
| if (vpx_codec_alpha_) { |
| vpx_codec_destroy(vpx_codec_alpha_); |
| @@ -342,6 +492,22 @@ void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, |
| gfx::Size size(vpx_image->d_w, vpx_image->d_h); |
| + if (!vpx_codec_alpha_ && memory_pool_) { |
| + *video_frame = VideoFrame::WrapExternalYuvData( |
| + VideoFrame::YV12, |
| + 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->ext_fb_priv)); |
| + return; |
| + } |
| + |
| *video_frame = frame_pool_.CreateFrame( |
| vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, |
| size, |