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