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..45835ebf4ad912c175211706e6f519641714f0c9 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,80 @@ 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; |
+ |
+// Minimum libvpx decoder ABI version required to support direct rendering. |
+static const int kLibvpxMinABIExternalBuffers = 6; |
+ |
+class VpxVideoDecoder::MemoryPool |
+ : public base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool> { |
+ public: |
+ 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(); |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<VpxVideoDecoder::MemoryPool>; |
+ ~MemoryPool(); |
+ |
+ // Empty method that gets called when a VideoFrame that references |
+ // this pool gets destroyed. |
+ void OnVideoFrameDestroyed(); |
+ |
+ vpx_codec_frame_buffer frame_buffers_[kVP9MaxFrameBuffers]; |
acolwell GONE FROM CHROMIUM
2013/12/19 15:26:12
Add DISALLOW_COPY_AND_ASSIGN(MemoryPool).
vignesh
2013/12/19 20:27:21
Done.
|
+}; |
+ |
+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() { |
+ return base::Bind(&MemoryPool::OnVideoFrameDestroyed, this); |
+} |
+ |
+void VpxVideoDecoder::MemoryPool::OnVideoFrameDestroyed() { |
+} |
+ |
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) { |
} |
VpxVideoDecoder::~VpxVideoDecoder() { |
@@ -142,6 +211,28 @@ 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 && |
+ VPX_DECODER_ABI_VERSION >= kLibvpxMinABIExternalBuffers) { |
acolwell GONE FROM CHROMIUM
2013/12/19 15:26:12
Is this really needed? Can this code even build w/
vignesh
2013/12/19 20:27:21
We don't need this. Getting rid of it.
|
+ memory_pool_ = new MemoryPool(); |
+ 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; |
+ } |
+ // Ask libvpx to use LRU to reuse frame buffers so that it doesn't |
+ // overwrite the buffers currently in use by chromium (jitter buffers). |
+ 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 +247,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_); |
@@ -342,6 +434,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], |
acolwell GONE FROM CHROMIUM
2013/12/19 15:26:12
It would be nice if vpx_image contained a vpx_code
vignesh
2013/12/19 20:27:21
frank's comments address this.
|
+ 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, |
@@ -361,8 +468,6 @@ void VpxVideoDecoder::CopyVpxImageTo(const vpx_image* vpx_image, |
vpx_image->stride[VPX_PLANE_V], |
(vpx_image->d_h + 1) / 2, |
video_frame->get()); |
- if (!vpx_codec_alpha_) |
acolwell GONE FROM CHROMIUM
2013/12/19 15:26:12
You should probably leave this in here since the m
vignesh
2013/12/19 20:27:21
Done.
|
- return; |
if (!vpx_image_alpha) { |
MakeOpaqueAPlane( |
vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h, video_frame->get()); |