summaryrefslogtreecommitdiffstats
path: root/media/video/ffmpeg_video_allocator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/video/ffmpeg_video_allocator.cc')
-rw-r--r--media/video/ffmpeg_video_allocator.cc179
1 files changed, 179 insertions, 0 deletions
diff --git a/media/video/ffmpeg_video_allocator.cc b/media/video/ffmpeg_video_allocator.cc
new file mode 100644
index 0000000..e9530b35
--- /dev/null
+++ b/media/video/ffmpeg_video_allocator.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/video/ffmpeg_video_allocator.h"
+
+#include "base/logging.h"
+#include "media/ffmpeg/ffmpeg_common.h"
+
+// Because Chromium could be build with FFMPEG version other than FFMPEG-MT
+// by using GYP_DEFINES variable "use-system-ffmpeg". The following code will
+// not build with vanilla FFMPEG. We will fall back to "disable direct
+// rendering" when that happens.
+// TODO(jiesun): Actually we could do better than this: we should modify the
+// following code to work with vanilla FFMPEG.
+
+namespace media {
+
+FFmpegVideoAllocator::FFmpegVideoAllocator()
+ : get_buffer_(NULL),
+ release_buffer_(NULL) {
+}
+
+void FFmpegVideoAllocator::Initialize(AVCodecContext* codec_context,
+ VideoFrame::Format surface_format) {
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ surface_format_ = surface_format;
+ get_buffer_ = codec_context->get_buffer;
+ release_buffer_ = codec_context->release_buffer;
+ codec_context->get_buffer = AllocateBuffer;
+ codec_context->release_buffer = ReleaseBuffer;
+ codec_context->opaque = this;
+#endif
+}
+
+void FFmpegVideoAllocator::Stop(AVCodecContext* codec_context) {
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ // Restore default buffer allocator functions.
+ // This does not work actually, because in ffmpeg-mt, there are
+ // multiple codec_context copies per threads. each context maintain
+ // its own internal buffer pools.
+ codec_context->get_buffer = get_buffer_;
+ codec_context->release_buffer = release_buffer_;
+
+ while (!frame_pool_.empty()) {
+ RefCountedAVFrame* ffmpeg_video_frame = frame_pool_.front();
+ frame_pool_.pop_front();
+ ffmpeg_video_frame->av_frame_.opaque = NULL;
+
+ // Reset per-context default buffer release functions.
+ ffmpeg_video_frame->av_frame_.owner->release_buffer = release_buffer_;
+ ffmpeg_video_frame->av_frame_.owner->get_buffer = get_buffer_;
+ delete ffmpeg_video_frame;
+ }
+ for (int i = 0; i < kMaxFFmpegThreads; ++i)
+ available_frames_[i].clear();
+ codec_index_map_.clear();
+#endif
+}
+
+void FFmpegVideoAllocator::DisplayDone(
+ AVCodecContext* codec_context,
+ scoped_refptr<VideoFrame> video_frame) {
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ RefCountedAVFrame* ffmpeg_video_frame =
+ reinterpret_cast<RefCountedAVFrame*>(video_frame->private_buffer());
+ if (ffmpeg_video_frame->Release() == 0) {
+ int index = codec_index_map_[ffmpeg_video_frame->av_frame_.owner];
+ available_frames_[index].push_back(ffmpeg_video_frame);
+ }
+#endif
+}
+
+scoped_refptr<VideoFrame> FFmpegVideoAllocator::DecodeDone(
+ AVCodecContext* codec_context,
+ AVFrame* av_frame) {
+ scoped_refptr<VideoFrame> frame;
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ RefCountedAVFrame* ffmpeg_video_frame =
+ reinterpret_cast<RefCountedAVFrame*>(av_frame->opaque);
+ ffmpeg_video_frame->av_frame_ = *av_frame;
+ ffmpeg_video_frame->AddRef();
+
+ VideoFrame::CreateFrameExternal(
+ VideoFrame::TYPE_SYSTEM_MEMORY, surface_format_,
+ codec_context->width, codec_context->height, 3,
+ av_frame->data,
+ av_frame->linesize,
+ StreamSample::kInvalidTimestamp,
+ StreamSample::kInvalidTimestamp,
+ ffmpeg_video_frame, // |private_buffer_|.
+ &frame);
+#endif
+ return frame;
+}
+
+int FFmpegVideoAllocator::AllocateBuffer(AVCodecContext* codec_context,
+ AVFrame* av_frame) {
+ FFmpegVideoAllocator* context =
+ reinterpret_cast<FFmpegVideoAllocator*>(codec_context->opaque);
+ return context->InternalAllocateBuffer(codec_context, av_frame);
+}
+
+void FFmpegVideoAllocator::ReleaseBuffer(AVCodecContext* codec_context,
+ AVFrame* av_frame) {
+ FFmpegVideoAllocator* context =
+ reinterpret_cast<FFmpegVideoAllocator*>(codec_context->opaque);
+ context->InternalReleaseBuffer(codec_context, av_frame);
+}
+
+int FFmpegVideoAllocator::InternalAllocateBuffer(
+ AVCodecContext* codec_context, AVFrame* av_frame) {
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ // If |codec_context| is not yet known to us, we add it to our map.
+ if (codec_index_map_.find(codec_context) == codec_index_map_.end()) {
+ int next_index = codec_index_map_.size();
+ codec_index_map_[codec_context] = next_index;
+ CHECK_LE((int)codec_index_map_.size(), kMaxFFmpegThreads);
+ }
+
+ int index = codec_index_map_[codec_context];
+
+ RefCountedAVFrame* ffmpeg_video_frame;
+ if (available_frames_[index].empty()) {
+ int ret = get_buffer_(codec_context, av_frame);
+ CHECK_EQ(ret, 0);
+ ffmpeg_video_frame = new RefCountedAVFrame();
+ ffmpeg_video_frame->av_frame_ = *av_frame;
+ frame_pool_.push_back(ffmpeg_video_frame);
+ } else {
+ ffmpeg_video_frame = available_frames_[index].front();
+ available_frames_[index].pop_front();
+ // We assume |get_buffer| immediately after |release_buffer| will
+ // not trigger real buffer allocation. We just use it to fill the
+ // correct value inside |pic|.
+ release_buffer_(codec_context, &ffmpeg_video_frame->av_frame_);
+ get_buffer_(codec_context, av_frame);
+ ffmpeg_video_frame->av_frame_ = *av_frame;
+ }
+
+ av_frame->opaque = ffmpeg_video_frame;
+ av_frame->type = FF_BUFFER_TYPE_USER;
+ ffmpeg_video_frame->AddRef();
+#endif
+ return 0;
+}
+
+void FFmpegVideoAllocator::InternalReleaseBuffer(
+ AVCodecContext* codec_context, AVFrame* av_frame) {
+#ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT.
+ if (av_frame->opaque == NULL) {
+ // This could happened in two scenario:
+ // 1. FFMPEG-MT H264 codec seems to allocate one frame during
+ // av_find_stream_info. This happens before we could even
+ // install the custom allocator functions.
+ // 2. When clean up time, we reset pic->opaque, and destruct ourselves.
+ // We could not use our own release_buffer function because
+ // handle-delayed-release() is called after we get destructed.
+ release_buffer_(codec_context, av_frame);
+ return;
+ }
+
+ RefCountedAVFrame* ffmpeg_video_frame =
+ reinterpret_cast<RefCountedAVFrame*>(av_frame->opaque);
+ release_buffer_(codec_context, av_frame);
+
+ // This is required for get_buffer().
+ ffmpeg_video_frame->av_frame_.data[0] = NULL;
+ get_buffer_(codec_context, &ffmpeg_video_frame->av_frame_);
+ int index = codec_index_map_[codec_context];
+ if (ffmpeg_video_frame->Release() == 0)
+ available_frames_[index].push_back(ffmpeg_video_frame);
+
+ for(int k = 0; k < 4; ++k)
+ av_frame->data[k]=NULL;
+#endif
+}
+
+} // namespace media