]> 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 598d6a72c90ea90fafcf790bf40fb14369189b10..aa83cf36662584a583fd758a55b3850c03d0a53b 100644 (file)
-/*\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
-#include <boost/range/iterator_range.hpp>\r
-#include <boost/range/adaptors.hpp>\r
-#include <boost/assign.hpp>\r
-#include <boost/algorithm/string.hpp>\r
-#include <boost/foreach.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
-\r
-namespace caspar { namespace ffmpeg {\r
-\r
-static int query_formats_444(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUV444P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-static int query_formats_422(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUV422P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-static int query_formats_420(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUV420P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-static int query_formats_420a(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUVA420P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-static int query_formats_411(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUV411P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-static int query_formats_410(AVFilterContext *ctx)\r
-{\r
-    static const int pix_fmts[] = {PIX_FMT_YUV410P, PIX_FMT_NONE};\r
-    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
-    return 0;\r
-}\r
-\r
-struct filter::impl\r
-{\r
-       std::wstring                                    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<safe_ptr<AVFrame>>   bypass_;\r
-               \r
-       impl(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) \r
-               : filters_(filters)\r
-               , parallel_yadif_ctx_(nullptr)\r
-               , pix_fmts_(pix_fmts)\r
-       {\r
-               if(pix_fmts_.empty())\r
-               {\r
-                       pix_fmts_ = boost::assign::list_of\r
-                               (PIX_FMT_YUVA420P)\r
-                               (PIX_FMT_YUV444P)\r
-                               (PIX_FMT_YUV422P)\r
-                               (PIX_FMT_YUV420P)\r
-                               (PIX_FMT_YUV411P)\r
-                               (PIX_FMT_BGRA)\r
-                               (PIX_FMT_ARGB)\r
-                               (PIX_FMT_RGBA)\r
-                               (PIX_FMT_ABGR)\r
-                               (PIX_FMT_GRAY8);\r
-               }\r
-               \r
-               pix_fmts_.push_back(PIX_FMT_NONE);\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(make_safe_ptr(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* inputs  = avfilter_inout_alloc();\r
-                                       AVFilterInOut* outputs = avfilter_inout_alloc();\r
-                                                               \r
-                                       outputs->name                   = av_strdup("in");\r
-                                       outputs->filter_ctx             = buffersrc_ctx_;\r
-                                       outputs->pad_idx                = 0;\r
-                                       outputs->next                   = nullptr;\r
-\r
-                                       inputs->name                    = av_strdup("out");\r
-                                       inputs->filter_ctx              = buffersink_ctx_;\r
-                                       inputs->pad_idx                 = 0;\r
-                                       inputs->next                    = nullptr;\r
-                       \r
-                                       std::string filters = boost::to_lower_copy(u8(filters_));\r
-                                       THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters.c_str(), &inputs, &outputs, NULL), "[filter]");\r
-                       \r
-                                       auto yadif_filter = boost::adaptors::filtered([&](AVFilterContext* p){return strstr(p->name, "yadif") != 0;});\r
-\r
-                                       BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)\r
-                                       {\r
-                                               // Don't trust that libavfilter chooses optimal format.\r
-                                               filter_ctx->filter->query_formats = [&]() -> int (*)(AVFilterContext*)\r
-                                               {\r
-                                                       switch(frame->format)\r
-                                                       {\r
-                                                       case PIX_FMT_YUV444P16: \r
-                                                       case PIX_FMT_YUV444P10: \r
-                                                       case PIX_FMT_YUV444P9:          \r
-                                                       case PIX_FMT_YUV444P:   \r
-                                                       case PIX_FMT_BGR24:             \r
-                                                       case PIX_FMT_RGB24:     \r
-                                                               return query_formats_444;\r
-                                                       case PIX_FMT_YUV422P16: \r
-                                                       case PIX_FMT_YUV422P10: \r
-                                                       case PIX_FMT_YUV422P9:  \r
-                                                       case PIX_FMT_YUV422P:   \r
-                                                       case PIX_FMT_UYVY422:   \r
-                                                       case PIX_FMT_YUYV422:   \r
-                                                               return query_formats_422;\r
-                                                       case PIX_FMT_YUV420P16: \r
-                                                       case PIX_FMT_YUV420P10: \r
-                                                       case PIX_FMT_YUV420P9:  \r
-                                                       case PIX_FMT_YUV420P:   \r
-                                                               return query_formats_420;\r
-                                                       case PIX_FMT_YUVA420P:  \r
-                                                       case PIX_FMT_BGRA:              \r
-                                                       case PIX_FMT_RGBA:              \r
-                                                       case PIX_FMT_ABGR:              \r
-                                                       case PIX_FMT_ARGB:              \r
-                                                               return query_formats_420a;\r
-                                                       case PIX_FMT_UYYVYY411: \r
-                                                       case PIX_FMT_YUV411P:   \r
-                                                               return query_formats_411;\r
-                                                       case PIX_FMT_YUV410P:   \r
-                                                               return query_formats_410;\r
-                                                       default:                                \r
-                                                               return filter_ctx->filter->query_formats;\r
-                                                       }\r
-                                               }();\r
-                                       }\r
-                                       \r
-                                       THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]"); \r
-                                       \r
-                                       BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)                                              \r
-                                               parallel_yadif_ctx_ = make_parallel_yadif(filter_ctx);                                          \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
-                                       return nullptr;\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
-                       return nullptr;\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
-\r
-filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new impl(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 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;
+}
+}}