// 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 video_frame) { #ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT. RefCountedAVFrame* ffmpeg_video_frame = reinterpret_cast(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 FFmpegVideoAllocator::DecodeDone( AVCodecContext* codec_context, AVFrame* av_frame) { scoped_refptr frame; #ifdef FF_THREAD_FRAME // Only defined in FFMPEG-MT. RefCountedAVFrame* ffmpeg_video_frame = reinterpret_cast(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(codec_context->opaque); return context->InternalAllocateBuffer(codec_context, av_frame); } void FFmpegVideoAllocator::ReleaseBuffer(AVCodecContext* codec_context, AVFrame* av_frame) { FFmpegVideoAllocator* context = reinterpret_cast(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(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