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 e270335504a523131f55f0f05325f98eeb79806d..f0bffbdc7ef759870e1fd9f186ae1e355e791bdf 100644 |
| --- a/media/filters/vpx_video_decoder.cc |
| +++ b/media/filters/vpx_video_decoder.cc |
| @@ -18,6 +18,7 @@ |
| #include "media/base/bind_to_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 +31,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,13 +69,115 @@ static int GetThreadCount(const VideoDecoderConfig& config) { |
| return decode_threads; |
| } |
| +// Maximum number of frame buffers allowed to be used by libvpx for VP9 |
| +// decoding. |
| +static const int kVP9MaxFrameBuffers = VP9_MAXIMUM_REF_BUFFERS + |
| + VPX_MAXIMUM_WORK_BUFFERS + |
| + limits::kMaxVideoFrames; |
| + |
| +class VpxVideoDecoder::MemoryPool |
| + : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> { |
| + public: |
| + MemoryPool(VpxVideoDecoder* decoder); |
| + vpx_codec_frame_buffer* frame_buffers() { return frame_buffers_; } |
| + size_t frame_buffers_size() const { return arraysize(frame_buffers_); } |
| + |
| + // Callback that will be called by libvpx if the frame buffer size needs to |
| + // increase. Parameters: |
| + // user_priv Data passed into libvpx (we pass NULL). |
| + // new_size Minimum size needed by libvpx to decompress the next frame. |
| + // fb Pointer to the frame buffer to update. |
| + // Returns VPX_CODEC_OK on success. Returns < 0 on failure. |
| + static int32 ReallocVP9FrameBuffer(void* user_priv, size_t new_size, |
| + vpx_codec_frame_buffer* fb); |
| + |
| + // Generates a "no_longer_needed" closure that holds a reference |
| + // to this pool. |
| + base::Closure frame_callback(); |
| + |
| + // Checks if it is safe to call libvpx. Since libvpx uses LRU to reuse frame |
| + // buffers, it is not safe to call libvpx if the "oldest" frame according to |
| + // libvpx is still in use by chromium. |
| + bool IsSafeToCallDecoder(); |
| + |
| + private: |
| + friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>; |
| + ~MemoryPool(); |
| + |
| + // Method that gets called when a VideoFrame that references this pool gets |
| + // destroyed. |
| + void OnVideoFrameDestroyed(uint64 frame_id); |
| + |
| + // Pointer to the VpxVideoDecoder object that has this object. |
| + VpxVideoDecoder* decoder_; |
| + |
| + // Incrementing counter of frames passed into libvpx. |
| + uint64 next_frame_id_; |
| + |
| + // List of frame ids currently in use by chromium. |
| + std::set<uint64> frame_ids_in_use_; |
| + |
| + vpx_codec_frame_buffer frame_buffers_[kVP9MaxFrameBuffers]; |
| + DISALLOW_COPY_AND_ASSIGN(MemoryPool); |
| +}; |
| + |
| +VpxVideoDecoder::MemoryPool::MemoryPool(VpxVideoDecoder* decoder) |
| + : decoder_(decoder), |
| + next_frame_id_(0) { |
| + memset(frame_buffers_, 0, |
| + sizeof(vpx_codec_frame_buffer) * arraysize(frame_buffers_)); |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
nit: I believe you should be able to just use size
vignesh
2013/12/20 19:59:40
I tried that before i used this, it did not work.
|
| +} |
| + |
| +VpxVideoDecoder::MemoryPool::~MemoryPool() { |
| + for (size_t i = 0; i < arraysize(frame_buffers_); ++i) |
| + delete[] frame_buffers_[i].data; |
| +} |
| + |
| +int32 VpxVideoDecoder::MemoryPool::ReallocVP9FrameBuffer( |
| + void* user_priv, size_t new_size, vpx_codec_frame_buffer* fb) { |
| + if (!fb) |
| + return -1; |
| + if (fb->data) |
| + delete[] fb->data; |
| + fb->data = new uint8[new_size]; |
| + if (!fb->data) { |
| + fb->size = 0; |
| + return -1; |
| + } |
| + fb->size = new_size; |
| + return VPX_CODEC_OK; |
| + } |
| + |
| +base::Closure VpxVideoDecoder::MemoryPool::frame_callback() { |
| + frame_ids_in_use_.insert(next_frame_id_); |
| + return BindToCurrentLoop( |
| + base::Bind(&MemoryPool::OnVideoFrameDestroyed, this, |
| + next_frame_id_++)); |
| +} |
| + |
| +void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed(uint64 frame_id) { |
| + frame_ids_in_use_.erase(frame_id); |
| + if (decoder_->pending_buffer_ && IsSafeToCallDecoder()) { |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
You'll need a decoder_ null check and a way for th
vignesh
2013/12/20 19:59:40
nice catch. Done.
|
| + decoder_->DecodeBuffer(decoder_->pending_buffer_); |
| + decoder_->pending_buffer_ = NULL; |
| + } |
| +} |
| + |
| +bool VpxVideoDecoder::MemoryPool::IsSafeToCallDecoder() { |
| + return next_frame_id_ < limits::kMaxVideoFrames || |
| + frame_ids_in_use_.find(next_frame_id_ - limits::kMaxVideoFrames) == |
| + frame_ids_in_use_.end(); |
| +} |
| + |
| VpxVideoDecoder::VpxVideoDecoder( |
| const scoped_refptr<base::MessageLoopProxy>& message_loop) |
| : message_loop_(message_loop), |
| weak_factory_(this), |
| state_(kUninitialized), |
| vpx_codec_(NULL), |
| - vpx_codec_alpha_(NULL) { |
| + vpx_codec_alpha_(NULL), |
| + memory_pool_(NULL), |
| + pending_buffer_(NULL) { |
| } |
| VpxVideoDecoder::~VpxVideoDecoder() { |
| @@ -142,6 +246,25 @@ 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(this); |
| + if (vpx_codec_set_frame_buffers( |
| + vpx_codec_, |
| + memory_pool_->frame_buffers(), |
| + memory_pool_->frame_buffers_size(), |
| + &MemoryPool::ReallocVP9FrameBuffer, |
| + NULL)) { |
| + LOG(ERROR) << "Failed to configure external buffers."; |
| + return false; |
| + } |
| + if (vpx_codec_control(vpx_codec_, VP9D_SET_FRAME_BUFFER_LRU_CACHE, 1)) { |
| + LOG(ERROR) << "Failed to set frame buffer lru cache."; |
| + return false; |
| + } |
| + } |
| + |
| if (config.format() == VideoFrame::YV12A) { |
| vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config); |
| if (!vpx_codec_alpha_) |
| @@ -156,6 +279,8 @@ void VpxVideoDecoder::CloseDecoder() { |
| vpx_codec_destroy(vpx_codec_); |
| delete vpx_codec_; |
| vpx_codec_ = NULL; |
| + memory_pool_ = NULL; |
|
acolwell GONE FROM CHROMIUM
2013/12/20 17:26:44
I think right before this line is where you need t
vignesh
2013/12/20 19:59:40
Done.
|
| + pending_buffer_ = NULL; |
| } |
| if (vpx_codec_alpha_) { |
| vpx_codec_destroy(vpx_codec_alpha_); |
| @@ -184,6 +309,12 @@ void VpxVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| return; |
| } |
| + // Check if it is safe to call libvpx. |
| + if (memory_pool_ && !memory_pool_->IsSafeToCallDecoder()) { |
| + pending_buffer_ = buffer; |
| + return; |
| + } |
| + |
| DecodeBuffer(buffer); |
| } |
| @@ -342,6 +473,21 @@ 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_->frame_callback()); |
| + return; |
| + } |
| + |
| *video_frame = frame_pool_.CreateFrame( |
| vpx_codec_alpha_ ? VideoFrame::YV12A : VideoFrame::YV12, |
| size, |