]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
ffmpeg: Cleanup and bug fixes.
[casparcg] / modules / ffmpeg / producer / filter / filter.cpp
1 /*\r
2 * Copyright 2013 Sveriges Television AB http://casparcg.com/\r
3 *\r
4 * This file is part of CasparCG (www.casparcg.com).\r
5 *\r
6 * CasparCG is free software: you can redistribute it and/or modify\r
7 * it under the terms of the GNU General Public License as published by\r
8 * the Free Software Foundation, either version 3 of the License, or\r
9 * (at your option) any later version.\r
10 *\r
11 * CasparCG is distributed in the hope that it will be useful,\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 * GNU General Public License for more details.\r
15 *\r
16 * You should have received a copy of the GNU General Public License\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 * Author: Robert Nagy, ronag89@gmail.com\r
20 */\r
21 \r
22 #include "../../stdafx.h"\r
23 \r
24 #include "filter.h"\r
25 \r
26 #include "../../ffmpeg_error.h"\r
27 \r
28 #include <common/exception/exceptions.h>\r
29 \r
30 #include <boost/assign.hpp>\r
31 #include <boost/range/iterator_range.hpp>\r
32 #include <boost/range/adaptors.hpp>\r
33 #include <boost/assign.hpp>\r
34 #include <boost/algorithm/string.hpp>\r
35 #include <boost/foreach.hpp>\r
36 #include <boost/thread.hpp>\r
37 #include <boost/format.hpp>\r
38 #include <boost/rational.hpp>\r
39 \r
40 #include <cstdio>\r
41 #include <sstream>\r
42 #include <string>\r
43 \r
44 #if defined(_MSC_VER)\r
45 #pragma warning (push)\r
46 #pragma warning (disable : 4244)\r
47 #endif\r
48 extern "C" \r
49 {\r
50         #include <libavutil/avutil.h>\r
51         #include <libavutil/imgutils.h>\r
52         #include <libavutil/opt.h>\r
53         #include <libavfilter/avfilter.h>\r
54         #include <libavfilter/avcodec.h>\r
55         #include <libavfilter/avfilter.h>\r
56         #include <libavfilter/buffersink.h>\r
57         #include <libavfilter/buffersrc.h>\r
58 }\r
59 #if defined(_MSC_VER)\r
60 #pragma warning (pop)\r
61 #endif\r
62 \r
63 namespace caspar { namespace ffmpeg {\r
64         \r
65 struct filter::implementation\r
66 {\r
67         std::string                                         filtergraph_;\r
68         std::shared_ptr<AVFilterGraph>  video_graph_;   \r
69     AVFilterContext*                            video_graph_in_;  \r
70     AVFilterContext*                            video_graph_out_; \r
71                 \r
72         implementation(\r
73                 int in_width,\r
74                 int in_height,\r
75                 boost::rational<int> in_time_base,\r
76                 boost::rational<int> in_frame_rate,\r
77                 boost::rational<int> in_sample_aspect_ratio,\r
78                 AVPixelFormat in_pix_fmt,\r
79                 std::vector<AVPixelFormat> out_pix_fmts,\r
80                 const std::string& filtergraph) \r
81                 : filtergraph_(boost::to_lower_copy(filtergraph))\r
82         {\r
83                 if(out_pix_fmts.empty())\r
84                 {\r
85                         out_pix_fmts = boost::assign::list_of\r
86                                 (AV_PIX_FMT_YUVA420P)\r
87                                 (AV_PIX_FMT_YUV444P)\r
88                                 (AV_PIX_FMT_YUV422P)\r
89                                 (AV_PIX_FMT_YUV420P)\r
90                                 (AV_PIX_FMT_YUV411P)\r
91                                 (AV_PIX_FMT_BGRA)\r
92                                 (AV_PIX_FMT_ARGB)\r
93                                 (AV_PIX_FMT_RGBA)\r
94                                 (AV_PIX_FMT_ABGR)\r
95                                 (AV_PIX_FMT_GRAY8);\r
96                 }\r
97 \r
98                 out_pix_fmts.push_back(AV_PIX_FMT_NONE);\r
99 \r
100                 video_graph_.reset(\r
101                         avfilter_graph_alloc(), \r
102                         [](AVFilterGraph* p)\r
103                         {\r
104                                 avfilter_graph_free(&p);\r
105                         });\r
106                 \r
107                 video_graph_->nb_threads  = boost::thread::hardware_concurrency();\r
108                 video_graph_->thread_type = AVFILTER_THREAD_SLICE;\r
109 \r
110                 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%")\r
111                         % in_width % in_height\r
112                         % in_pix_fmt\r
113                         % in_time_base.numerator() % in_time_base.denominator()\r
114                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()\r
115                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();\r
116                                         \r
117                 AVFilterContext* filt_vsrc = nullptr;                   \r
118                 FF(avfilter_graph_create_filter(\r
119                         &filt_vsrc,\r
120                         avfilter_get_by_name("buffer"), \r
121                         "filter_buffer",\r
122                         vsrc_options.c_str(), \r
123                         nullptr, \r
124                         video_graph_.get()));\r
125                                 \r
126                 AVFilterContext* filt_vsink = nullptr;\r
127                 FF(avfilter_graph_create_filter(\r
128                         &filt_vsink,\r
129                         avfilter_get_by_name("buffersink"), \r
130                         "filter_buffersink",\r
131                         nullptr, \r
132                         nullptr, \r
133                         video_graph_.get()));\r
134                 \r
135 #pragma warning (push)\r
136 #pragma warning (disable : 4245)\r
137 \r
138                 FF(av_opt_set_int_list(\r
139                         filt_vsink, \r
140                         "pix_fmts", \r
141                         out_pix_fmts.data(), \r
142                         -1,\r
143                         AV_OPT_SEARCH_CHILDREN));\r
144 \r
145 #pragma warning (pop)\r
146                         \r
147                 configure_filtergraph(\r
148                         *video_graph_, \r
149                         filtergraph_,\r
150                         *filt_vsrc,\r
151                         *filt_vsink);\r
152 \r
153                 video_graph_in_  = filt_vsrc;\r
154                 video_graph_out_ = filt_vsink;\r
155                 \r
156                 CASPAR_LOG(info)\r
157                         <<      widen(std::string("\n") \r
158                                 + avfilter_graph_dump(\r
159                                                 video_graph_.get(), \r
160                                                 nullptr));\r
161         }\r
162         \r
163         void configure_filtergraph(\r
164                 AVFilterGraph& graph, \r
165                 const std::string& filtergraph, \r
166                 AVFilterContext& source_ctx, \r
167                 AVFilterContext& sink_ctx)\r
168         {\r
169                 AVFilterInOut* outputs = nullptr;\r
170                 AVFilterInOut* inputs = nullptr;\r
171 \r
172                 try\r
173                 {\r
174                         if(!filtergraph.empty()) \r
175                         {\r
176                                 outputs = avfilter_inout_alloc();\r
177                                 inputs  = avfilter_inout_alloc();\r
178 \r
179                                 CASPAR_VERIFY(outputs && inputs);\r
180 \r
181                                 outputs->name       = av_strdup("in");\r
182                                 outputs->filter_ctx = &source_ctx;\r
183                                 outputs->pad_idx    = 0;\r
184                                 outputs->next       = nullptr;\r
185 \r
186                                 inputs->name        = av_strdup("out");\r
187                                 inputs->filter_ctx  = &sink_ctx;\r
188                                 inputs->pad_idx     = 0;\r
189                                 inputs->next        = nullptr;\r
190 \r
191                                 FF(avfilter_graph_parse(\r
192                                         &graph, \r
193                                         filtergraph.c_str(), \r
194                                         &inputs, \r
195                                         &outputs, \r
196                                         nullptr));\r
197                         } \r
198                         else \r
199                         {\r
200                                 FF(avfilter_link(\r
201                                         &source_ctx, \r
202                                         0, \r
203                                         &sink_ctx, \r
204                                         0));\r
205                         }\r
206 \r
207                         FF(avfilter_graph_config(\r
208                                 &graph, \r
209                                 nullptr));\r
210                 }\r
211                 catch(...)\r
212                 {\r
213                         avfilter_inout_free(&outputs);\r
214                         avfilter_inout_free(&inputs);\r
215                         throw;\r
216                 }\r
217         }\r
218 \r
219         void push(const std::shared_ptr<AVFrame>& src_av_frame)\r
220         {               \r
221                 FF(av_buffersrc_add_frame(\r
222                         video_graph_in_, \r
223                         src_av_frame.get()));\r
224         }\r
225 \r
226         std::shared_ptr<AVFrame> poll()\r
227         {\r
228                 std::shared_ptr<AVFrame> filt_frame(\r
229                         av_frame_alloc(), \r
230                         [](AVFrame* p)\r
231                         {\r
232                                 av_frame_free(&p);\r
233                         });\r
234                 \r
235                 const auto ret = av_buffersink_get_frame(\r
236                         video_graph_out_, \r
237                         filt_frame.get());\r
238                                 \r
239                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))\r
240                         return nullptr;\r
241                                         \r
242                 FF_RET(ret, "poll");\r
243 \r
244                 return filt_frame;\r
245         }\r
246 };\r
247 \r
248 filter::filter(\r
249                 int in_width,\r
250                 int in_height,\r
251                 boost::rational<int> in_time_base,\r
252                 boost::rational<int> in_frame_rate,\r
253                 boost::rational<int> in_sample_aspect_ratio,\r
254                 AVPixelFormat in_pix_fmt,\r
255                 std::vector<AVPixelFormat> out_pix_fmts,\r
256                 const std::string& filtergraph) \r
257                 : impl_(new implementation(\r
258                         in_width,\r
259                         in_height,\r
260                         in_time_base,\r
261                         in_frame_rate,\r
262                         in_sample_aspect_ratio,\r
263                         in_pix_fmt,\r
264                         out_pix_fmts,\r
265                         filtergraph)){}\r
266 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
267 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
268 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
269 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
270 std::wstring filter::filter_str() const{return widen(impl_->filtergraph_);}\r
271 std::vector<safe_ptr<AVFrame>> filter::poll_all()\r
272 {       \r
273         std::vector<safe_ptr<AVFrame>> frames;\r
274         for(auto frame = poll(); frame; frame = poll())\r
275                 frames.push_back(make_safe_ptr(frame));\r
276         return frames;\r
277 }\r
278 \r
279 }}