| // 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) { |
| } |
| |
| FFmpegVideoAllocator::~FFmpegVideoAllocator() {} |
| |
| 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, |
| kNoTimestamp, |
| kNoTimestamp, |
| 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 |