]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
2.0.0.2: Refactoring. Working on re-enabling filter functionality.
[casparcg] / modules / ffmpeg / producer / filter / filter.cpp
1 #include "../../stdafx.h"\r
2 \r
3 #include "filter.h"\r
4 \r
5 #include "../../ffmpeg_error.h"\r
6 \r
7 #include <common/exception/exceptions.h>\r
8 #include <core/producer/frame/basic_frame.h>\r
9 #include <core/producer/frame/frame_factory.h>\r
10 #include <core/mixer/write_frame.h>\r
11 \r
12 #include <boost/circular_buffer.hpp>\r
13 \r
14 #include <cstdio>\r
15 #include <sstream>\r
16 \r
17 extern "C" \r
18 {\r
19         #define __STDC_CONSTANT_MACROS\r
20         #define __STDC_LIMIT_MACROS\r
21         #include <libavutil/avutil.h>\r
22         #include <libavutil/imgutils.h>\r
23         #include <libavfilter/avfilter.h>\r
24         #include <libavfilter/avcodec.h>\r
25         #include <libavfilter/avfiltergraph.h>\r
26         #include <libavfilter/vsink_buffer.h>\r
27         #include <libavfilter/vsrc_buffer.h>\r
28 }\r
29 \r
30 namespace caspar {\r
31         \r
32 struct filter::implementation\r
33 {\r
34         std::string                                             filters_;\r
35         std::shared_ptr<AVFilterGraph>  graph_; \r
36         AVFilterContext*                                buffersink_ctx_;\r
37         AVFilterContext*                                buffersrc_ctx_;\r
38         \r
39         implementation(const std::wstring& filters) \r
40                 : filters_(narrow(filters))\r
41         {\r
42                 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
43         }\r
44 \r
45         void push(const safe_ptr<AVFrame>& frame)\r
46         {               \r
47                 int errn = 0;   \r
48 \r
49                 if(!graph_)\r
50                 {\r
51                         graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
52                                                                 \r
53                         // Input\r
54                         std::stringstream args;\r
55                         args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
56                         errn = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get());\r
57                         if(errn < 0)\r
58                         {\r
59                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
60                                         boost::errinfo_api_function("avfilter_graph_create_filter") <<  boost::errinfo_errno(AVUNERROR(errn)));\r
61                         }\r
62 \r
63                         PixelFormat pix_fmts[] = { PIX_FMT_BGRA, PIX_FMT_NONE };\r
64 \r
65                         // Output\r
66                         errn = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts, graph_.get());\r
67                         if(errn < 0)\r
68                         {\r
69                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
70                                         boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
71                         }\r
72                         \r
73                         AVFilterInOut* outputs = avfilter_inout_alloc();\r
74                         AVFilterInOut* inputs  = avfilter_inout_alloc();\r
75 \r
76                         outputs->name                   = av_strdup("in");\r
77                         outputs->filter_ctx             = buffersrc_ctx_;\r
78                         outputs->pad_idx                = 0;\r
79                         outputs->next                   = NULL;\r
80 \r
81                         inputs->name                    = av_strdup("out");\r
82                         inputs->filter_ctx              = buffersink_ctx_;\r
83                         inputs->pad_idx                 = 0;\r
84                         inputs->next                    = NULL;\r
85                         \r
86                         errn = avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL);\r
87                         if(errn < 0)\r
88                         {\r
89                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
90                                         boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));\r
91                         }\r
92                         \r
93                         errn = avfilter_graph_config(graph_.get(), NULL);\r
94                         if(errn < 0)\r
95                         {\r
96                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) \r
97                                         <<      boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));\r
98                         }\r
99                 }\r
100         \r
101                 errn = av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0);\r
102                 if(errn < 0)\r
103                 {\r
104                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
105                                 boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
106                 }\r
107         }\r
108 \r
109         std::vector<safe_ptr<AVFrame>> poll()\r
110         {\r
111                 std::vector<safe_ptr<AVFrame>> result;\r
112 \r
113                 if(!graph_)\r
114                         return result;\r
115                 \r
116                 while (avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
117                 {\r
118                         AVFilterBufferRef *picref;\r
119             av_vsink_buffer_get_video_buffer_ref(buffersink_ctx_, &picref, 0);\r
120             if (picref) \r
121                         {               \r
122                                 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
123                                 {\r
124                                         av_free(p);\r
125                                    avfilter_unref_buffer(picref);\r
126                                 });\r
127 \r
128                                 avcodec_get_frame_defaults(frame.get());        \r
129 \r
130                                 for(size_t n = 0; n < 4; ++n)\r
131                                 {\r
132                                         frame->data[n]          = picref->data[n];\r
133                                         frame->linesize[n]      = picref->linesize[n];\r
134                                 }\r
135                                 \r
136                                 frame->format                   = picref->format;\r
137                                 frame->width                    = picref->video->w;\r
138                                 frame->height                   = picref->video->h;\r
139                                 frame->interlaced_frame = picref->video->interlaced;\r
140                                 frame->top_field_first  = picref->video->top_field_first;\r
141                                 frame->key_frame                = picref->video->key_frame;\r
142 \r
143                                 result.push_back(frame);\r
144             }\r
145         }\r
146 \r
147                 return result;\r
148         }\r
149 };\r
150 \r
151 filter::filter(const std::wstring& filters) : impl_(new implementation(filters)){}\r
152 void filter::push(const safe_ptr<AVFrame>& frame) {impl_->push(frame);}\r
153 std::vector<safe_ptr<AVFrame>> filter::poll() {return impl_->poll();}\r
154 }