-/*\r
-* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>\r
-*\r
-* This file is part of CasparCG (www.casparcg.com).\r
-*\r
-* CasparCG is free software: you can redistribute it and/or modify\r
-* it under the terms of the GNU General Public License as published by\r
-* the Free Software Foundation, either version 3 of the License, or\r
-* (at your option) any later version.\r
-*\r
-* CasparCG is distributed in the hope that it will be useful,\r
-* but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-* GNU General Public License for more details.\r
-*\r
-* You should have received a copy of the GNU General Public License\r
-* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
-*\r
-* Author: Robert Nagy, ronag89@gmail.com\r
-*/\r
-\r
-#include "../../stdafx.h"\r
-\r
-#include "filter.h"\r
-\r
-#include "parallel_yadif.h"\r
-\r
-#include "../../ffmpeg_error.h"\r
-\r
-#include <common/exception/exceptions.h>\r
-\r
-#include <boost/assign.hpp>\r
-\r
-#include <cstdio>\r
-#include <sstream>\r
-\r
-#if defined(_MSC_VER)\r
-#pragma warning (push)\r
-#pragma warning (disable : 4244)\r
-#endif\r
-extern "C" \r
-{\r
- #include <libavutil/avutil.h>\r
- #include <libavutil/imgutils.h>\r
- #include <libavfilter/avfilter.h>\r
- #include <libavfilter/avcodec.h>\r
- #include <libavfilter/avfiltergraph.h>\r
- #include <libavfilter/buffersink.h>\r
- #include <libavfilter/vsrc_buffer.h>\r
-}\r
-#if defined(_MSC_VER)\r
-#pragma warning (pop)\r
-#endif\r
-\r
-namespace caspar { namespace ffmpeg {\r
- \r
-struct filter::implementation\r
-{\r
- std::string filters_;\r
- std::shared_ptr<AVFilterGraph> graph_; \r
- AVFilterContext* buffersink_ctx_;\r
- AVFilterContext* buffersrc_ctx_;\r
- std::shared_ptr<void> parallel_yadif_ctx_;\r
- std::vector<PixelFormat> pix_fmts_;\r
- std::queue<std::shared_ptr<AVFrame>> bypass_;\r
- \r
- implementation(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) \r
- : filters_(narrow(filters))\r
- , parallel_yadif_ctx_(nullptr)\r
- , pix_fmts_(pix_fmts)\r
- {\r
- if(pix_fmts_.empty())\r
- {\r
- pix_fmts_.push_back(PIX_FMT_YUVA420P);\r
- pix_fmts_.push_back(PIX_FMT_YUV444P);\r
- pix_fmts_.push_back(PIX_FMT_YUV422P);\r
- pix_fmts_.push_back(PIX_FMT_YUV420P);\r
- pix_fmts_.push_back(PIX_FMT_YUV411P);\r
- pix_fmts_.push_back(PIX_FMT_BGRA);\r
- pix_fmts_.push_back(PIX_FMT_ARGB);\r
- pix_fmts_.push_back(PIX_FMT_RGBA);\r
- pix_fmts_.push_back(PIX_FMT_ABGR);\r
- pix_fmts_.push_back(PIX_FMT_GRAY8);\r
- pix_fmts_.push_back(PIX_FMT_NONE);\r
- }\r
- else\r
- pix_fmts_.push_back(PIX_FMT_NONE);\r
-\r
- std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
- }\r
- \r
- void push(const std::shared_ptr<AVFrame>& frame)\r
- { \r
- if(!frame)\r
- return;\r
-\r
- if(frame->data[0] == nullptr || frame->width < 1)\r
- BOOST_THROW_EXCEPTION(invalid_argument());\r
-\r
- if(filters_.empty())\r
- {\r
- bypass_.push(frame);\r
- return;\r
- }\r
- \r
- try\r
- {\r
- if(!graph_)\r
- {\r
- try\r
- {\r
- graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
- \r
- // Input\r
- std::stringstream args;\r
- args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
- THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");\r
- \r
-#if FF_API_OLD_VSINK_API\r
- THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");\r
-#else\r
- safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);\r
- buffersink_params->pixel_fmts = pix_fmts_.data();\r
- THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");\r
-#endif\r
- AVFilterInOut* outputs = avfilter_inout_alloc();\r
- AVFilterInOut* inputs = avfilter_inout_alloc();\r
- \r
- outputs->name = av_strdup("in");\r
- outputs->filter_ctx = buffersrc_ctx_;\r
- outputs->pad_idx = 0;\r
- outputs->next = NULL;\r
-\r
- inputs->name = av_strdup("out");\r
- inputs->filter_ctx = buffersink_ctx_;\r
- inputs->pad_idx = 0;\r
- inputs->next = NULL;\r
- \r
- THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL), "[filter]");\r
- \r
- avfilter_inout_free(&inputs);\r
- avfilter_inout_free(&outputs);\r
-\r
- THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]"); \r
-\r
- for(size_t n = 0; n < graph_->filter_count; ++n)\r
- {\r
- auto filter_name = graph_->filters[n]->name;\r
- if(strstr(filter_name, "yadif") != 0)\r
- parallel_yadif_ctx_ = make_parallel_yadif(graph_->filters[n]);\r
- }\r
- }\r
- catch(...)\r
- {\r
- graph_ = nullptr;\r
- throw;\r
- }\r
- }\r
- \r
- THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");\r
- }\r
- catch(ffmpeg_error&)\r
- {\r
- throw;\r
- }\r
- catch(...)\r
- {\r
- BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
- }\r
- }\r
-\r
- std::shared_ptr<AVFrame> poll()\r
- {\r
- if(filters_.empty())\r
- {\r
- if(bypass_.empty())\r
- return nullptr;\r
- auto frame = bypass_.front();\r
- bypass_.pop();\r
- return frame;\r
- }\r
-\r
- if(!graph_)\r
- return nullptr;\r
- \r
- try\r
- {\r
- if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
- {\r
- AVFilterBufferRef *picref;\r
- THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");\r
-\r
- if (picref) \r
- { \r
- safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
- {\r
- av_free(p);\r
- avfilter_unref_buffer(picref);\r
- });\r
-\r
- avcodec_get_frame_defaults(frame.get()); \r
-\r
- memcpy(frame->data, picref->data, sizeof(frame->data));\r
- memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));\r
- frame->format = picref->format;\r
- frame->width = picref->video->w;\r
- frame->height = picref->video->h;\r
- frame->pkt_pos = picref->pos;\r
- frame->interlaced_frame = picref->video->interlaced;\r
- frame->top_field_first = picref->video->top_field_first;\r
- frame->key_frame = picref->video->key_frame;\r
- frame->pict_type = picref->video->pict_type;\r
- frame->sample_aspect_ratio = picref->video->sample_aspect_ratio;\r
- \r
- return frame;\r
- }\r
- }\r
- }\r
- catch(ffmpeg_error&)\r
- {\r
- throw;\r
- }\r
- catch(...)\r
- {\r
- BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
- }\r
-\r
- return nullptr;\r
- }\r
-};\r
-\r
-filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new implementation(filters, pix_fmts)){}\r
-filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
-filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
-void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
-std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
-std::wstring filter::filter_str() const{return widen(impl_->filters_);}\r
-std::vector<safe_ptr<AVFrame>> filter::poll_all()\r
-{ \r
- std::vector<safe_ptr<AVFrame>> frames;\r
- for(auto frame = poll(); frame; frame = poll())\r
- frames.push_back(make_safe_ptr(frame));\r
- return frames;\r
-}\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;
+}
+}}