]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
3e9c35171d13c5a4869495ca959b8d9d6b8d04ad
[casparcg] / modules / ffmpeg / producer / filter / filter.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@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 "parallel_yadif.h"\r
27 \r
28 #include "../../ffmpeg_error.h"\r
29 \r
30 #include <common/exception/exceptions.h>\r
31 \r
32 #include <boost/assign.hpp>\r
33 #include <boost/range/iterator_range.hpp>\r
34 #include <boost/range/adaptors.hpp>\r
35 #include <boost/assign.hpp>\r
36 #include <boost/algorithm/string.hpp>\r
37 #include <boost/foreach.hpp>\r
38 \r
39 #include <cstdio>\r
40 #include <sstream>\r
41 \r
42 #if defined(_MSC_VER)\r
43 #pragma warning (push)\r
44 #pragma warning (disable : 4244)\r
45 #endif\r
46 extern "C" \r
47 {\r
48         #include <libavutil/avutil.h>\r
49         #include <libavutil/imgutils.h>\r
50         #include <libavfilter/avfilter.h>\r
51         #include <libavfilter/avcodec.h>\r
52         #include <libavfilter/avfiltergraph.h>\r
53         #include <libavfilter/buffersink.h>\r
54         #include <libavfilter/vsrc_buffer.h>\r
55 }\r
56 #if defined(_MSC_VER)\r
57 #pragma warning (pop)\r
58 #endif\r
59 \r
60 \r
61 namespace caspar { namespace ffmpeg {\r
62 \r
63 static int query_formats_444(AVFilterContext *ctx)\r
64 {\r
65     static const int pix_fmts[] = {PIX_FMT_YUV444P, PIX_FMT_NONE};\r
66     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
67     return 0;\r
68 }\r
69 \r
70 static int query_formats_422(AVFilterContext *ctx)\r
71 {\r
72     static const int pix_fmts[] = {PIX_FMT_YUV422P, PIX_FMT_NONE};\r
73     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
74     return 0;\r
75 }\r
76 \r
77 static int query_formats_420(AVFilterContext *ctx)\r
78 {\r
79     static const int pix_fmts[] = {PIX_FMT_YUV420P, PIX_FMT_NONE};\r
80     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
81     return 0;\r
82 }\r
83 \r
84 static int query_formats_420a(AVFilterContext *ctx)\r
85 {\r
86     static const int pix_fmts[] = {PIX_FMT_YUVA420P, PIX_FMT_NONE};\r
87     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
88     return 0;\r
89 }\r
90 \r
91 static int query_formats_411(AVFilterContext *ctx)\r
92 {\r
93     static const int pix_fmts[] = {PIX_FMT_YUV411P, PIX_FMT_NONE};\r
94     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
95     return 0;\r
96 }\r
97 \r
98 static int query_formats_410(AVFilterContext *ctx)\r
99 {\r
100     static const int pix_fmts[] = {PIX_FMT_YUV410P, PIX_FMT_NONE};\r
101     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
102     return 0;\r
103 }\r
104 \r
105 struct filter::implementation\r
106 {\r
107         std::wstring                                    filters_;\r
108         std::shared_ptr<AVFilterGraph>  graph_; \r
109         AVFilterContext*                                buffersink_ctx_;\r
110         AVFilterContext*                                buffersrc_ctx_;\r
111         std::shared_ptr<void>                   parallel_yadif_ctx_;\r
112         std::vector<PixelFormat>                pix_fmts_;\r
113         std::queue<safe_ptr<AVFrame>>   bypass_;\r
114                 \r
115         implementation(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) \r
116                 : filters_(filters)\r
117                 , parallel_yadif_ctx_(nullptr)\r
118                 , pix_fmts_(pix_fmts)\r
119         {\r
120                 if(pix_fmts_.empty())\r
121                 {\r
122                         pix_fmts_ = boost::assign::list_of\r
123                                 (PIX_FMT_YUVA420P)\r
124                                 (PIX_FMT_YUV444P)\r
125                                 (PIX_FMT_YUV422P)\r
126                                 (PIX_FMT_YUV420P)\r
127                                 (PIX_FMT_YUV411P)\r
128                                 (PIX_FMT_BGRA)\r
129                                 (PIX_FMT_ARGB)\r
130                                 (PIX_FMT_RGBA)\r
131                                 (PIX_FMT_ABGR)\r
132                                 (PIX_FMT_GRAY8);\r
133                 }\r
134                 \r
135                 pix_fmts_.push_back(PIX_FMT_NONE);\r
136         }\r
137         \r
138         void push(const std::shared_ptr<AVFrame>& frame)\r
139         {               \r
140                 if(!frame)\r
141                         return;\r
142 \r
143                 if(frame->data[0] == nullptr || frame->width < 1)\r
144                         BOOST_THROW_EXCEPTION(invalid_argument());\r
145 \r
146                 if(filters_.empty())\r
147                 {\r
148                         bypass_.push(make_safe_ptr(frame));\r
149                         return;\r
150                 }\r
151                 \r
152                 try\r
153                 {\r
154                         if(!graph_)\r
155                         {\r
156                                 try\r
157                                 {\r
158                                         graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
159                                                                 \r
160                                         // Input\r
161                                         std::stringstream args;\r
162                                         args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
163                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");\r
164                                         \r
165 #if FF_API_OLD_VSINK_API\r
166                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");\r
167 #else\r
168                                         safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);\r
169                                         buffersink_params->pixel_fmts = pix_fmts_.data();\r
170                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");\r
171 #endif\r
172                                         AVFilterInOut* inputs  = avfilter_inout_alloc();\r
173                                         AVFilterInOut* outputs = avfilter_inout_alloc();\r
174                                                                 \r
175                                         outputs->name                   = av_strdup("in");\r
176                                         outputs->filter_ctx             = buffersrc_ctx_;\r
177                                         outputs->pad_idx                = 0;\r
178                                         outputs->next                   = nullptr;\r
179 \r
180                                         inputs->name                    = av_strdup("out");\r
181                                         inputs->filter_ctx              = buffersink_ctx_;\r
182                                         inputs->pad_idx                 = 0;\r
183                                         inputs->next                    = nullptr;\r
184                         \r
185                                         std::string filters = boost::to_lower_copy(narrow(filters_));\r
186                                         THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters.c_str(), &inputs, &outputs, NULL), "[filter]");\r
187                         \r
188                                         auto yadif_filter = boost::adaptors::filtered([&](AVFilterContext* p){return strstr(p->name, "yadif") != 0;});\r
189 \r
190                                         BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)\r
191                                         {\r
192                                                 // Don't trust libavfilter format choice.\r
193                                                 filter_ctx->filter->query_formats = [&]() -> int (*)(AVFilterContext*)\r
194                                                 {\r
195                                                         switch(frame->format)\r
196                                                         {\r
197                                                         case PIX_FMT_YUV410P:   return query_formats_410;\r
198                                                         case PIX_FMT_YUV411P:   return query_formats_411;\r
199                                                         case PIX_FMT_YUV420P:   return query_formats_420;\r
200                                                         case PIX_FMT_YUV422P:   return query_formats_422;\r
201                                                         case PIX_FMT_YUV444P:   return query_formats_444;\r
202                                                         case PIX_FMT_YUVA420P:  return query_formats_420a;\r
203                                                         case PIX_FMT_UYVY422:   return query_formats_422;\r
204                                                         case PIX_FMT_YUYV422:   return query_formats_422;\r
205                                                         case PIX_FMT_UYYVYY411: return query_formats_411;\r
206                                                         case PIX_FMT_YUV420P10: return query_formats_420;\r
207                                                         case PIX_FMT_YUV422P10: return query_formats_422;\r
208                                                         case PIX_FMT_YUV444P10: return query_formats_444;\r
209                                                         case PIX_FMT_YUV420P16: return query_formats_420;\r
210                                                         case PIX_FMT_YUV422P16: return query_formats_422;\r
211                                                         case PIX_FMT_YUV444P16: return query_formats_444;\r
212                                                         case PIX_FMT_YUV420P9:  return query_formats_420;\r
213                                                         case PIX_FMT_YUV422P9:  return query_formats_422;\r
214                                                         case PIX_FMT_YUV444P9:  return query_formats_444;\r
215                                                         case PIX_FMT_BGR24:             return query_formats_444;\r
216                                                         case PIX_FMT_RGB24:             return query_formats_444;\r
217                                                         case PIX_FMT_BGRA:              return query_formats_420a;\r
218                                                         case PIX_FMT_RGBA:              return query_formats_420a;\r
219                                                         case PIX_FMT_ABGR:              return query_formats_420a;\r
220                                                         case PIX_FMT_ARGB:              return query_formats_420a;\r
221                                                         default:                                return filter_ctx->filter->query_formats;\r
222                                                         }\r
223                                                 }();\r
224                                         }\r
225                                         \r
226                                         THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]"); \r
227                                         \r
228                                         BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)                                              \r
229                                                 parallel_yadif_ctx_ = make_parallel_yadif(filter_ctx);                                          \r
230                                 }\r
231                                 catch(...)\r
232                                 {\r
233                                         graph_ = nullptr;\r
234                                         throw;\r
235                                 }\r
236                         }\r
237                 \r
238                         THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");\r
239                 }\r
240                 catch(ffmpeg_error&)\r
241                 {\r
242                         throw;\r
243                 }\r
244                 catch(...)\r
245                 {\r
246                         BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
247                 }\r
248         }\r
249 \r
250         std::shared_ptr<AVFrame> poll()\r
251         {\r
252                 if(filters_.empty())\r
253                 {\r
254                         if(bypass_.empty())\r
255                                 return nullptr;\r
256                         auto frame = bypass_.front();\r
257                         bypass_.pop();\r
258                         return frame;\r
259                 }\r
260 \r
261                 if(!graph_)\r
262                         return nullptr;\r
263                 \r
264                 try\r
265                 {\r
266                         if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
267                         {\r
268                                 AVFilterBufferRef *picref;\r
269                                 THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");\r
270 \r
271                                 if (!picref) \r
272                                         return nullptr;\r
273                                 \r
274                                 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
275                                 {\r
276                                         av_free(p);\r
277                                         avfilter_unref_buffer(picref);\r
278                                 });\r
279 \r
280                                 avcodec_get_frame_defaults(frame.get());        \r
281 \r
282                                 memcpy(frame->data,     picref->data,     sizeof(frame->data));\r
283                                 memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));\r
284                                 frame->format                           = picref->format;\r
285                                 frame->width                            = picref->video->w;\r
286                                 frame->height                           = picref->video->h;\r
287                                 frame->pkt_pos                          = picref->pos;\r
288                                 frame->interlaced_frame         = picref->video->interlaced;\r
289                                 frame->top_field_first          = picref->video->top_field_first;\r
290                                 frame->key_frame                        = picref->video->key_frame;\r
291                                 frame->pict_type                        = picref->video->pict_type;\r
292                                 frame->sample_aspect_ratio      = picref->video->sample_aspect_ratio;\r
293                                         \r
294                                 return frame;                           \r
295                         }\r
296 \r
297                         return nullptr;\r
298                 }\r
299                 catch(ffmpeg_error&)\r
300                 {\r
301                         throw;\r
302                 }\r
303                 catch(...)\r
304                 {\r
305                         BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
306                 }\r
307         }\r
308 };\r
309 \r
310 filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new implementation(filters, pix_fmts)){}\r
311 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
312 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
313 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
314 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
315 std::wstring filter::filter_str() const{return impl_->filters_;}\r
316 std::vector<safe_ptr<AVFrame>> filter::poll_all()\r
317 {       \r
318         std::vector<safe_ptr<AVFrame>> frames;\r
319         for(auto frame = poll(); frame; frame = poll())\r
320                 frames.push_back(make_safe_ptr(frame));\r
321         return frames;\r
322 }\r
323 \r
324 }}