-#include "../../stdafx.h"\r
-\r
-#include "filter.h"\r
-\r
-#include "../../ffmpeg_error.h"\r
-\r
-#include <common/exception/exceptions.h>\r
-#include <core/producer/frame/basic_frame.h>\r
-#include <core/producer/frame/frame_factory.h>\r
-#include <core/mixer/write_frame.h>\r
-\r
-#include <boost/circular_buffer.hpp>\r
-\r
-#include <cstdio>\r
-#include <sstream>\r
-\r
-extern "C" \r
-{\r
- #define __STDC_CONSTANT_MACROS\r
- #define __STDC_LIMIT_MACROS\r
- #include <libavutil/avutil.h>\r
- #include <libavutil/imgutils.h>\r
- #include <libavfilter/avfilter.h>\r
- #include <libavfilter/avcodec.h>\r
- #include <libavfilter/vsrc_buffer.h>\r
- #include <libavfilter/avfiltergraph.h>\r
-}\r
-\r
-namespace caspar {\r
- \r
-struct filter::implementation\r
-{\r
- std::string filters_;\r
- std::shared_ptr<AVFilterGraph> graph_;\r
- AVFilterContext* video_in_filter_;\r
- AVFilterContext* video_out_filter_;\r
-\r
- implementation(const std::string& filters) \r
- : filters_(filters)\r
- {\r
- std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
- }\r
-\r
- void push(const safe_ptr<AVFrame>& frame)\r
- { \r
- int errn = 0; \r
-\r
- if(!graph_)\r
- {\r
- graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
- \r
- // Input\r
- std::stringstream buffer_ss;\r
- buffer_ss << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
- errn = avfilter_graph_create_filter(&video_in_filter_, avfilter_get_by_name("buffer"), "src", buffer_ss.str().c_str(), NULL, graph_.get());\r
- if(errn < 0 || !video_in_filter_)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
-\r
- // Output\r
- errn = avfilter_graph_create_filter(&video_out_filter_, avfilter_get_by_name("nullsink"), "out", NULL, NULL, graph_.get());\r
- if(errn < 0 || !video_out_filter_)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
- \r
- AVFilterInOut* outputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
- AVFilterInOut* inputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
-\r
- outputs->name = av_strdup("in");\r
- outputs->filter_ctx = video_in_filter_;\r
- outputs->pad_idx = 0;\r
- outputs->next = NULL;\r
-\r
- inputs->name = av_strdup("out");\r
- inputs->filter_ctx = video_out_filter_;\r
- inputs->pad_idx = 0;\r
- inputs->next = NULL;\r
- \r
- errn = avfilter_graph_parse(graph_.get(), filters_.c_str(), inputs, outputs, NULL);\r
- if(errn < 0)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
-\r
-// av_free(outputs);\r
-// av_free(inputs);\r
-\r
- errn = avfilter_graph_config(graph_.get(), NULL);\r
- if(errn < 0)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) \r
- << boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
- }\r
- \r
- errn = av_vsrc_buffer_add_frame(video_in_filter_, frame.get(), 0);\r
- if(errn < 0)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
- }\r
-\r
- std::vector<safe_ptr<AVFrame>> poll()\r
- {\r
- int errn = avfilter_poll_frame(video_out_filter_->inputs[0]);\r
- if(errn < 0)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("avfilter_poll_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
-\r
- std::vector<safe_ptr<AVFrame>> result;\r
-\r
- std::generate_n(std::back_inserter(result), errn, [&]{return request_frame();});\r
-\r
- return result;\r
- }\r
- \r
- safe_ptr<AVFrame> request_frame()\r
- { \r
- auto link = video_out_filter_->inputs[0];\r
- \r
- int errn = avfilter_request_frame(link); \r
- if(errn < 0)\r
- {\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
- boost::errinfo_api_function("avfilter_request_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
- }\r
- \r
- auto cur_buf = link->cur_buf;\r
- auto pic = reinterpret_cast<AVPicture*>(link->cur_buf->buf);\r
- \r
- safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
- {\r
- av_free(p);\r
- avfilter_unref_buffer(cur_buf);\r
- });\r
-\r
- avcodec_get_frame_defaults(frame.get()); \r
-\r
- for(size_t n = 0; n < 4; ++n)\r
- {\r
- frame->data[n] = pic->data[n];\r
- frame->linesize[n] = pic->linesize[n];\r
- }\r
-\r
- frame->width = link->cur_buf->video->w;\r
- frame->height = link->cur_buf->video->h;\r
- frame->format = link->cur_buf->format;\r
- frame->interlaced_frame = link->cur_buf->video->interlaced;\r
- frame->top_field_first = link->cur_buf->video->top_field_first;\r
- frame->key_frame = link->cur_buf->video->key_frame;\r
-\r
- return frame;\r
- }\r
-};\r
-\r
-filter::filter(const std::string& filters) : impl_(new implementation(filters)){}\r
-void filter::push(const safe_ptr<AVFrame>& frame) {impl_->push(frame);}\r
-std::vector<safe_ptr<AVFrame>> filter::poll() {return impl_->poll();}\r
-bool filter::is_ready() const{return impl_->graph_ != nullptr;}\r
-\r
-}
\ 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 <http://www.gnu.org/licenses/>.
+*
+* Author: Robert Nagy, ronag89@gmail.com
+*/
+
+#include "../../StdAfx.h"
+
+#include "filter.h"
+
+#include "../../ffmpeg_error.h"
+#include "../../ffmpeg.h"
+#include "../util/util.h"
+
+#include <common/assert.h>
+#include <common/except.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/thread.hpp>
+#include <boost/format.hpp>
+#include <boost/rational.hpp>
+
+#include <cstdio>
+#include <sstream>
+#include <string>
+#include <queue>
+
+#if defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4244)
+#endif
+extern "C"
+{
+ #include <libavutil/avutil.h>
+ #include <libavutil/imgutils.h>
+ #include <libavutil/opt.h>
+ #include <libavfilter/avfilter.h>
+ #include <libavfilter/avcodec.h>
+ #include <libavfilter/buffersink.h>
+ #include <libavfilter/buffersrc.h>
+}
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
+namespace caspar { namespace ffmpeg {
+struct filter::implementation
+{
+ std::string filtergraph_;
+
+ std::shared_ptr<AVFilterGraph> video_graph_;
+ AVFilterContext* video_graph_in_;
+ AVFilterContext* video_graph_out_;
+
+ std::queue<std::shared_ptr<AVFrame>> fast_path_;
+
+ implementation(
+ int in_width,
+ int in_height,
+ boost::rational<int> in_time_base,
+ boost::rational<int> in_frame_rate,
+ boost::rational<int> in_sample_aspect_ratio,
+ AVPixelFormat in_pix_fmt,
+ std::vector<AVPixelFormat> 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<AVFrame>& 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<AVFrame> 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<int> in_time_base,
+ boost::rational<int> in_frame_rate,
+ boost::rational<int> in_sample_aspect_ratio,
+ AVPixelFormat in_pix_fmt,
+ std::vector<AVPixelFormat> 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<AVFrame>& frame){impl_->push(frame);}
+std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
+std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);}
+std::vector<spl::shared_ptr<AVFrame>> filter::poll_all()
+{
+ std::vector<spl::shared_ptr<AVFrame>> frames;
+ for(auto frame = poll(); frame; frame = poll())
+ frames.push_back(spl::make_shared_ptr(frame));
+ return frames;
+}
+}}