]> git.sesse.net Git - casparcg/blobdiff - modules/ffmpeg/producer/filter/filter.cpp
[ffmpeg] Ported 2.0.7 ffmpeg producer to 2.1.0 while still keeping the usage of the...
[casparcg] / modules / ffmpeg / producer / filter / filter.cpp
index 9a5d26688c49fa825c40cb21ccc70a532d61270e..aa83cf36662584a583fd758a55b3850c03d0a53b 100644 (file)
-#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/avfiltergraph.h>\r
-       #include <libavfilter/vsink_buffer.h>\r
-       #include <libavfilter/vsrc_buffer.h>\r
-}\r
-\r
-namespace caspar {\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
-       \r
-       implementation(const std::wstring& filters) \r
-               : filters_(narrow(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 args;\r
-                       args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
-                       errn = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get());\r
-                       if(errn < 0)\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
-                       PixelFormat pix_fmts[] = { PIX_FMT_BGRA, PIX_FMT_NONE };\r
-\r
-                       // Output\r
-                       errn = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts, graph_.get());\r
-                       if(errn < 0)\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 = 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
-                       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
-                       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(buffersrc_ctx_, 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
-               std::vector<safe_ptr<AVFrame>> result;\r
-\r
-               if(!graph_)\r
-                       return result;\r
-               \r
-               while (avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
-               {\r
-                       AVFilterBufferRef *picref;\r
-            av_vsink_buffer_get_video_buffer_ref(buffersink_ctx_, &picref, 0);\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
-                               for(size_t n = 0; n < 4; ++n)\r
-                               {\r
-                                       frame->data[n]          = picref->data[n];\r
-                                       frame->linesize[n]      = picref->linesize[n];\r
-                               }\r
-                               \r
-                               frame->format                   = picref->format;\r
-                               frame->width                    = picref->video->w;\r
-                               frame->height                   = picref->video->h;\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
-\r
-                               result.push_back(frame);\r
-            }\r
-        }\r
-\r
-               return result;\r
-       }\r
-};\r
-\r
-filter::filter(const std::wstring& 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
-}
\ 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;
+}
+}}