X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fproducer%2Ffilter%2Ffilter.cpp;h=aa83cf36662584a583fd758a55b3850c03d0a53b;hb=726897adbf881d3b75f171fff24f2b917ba5f05a;hp=f7f6ac19d05a3ad6c1bff95c8d2ff62991758bd0;hpb=a433c0a30ce042e5e32483b592805102dbaf1eb8;p=casparcg diff --git a/modules/ffmpeg/producer/filter/filter.cpp b/modules/ffmpeg/producer/filter/filter.cpp index f7f6ac19d..aa83cf366 100644 --- a/modules/ffmpeg/producer/filter/filter.cpp +++ b/modules/ffmpeg/producer/filter/filter.cpp @@ -1,222 +1,295 @@ -#include "../../stdafx.h" - -#include "filter.h" - -#include "parallel_yadif.h" - -#include "../../ffmpeg_error.h" - -#include - -#include - -#include -#include - -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4244) -#endif -extern "C" -{ - #include - #include - #include - #include - #include - #include - #include -} -#if defined(_MSC_VER) -#pragma warning (pop) -#endif - -namespace caspar { namespace ffmpeg { - -struct filter::implementation -{ - std::string filters_; - std::shared_ptr graph_; - AVFilterContext* buffersink_ctx_; - AVFilterContext* buffersrc_ctx_; - std::shared_ptr parallel_yadif_ctx_; - std::vector pix_fmts_; - std::queue> bypass_; - - implementation(const std::wstring& filters, const std::vector& pix_fmts) - : filters_(narrow(filters)) - , parallel_yadif_ctx_(nullptr) - , pix_fmts_(pix_fmts) - { - if(pix_fmts_.empty()) - { - pix_fmts_.push_back(PIX_FMT_YUV420P); - pix_fmts_.push_back(PIX_FMT_YUVA420P); - pix_fmts_.push_back(PIX_FMT_YUV422P); - pix_fmts_.push_back(PIX_FMT_YUV444P); - pix_fmts_.push_back(PIX_FMT_YUV411P); - pix_fmts_.push_back(PIX_FMT_ARGB); - pix_fmts_.push_back(PIX_FMT_RGBA); - pix_fmts_.push_back(PIX_FMT_ABGR); - pix_fmts_.push_back(PIX_FMT_GRAY8); - pix_fmts_.push_back(PIX_FMT_NONE); - } - else - pix_fmts_.push_back(PIX_FMT_NONE); - - std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower); - } - - void push(const std::shared_ptr& frame) - { - if(!frame) - return; - - if(frame->data[0] == nullptr || frame->width < 1) - BOOST_THROW_EXCEPTION(invalid_argument()); - - if(filters_.empty()) - { - bypass_.push(frame); - return; - } - - try - { - if(!graph_) - { - try - { - graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);}); - - // Input - std::stringstream args; - args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio - THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]"); - - // OPIX_FMT_BGRAutput - AVBufferSinkParams *buffersink_params = av_buffersink_params_alloc(); - buffersink_params->pixel_fmts = pix_fmts_.data(); - THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params, graph_.get()), "[filter]"); - - AVFilterInOut* outputs = avfilter_inout_alloc(); - AVFilterInOut* inputs = avfilter_inout_alloc(); - - outputs->name = av_strdup("in"); - outputs->filter_ctx = buffersrc_ctx_; - outputs->pad_idx = 0; - outputs->next = NULL; - - inputs->name = av_strdup("out"); - inputs->filter_ctx = buffersink_ctx_; - inputs->pad_idx = 0; - inputs->next = NULL; - - THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL), "[filter]"); - - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - - THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]"); - - for(size_t n = 0; n < graph_->filter_count; ++n) - { - auto filter_name = graph_->filters[n]->name; - if(strstr(filter_name, "yadif") != 0) - parallel_yadif_ctx_ = make_parallel_yadif(graph_->filters[n]); - } - } - catch(...) - { - graph_ = nullptr; - throw; - } - } - - THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]"); - } - catch(ffmpeg_error&) - { - throw; - } - catch(...) - { - BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception())); - } - } - - std::shared_ptr poll() - { - if(filters_.empty()) - { - if(bypass_.empty()) - return nullptr; - auto frame = bypass_.front(); - bypass_.pop(); - return frame; - } - - if(!graph_) - return nullptr; - - try - { - if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) - { - AVFilterBufferRef *picref; - THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]"); - - if (picref) - { - safe_ptr frame(avcodec_alloc_frame(), [=](AVFrame* p) - { - av_free(p); - avfilter_unref_buffer(picref); - }); - - avcodec_get_frame_defaults(frame.get()); - - memcpy(frame->data, picref->data, sizeof(frame->data)); - memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize)); - frame->format = picref->format; - frame->width = picref->video->w; - frame->height = picref->video->h; - frame->pkt_pos = picref->pos; - frame->interlaced_frame = picref->video->interlaced; - frame->top_field_first = picref->video->top_field_first; - frame->key_frame = picref->video->key_frame; - frame->pict_type = picref->video->pict_type; - frame->sample_aspect_ratio = picref->video->sample_aspect_ratio; - - return frame; - } - } - } - catch(ffmpeg_error&) - { - throw; - } - catch(...) - { - BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception())); - } - - return nullptr; - } -}; - -filter::filter(const std::wstring& filters, const std::vector& pix_fmts) : impl_(new implementation(filters, pix_fmts)){} -filter::filter(filter&& other) : impl_(std::move(other.impl_)){} -filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;} -void filter::push(const std::shared_ptr& frame){impl_->push(frame);} -std::shared_ptr filter::poll(){return impl_->poll();} -std::string filter::filter_str() const{return impl_->filters_;} -std::vector> filter::poll_all() -{ - std::vector> frames; - for(auto frame = poll(); frame; frame = poll()) - frames.push_back(make_safe_ptr(frame)); - return frames; -} - -}} \ No newline at end of file +/* +* Copyright 2013 Sveriges Television AB http://casparcg.com/ +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + +#include "../../StdAfx.h" + +#include "filter.h" + +#include "../../ffmpeg_error.h" +#include "../../ffmpeg.h" +#include "../util/util.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4244) +#endif +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include +} +#if defined(_MSC_VER) +#pragma warning (pop) +#endif + +namespace caspar { namespace ffmpeg { +struct filter::implementation +{ + std::string filtergraph_; + + std::shared_ptr video_graph_; + AVFilterContext* video_graph_in_; + AVFilterContext* video_graph_out_; + + std::queue> fast_path_; + + implementation( + int in_width, + int in_height, + boost::rational in_time_base, + boost::rational in_frame_rate, + boost::rational in_sample_aspect_ratio, + AVPixelFormat in_pix_fmt, + std::vector out_pix_fmts, + const std::string& filtergraph, + bool multithreaded) + : filtergraph_(boost::to_lower_copy(filtergraph)) + { + if(out_pix_fmts.empty()) + { + out_pix_fmts = { + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_BGRA, + AV_PIX_FMT_ARGB, + AV_PIX_FMT_RGBA, + AV_PIX_FMT_ABGR, + AV_PIX_FMT_GRAY8 + }; + } + + out_pix_fmts.push_back(AV_PIX_FMT_NONE); + + video_graph_.reset( + avfilter_graph_alloc(), + [](AVFilterGraph* p) + { + avfilter_graph_free(&p); + }); + + if (multithreaded) + { + video_graph_->nb_threads = 0; + video_graph_->thread_type = AVFILTER_THREAD_SLICE; + } + else + { + video_graph_->nb_threads = 1; + } + + const auto vsrc_options = (boost::format("video_size=%1%x%2%:pix_fmt=%3%:time_base=%4%/%5%:pixel_aspect=%6%/%7%:frame_rate=%8%/%9%") + % in_width % in_height + % in_pix_fmt + % in_time_base.numerator() % in_time_base.denominator() + % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator() + % in_frame_rate.numerator() % in_frame_rate.denominator()).str(); + + AVFilterContext* filt_vsrc = nullptr; + FF(avfilter_graph_create_filter( + &filt_vsrc, + avfilter_get_by_name("buffer"), + "filter_buffer", + vsrc_options.c_str(), + nullptr, + video_graph_.get())); + + AVFilterContext* filt_vsink = nullptr; + FF(avfilter_graph_create_filter( + &filt_vsink, + avfilter_get_by_name("buffersink"), + "filter_buffersink", + nullptr, + nullptr, + video_graph_.get())); + +#pragma warning (push) +#pragma warning (disable : 4245) + + FF(av_opt_set_int_list( + filt_vsink, + "pix_fmts", + out_pix_fmts.data(), + -1, + AV_OPT_SEARCH_CHILDREN)); + +#pragma warning (pop) + + configure_filtergraph( + *video_graph_, + filtergraph_, + *filt_vsrc, + *filt_vsink); + + video_graph_in_ = filt_vsrc; + video_graph_out_ = filt_vsink; + + if (is_logging_quiet_for_thread()) + CASPAR_LOG(trace) + << u16(std::string("\n") + + avfilter_graph_dump( + video_graph_.get(), + nullptr)); + else + CASPAR_LOG(debug) + << u16(std::string("\n") + + avfilter_graph_dump( + video_graph_.get(), + nullptr)); + } + + void configure_filtergraph( + AVFilterGraph& graph, + const std::string& filtergraph, + AVFilterContext& source_ctx, + AVFilterContext& sink_ctx) + { + if (!filtergraph.empty()) + { + auto outputs = avfilter_inout_alloc(); + auto inputs = avfilter_inout_alloc(); + + CASPAR_VERIFY(outputs && inputs); + + outputs->name = av_strdup("in"); + outputs->filter_ctx = &source_ctx; + outputs->pad_idx = 0; + outputs->next = nullptr; + + inputs->name = av_strdup("out"); + inputs->filter_ctx = &sink_ctx; + inputs->pad_idx = 0; + inputs->next = nullptr; + + FF(avfilter_graph_parse( + &graph, + filtergraph.c_str(), + inputs, + outputs, + nullptr)); + } + else + { + FF(avfilter_link( + &source_ctx, + 0, + &sink_ctx, + 0)); + } + + FF(avfilter_graph_config(&graph, nullptr)); + } + + bool fast_path() const + { + return filtergraph_.empty(); + } + + void push(const std::shared_ptr& src_av_frame) + { + if (fast_path()) + fast_path_.push(src_av_frame); + else + FF(av_buffersrc_add_frame( + video_graph_in_, + src_av_frame.get())); + } + + std::shared_ptr poll() + { + if (fast_path()) + { + if (fast_path_.empty()) + return nullptr; + + auto result = fast_path_.front(); + fast_path_.pop(); + return result; + } + + auto filt_frame = create_frame(); + + const auto ret = av_buffersink_get_frame( + video_graph_out_, + filt_frame.get()); + + if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + return nullptr; + + FF_RET(ret, "poll"); + + return filt_frame; + } +}; + +filter::filter( + int in_width, + int in_height, + boost::rational in_time_base, + boost::rational in_frame_rate, + boost::rational in_sample_aspect_ratio, + AVPixelFormat in_pix_fmt, + std::vector out_pix_fmts, + const std::string& filtergraph, + bool multithreaded) + : impl_(new implementation( + in_width, + in_height, + in_time_base, + in_frame_rate, + in_sample_aspect_ratio, + in_pix_fmt, + out_pix_fmts, + filtergraph, + multithreaded)){} +filter::filter(filter&& other) : impl_(std::move(other.impl_)){} +filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;} +void filter::push(const std::shared_ptr& frame){impl_->push(frame);} +std::shared_ptr filter::poll(){return impl_->poll();} +std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);} +std::vector> filter::poll_all() +{ + std::vector> frames; + for(auto frame = poll(); frame; frame = poll()) + frames.push_back(spl::make_shared_ptr(frame)); + return frames; +} +}}