]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
2.0. filter: Possible to run async with 1 frame extra latency.
[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 <tbb/task_group.h>\r
15 \r
16 #include <cstdio>\r
17 #include <sstream>\r
18 \r
19 extern "C" \r
20 {\r
21         #define __STDC_CONSTANT_MACROS\r
22         #define __STDC_LIMIT_MACROS\r
23         #include <libavutil/avutil.h>\r
24         #include <libavutil/imgutils.h>\r
25         #include <libavfilter/avfilter.h>\r
26         #include <libavfilter/avcodec.h>\r
27         #include <libavfilter/avfiltergraph.h>\r
28         #include <libavfilter/vsink_buffer.h>\r
29         #include <libavfilter/vsrc_buffer.h>\r
30 }\r
31 \r
32 namespace caspar {\r
33         \r
34 struct filter::implementation\r
35 {\r
36         std::string                                             filters_;\r
37         std::shared_ptr<AVFilterGraph>  graph_; \r
38         AVFilterContext*                                buffersink_ctx_;\r
39         AVFilterContext*                                buffersrc_ctx_;\r
40 \r
41         filter::flags                                   flags_;\r
42         std::vector<safe_ptr<AVFrame>>  frames_;\r
43         tbb::task_group                                 tasks_;\r
44         \r
45         implementation(const std::wstring& filters, filter::flags filter_flags) \r
46                 : filters_(filters.empty() ? "null" : narrow(filters))\r
47                 , flags_(filter_flags)\r
48         {\r
49                 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
50         }\r
51 \r
52         std::vector<safe_ptr<AVFrame>> execute(const std::shared_ptr<AVFrame>& frame)\r
53         {\r
54                 if((flags_ | filter::low_latency) != 0)\r
55                 {\r
56                         push(frame);\r
57                         return poll();\r
58                 }\r
59 \r
60                 tasks_.wait();\r
61                         \r
62                 push(frame);\r
63 \r
64                 auto frames = std::move(frames_);\r
65 \r
66                 tasks_.run([=]\r
67                 {\r
68                         frames_ = poll();\r
69                 });\r
70 \r
71                 return frames;\r
72         }\r
73 \r
74         void push(const std::shared_ptr<AVFrame>& frame)\r
75         {               \r
76                 if(!frame)\r
77                         return;\r
78 \r
79                 int errn = 0;   \r
80 \r
81                 if(!graph_)\r
82                 {\r
83                         graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
84                                                                 \r
85                         // Input\r
86                         std::stringstream args;\r
87                         args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
88                         errn = avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get());\r
89                         if(errn < 0)\r
90                         {\r
91                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
92                                         boost::errinfo_api_function("avfilter_graph_create_filter") <<  boost::errinfo_errno(AVUNERROR(errn)));\r
93                         }\r
94 \r
95                         PixelFormat pix_fmts[] = \r
96                         {\r
97                                 PIX_FMT_YUV420P,\r
98                                 PIX_FMT_YUVA420P,\r
99                                 PIX_FMT_YUV422P,\r
100                                 PIX_FMT_YUV444P,\r
101                                 PIX_FMT_YUV411P,\r
102                                 PIX_FMT_ARGB, \r
103                                 PIX_FMT_RGBA,\r
104                                 PIX_FMT_ABGR,\r
105                                 PIX_FMT_GRAY8,\r
106                                 PIX_FMT_NONE\r
107                         };      \r
108                         // OPIX_FMT_BGRAutput\r
109                         errn = avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts, graph_.get());\r
110                         if(errn < 0)\r
111                         {\r
112                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
113                                         boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
114                         }\r
115                         \r
116                         AVFilterInOut* outputs = avfilter_inout_alloc();\r
117                         AVFilterInOut* inputs  = avfilter_inout_alloc();\r
118 \r
119                         outputs->name                   = av_strdup("in");\r
120                         outputs->filter_ctx             = buffersrc_ctx_;\r
121                         outputs->pad_idx                = 0;\r
122                         outputs->next                   = NULL;\r
123 \r
124                         inputs->name                    = av_strdup("out");\r
125                         inputs->filter_ctx              = buffersink_ctx_;\r
126                         inputs->pad_idx                 = 0;\r
127                         inputs->next                    = NULL;\r
128                         \r
129                         errn = avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL);\r
130 \r
131                         avfilter_inout_free(&inputs);\r
132                         avfilter_inout_free(&outputs);\r
133 \r
134                         if(errn < 0)\r
135                         {\r
136                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
137                                         boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));\r
138                         }\r
139                         \r
140                         errn = avfilter_graph_config(graph_.get(), NULL);\r
141                         if(errn < 0)\r
142                         {\r
143                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) \r
144                                         <<      boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));\r
145                         }\r
146                 }\r
147         \r
148                 errn = av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0);\r
149                 if(errn < 0)\r
150                 {\r
151                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
152                                 boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
153                 }\r
154         }\r
155 \r
156         std::vector<safe_ptr<AVFrame>> poll()\r
157         {\r
158                 std::vector<safe_ptr<AVFrame>> result;\r
159 \r
160                 if(!graph_)\r
161                         return result;\r
162                 \r
163                 while (avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
164                 {\r
165                         AVFilterBufferRef *picref;\r
166             av_vsink_buffer_get_video_buffer_ref(buffersink_ctx_, &picref, 0);\r
167             if (picref) \r
168                         {               \r
169                                 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
170                                 {\r
171                                         av_free(p);\r
172                                         avfilter_unref_buffer(picref);\r
173                                 });\r
174 \r
175                                 avcodec_get_frame_defaults(frame.get());        \r
176 \r
177                                 memcpy(frame->data,     picref->data,     sizeof(frame->data));\r
178                                 memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));\r
179                                 frame->format                           = picref->format;\r
180                                 frame->width                            = picref->video->w;\r
181                                 frame->height                           = picref->video->h;\r
182                                 frame->pkt_pos                          = picref->pos;\r
183                                 frame->interlaced_frame         = picref->video->interlaced;\r
184                                 frame->top_field_first          = picref->video->top_field_first;\r
185                                 frame->key_frame                        = picref->video->key_frame;\r
186                                 frame->pict_type                        = picref->video->pict_type;\r
187                                 frame->sample_aspect_ratio      = picref->video->sample_aspect_ratio;\r
188 \r
189                                 result.push_back(frame);\r
190             }\r
191         }\r
192 \r
193                 return result;\r
194         }\r
195 };\r
196 \r
197 filter::filter(const std::wstring& filters, flags filter_flags) : impl_(new implementation(filters, filter_flags)){}\r
198 std::vector<safe_ptr<AVFrame>> filter::execute(const std::shared_ptr<AVFrame>& frame) {return impl_->execute(frame);}\r
199 }