]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
Updated master to latest ffmpeg version.
[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 "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 #include <boost/thread.hpp>\r
39 #include <boost/format.hpp>\r
40 #include <boost/rational.hpp>\r
41 \r
42 #include <cstdio>\r
43 #include <sstream>\r
44 #include <string>\r
45 \r
46 #if defined(_MSC_VER)\r
47 #pragma warning (push)\r
48 #pragma warning (disable : 4244)\r
49 #endif\r
50 extern "C" \r
51 {\r
52         #include <libavutil/avutil.h>\r
53         #include <libavutil/imgutils.h>\r
54         #include <libavutil/opt.h>\r
55         #include <libavfilter/avfilter.h>\r
56         #include <libavfilter/avcodec.h>\r
57         #include <libavfilter/avfilter.h>\r
58         #include <libavfilter/buffersink.h>\r
59         #include <libavfilter/buffersrc.h>\r
60 }\r
61 #if defined(_MSC_VER)\r
62 #pragma warning (pop)\r
63 #endif\r
64 \r
65 namespace caspar { namespace ffmpeg {\r
66         \r
67 //static int query_formats_444(AVFilterContext *ctx)\r
68 //{\r
69 //    static const int pix_fmts[] = {PIX_FMT_YUV444P, PIX_FMT_NONE};\r
70 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
71 //    return 0;\r
72 //}\r
73 //\r
74 //static int query_formats_422(AVFilterContext *ctx)\r
75 //{\r
76 //    static const int pix_fmts[] = {PIX_FMT_YUV422P, PIX_FMT_NONE};\r
77 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
78 //    return 0;\r
79 //}\r
80 //\r
81 //static int query_formats_420(AVFilterContext *ctx)\r
82 //{\r
83 //    static const int pix_fmts[] = {PIX_FMT_YUV420P, PIX_FMT_NONE};\r
84 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
85 //    return 0;\r
86 //}\r
87 //\r
88 //static int query_formats_420a(AVFilterContext *ctx)\r
89 //{\r
90 //    static const int pix_fmts[] = {PIX_FMT_YUVA420P, PIX_FMT_NONE};\r
91 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
92 //    return 0;\r
93 //}\r
94 //\r
95 //static int query_formats_411(AVFilterContext *ctx)\r
96 //{\r
97 //    static const int pix_fmts[] = {PIX_FMT_YUV411P, PIX_FMT_NONE};\r
98 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
99 //    return 0;\r
100 //}\r
101 //\r
102 //static int query_formats_410(AVFilterContext *ctx)\r
103 //{\r
104 //    static const int pix_fmts[] = {PIX_FMT_YUV410P, PIX_FMT_NONE};\r
105 //    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
106 //    return 0;\r
107 //}\r
108 \r
109 struct filter::implementation\r
110 {\r
111         std::wstring                                    filters_;\r
112         //std::shared_ptr<AVFilterGraph>        graph_; \r
113         //AVFilterContext*                              buffersink_ctx_;\r
114         //AVFilterContext*                              buffersrc_ctx_;\r
115         //std::shared_ptr<void>                 parallel_yadif_ctx_;\r
116         std::vector<PixelFormat>                pix_fmts_;\r
117         //std::queue<safe_ptr<AVFrame>> bypass_;\r
118 \r
119         std::shared_ptr<AVFilterGraph>  video_graph_;   \r
120     AVFilterContext*                            video_graph_in_;  \r
121     AVFilterContext*                            video_graph_out_; \r
122                 \r
123         implementation(\r
124                 int in_width,\r
125                 int in_height,\r
126                 boost::rational<int> in_time_base,\r
127                 boost::rational<int> in_frame_rate,\r
128                 boost::rational<int> in_sample_aspect_ratio,\r
129                 AVPixelFormat in_pix_fmt,\r
130                 std::vector<AVPixelFormat> out_pix_fmts,\r
131                 const std::string& filtergraph) \r
132         {\r
133                 if(out_pix_fmts.empty())\r
134                 {\r
135                         pix_fmts_ = boost::assign::list_of\r
136                                 (AV_PIX_FMT_YUVA420P)\r
137                                 (AV_PIX_FMT_YUV444P)\r
138                                 (AV_PIX_FMT_YUV422P)\r
139                                 (AV_PIX_FMT_YUV420P)\r
140                                 (AV_PIX_FMT_YUV411P)\r
141                                 (AV_PIX_FMT_BGRA)\r
142                                 (AV_PIX_FMT_ARGB)\r
143                                 (AV_PIX_FMT_RGBA)\r
144                                 (AV_PIX_FMT_ABGR)\r
145                                 (AV_PIX_FMT_GRAY8);\r
146                 }\r
147 \r
148                 out_pix_fmts.push_back(AV_PIX_FMT_NONE);\r
149 \r
150                 video_graph_.reset(\r
151                         avfilter_graph_alloc(), \r
152                         [](AVFilterGraph* p)\r
153                         {\r
154                                 avfilter_graph_free(&p);\r
155                         });\r
156                 \r
157                 video_graph_->nb_threads  = boost::thread::hardware_concurrency();\r
158                 video_graph_->thread_type = AVFILTER_THREAD_SLICE;\r
159 \r
160                 //const auto sample_aspect_ratio = \r
161                 //      boost::rational<int>(\r
162                 //              in_video_format_.square_width, \r
163                 //              in_video_format_.square_height) /\r
164                 //      boost::rational<int>(\r
165                 //              in_video_format_.width, \r
166                 //              in_video_format_.height);\r
167                 \r
168                 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
169                         % in_width % in_height\r
170                         % in_pix_fmt\r
171                         % in_time_base.numerator() % in_time_base.denominator()\r
172                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()\r
173                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();\r
174                                         \r
175                 AVFilterContext* filt_vsrc = nullptr;                   \r
176                 FF(avfilter_graph_create_filter(\r
177                         &filt_vsrc,\r
178                         avfilter_get_by_name("buffer"), \r
179                         "ffmpeg_consumer_buffer",\r
180                         vsrc_options.c_str(), \r
181                         nullptr, \r
182                         video_graph_.get()));\r
183                                 \r
184                 AVFilterContext* filt_vsink = nullptr;\r
185                 FF(avfilter_graph_create_filter(\r
186                         &filt_vsink,\r
187                         avfilter_get_by_name("buffersink"), \r
188                         "ffmpeg_consumer_buffersink",\r
189                         nullptr, \r
190                         nullptr, \r
191                         video_graph_.get()));\r
192                 \r
193 #pragma warning (push)\r
194 #pragma warning (disable : 4245)\r
195 \r
196                 FF(av_opt_set_int_list(\r
197                         filt_vsink, \r
198                         "pix_fmts", \r
199                         out_pix_fmts.data(), \r
200                         -1,\r
201                         AV_OPT_SEARCH_CHILDREN));\r
202 \r
203 #pragma warning (pop)\r
204                         \r
205                 configure_filtergraph(\r
206                         *video_graph_, \r
207                         filtergraph,\r
208                         *filt_vsrc,\r
209                         *filt_vsink);\r
210 \r
211                 video_graph_in_  = filt_vsrc;\r
212                 video_graph_out_ = filt_vsink;\r
213                 \r
214                 CASPAR_LOG(info)\r
215                         <<      widen(std::string("\n") \r
216                                 + avfilter_graph_dump(\r
217                                                 video_graph_.get(), \r
218                                                 nullptr));\r
219         }\r
220         \r
221         void configure_filtergraph(\r
222                 AVFilterGraph& graph, \r
223                 const std::string& filtergraph, \r
224                 AVFilterContext& source_ctx, \r
225                 AVFilterContext& sink_ctx)\r
226         {\r
227                 AVFilterInOut* outputs = nullptr;\r
228                 AVFilterInOut* inputs = nullptr;\r
229 \r
230                 try\r
231                 {\r
232                         if(!filtergraph.empty()) \r
233                         {\r
234                                 outputs = avfilter_inout_alloc();\r
235                                 inputs  = avfilter_inout_alloc();\r
236 \r
237                                 CASPAR_VERIFY(outputs && inputs);\r
238 \r
239                                 outputs->name       = av_strdup("in");\r
240                                 outputs->filter_ctx = &source_ctx;\r
241                                 outputs->pad_idx    = 0;\r
242                                 outputs->next       = nullptr;\r
243 \r
244                                 inputs->name        = av_strdup("out");\r
245                                 inputs->filter_ctx  = &sink_ctx;\r
246                                 inputs->pad_idx     = 0;\r
247                                 inputs->next        = nullptr;\r
248 \r
249                                 FF(avfilter_graph_parse(\r
250                                         &graph, \r
251                                         filtergraph.c_str(), \r
252                                         &inputs, \r
253                                         &outputs, \r
254                                         nullptr));\r
255                         } \r
256                         else \r
257                         {\r
258                                 FF(avfilter_link(\r
259                                         &source_ctx, \r
260                                         0, \r
261                                         &sink_ctx, \r
262                                         0));\r
263                         }\r
264 \r
265                         FF(avfilter_graph_config(\r
266                                 &graph, \r
267                                 nullptr));\r
268                 }\r
269                 catch(...)\r
270                 {\r
271                         avfilter_inout_free(&outputs);\r
272                         avfilter_inout_free(&inputs);\r
273                         throw;\r
274                 }\r
275         }\r
276 \r
277         void push(const std::shared_ptr<AVFrame>& src_av_frame)\r
278         {               \r
279                 FF(av_buffersrc_add_frame(\r
280                         video_graph_in_, \r
281                         src_av_frame.get()));\r
282         }\r
283 \r
284         std::shared_ptr<AVFrame> poll()\r
285         {\r
286                 std::shared_ptr<AVFrame> filt_frame(\r
287                         av_frame_alloc(), \r
288                         [](AVFrame* p)\r
289                         {\r
290                                 av_frame_free(&p);\r
291                         });\r
292                 \r
293                 const auto ret = av_buffersink_get_frame(\r
294                         video_graph_out_, \r
295                         filt_frame.get());\r
296                                 \r
297                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))\r
298                         return nullptr;\r
299                                         \r
300                 FF_RET(ret, "poll");\r
301 \r
302                 return filt_frame;\r
303         }\r
304 \r
305 //      void push(const std::shared_ptr<AVFrame>& frame)\r
306 //      {               \r
307 //              if(!frame)\r
308 //                      return;\r
309 //\r
310 //              if(frame->data[0] == nullptr || frame->width < 1)\r
311 //                      BOOST_THROW_EXCEPTION(invalid_argument());\r
312 //\r
313 //              if(filters_.empty())\r
314 //              {\r
315 //                      bypass_.push(make_safe_ptr(frame));\r
316 //                      return;\r
317 //              }\r
318 //              \r
319 //              try\r
320 //              {\r
321 //                      if(!graph_)\r
322 //                      {\r
323 //                              try\r
324 //                              {\r
325 //                                      graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
326 //                                                              \r
327 //                                      // Input\r
328 //                                      std::stringstream args;\r
329 //                                      args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
330 //                                      THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");\r
331 //                                      \r
332 //#if FF_API_OLD_VSINK_API\r
333 //                                      THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");\r
334 //#else\r
335 //                                      safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);\r
336 //                                      buffersink_params->pixel_fmts = pix_fmts_.data();\r
337 //                                      THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");\r
338 //#endif\r
339 //                                      AVFilterInOut* inputs  = avfilter_inout_alloc();\r
340 //                                      AVFilterInOut* outputs = avfilter_inout_alloc();\r
341 //                                                              \r
342 //                                      outputs->name                   = av_strdup("in");\r
343 //                                      outputs->filter_ctx             = buffersrc_ctx_;\r
344 //                                      outputs->pad_idx                = 0;\r
345 //                                      outputs->next                   = nullptr;\r
346 //\r
347 //                                      inputs->name                    = av_strdup("out");\r
348 //                                      inputs->filter_ctx              = buffersink_ctx_;\r
349 //                                      inputs->pad_idx                 = 0;\r
350 //                                      inputs->next                    = nullptr;\r
351 //                      \r
352 //                                      std::string filters = boost::to_lower_copy(narrow(filters_));\r
353 //                                      THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters.c_str(), &inputs, &outputs, NULL), "[filter]");\r
354 //                      \r
355 //                                      auto yadif_filter = boost::adaptors::filtered([&](AVFilterContext* p){return strstr(p->name, "yadif") != 0;});\r
356 //\r
357 //                                      BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)\r
358 //                                      {\r
359 //                                              // Don't trust that libavfilter chooses optimal format.\r
360 //                                              filter_ctx->filter->query_formats = [&]() -> int (*)(AVFilterContext*)\r
361 //                                              {\r
362 //                                                      switch(frame->format)\r
363 //                                                      {\r
364 //                                                      case PIX_FMT_YUV444P16: \r
365 //                                                      case PIX_FMT_YUV444P10: \r
366 //                                                      case PIX_FMT_YUV444P9:          \r
367 //                                                      case PIX_FMT_YUV444P:   \r
368 //                                                      case PIX_FMT_BGR24:             \r
369 //                                                      case PIX_FMT_RGB24:     \r
370 //                                                              return query_formats_444;\r
371 //                                                      case PIX_FMT_YUV422P16: \r
372 //                                                      case PIX_FMT_YUV422P10: \r
373 //                                                      case PIX_FMT_YUV422P9:  \r
374 //                                                      case PIX_FMT_YUV422P:   \r
375 //                                                      case PIX_FMT_UYVY422:   \r
376 //                                                      case PIX_FMT_YUYV422:   \r
377 //                                                              return query_formats_422;\r
378 //                                                      case PIX_FMT_YUV420P16: \r
379 //                                                      case PIX_FMT_YUV420P10: \r
380 //                                                      case PIX_FMT_YUV420P9:  \r
381 //                                                      case PIX_FMT_YUV420P:   \r
382 //                                                              return query_formats_420;\r
383 //                                                      case PIX_FMT_YUVA420P:  \r
384 //                                                      case PIX_FMT_BGRA:              \r
385 //                                                      case PIX_FMT_RGBA:              \r
386 //                                                      case PIX_FMT_ABGR:              \r
387 //                                                      case PIX_FMT_ARGB:              \r
388 //                                                              return query_formats_420a;\r
389 //                                                      case PIX_FMT_UYYVYY411: \r
390 //                                                      case PIX_FMT_YUV411P:   \r
391 //                                                              return query_formats_411;\r
392 //                                                      case PIX_FMT_YUV410P:   \r
393 //                                                              return query_formats_410;\r
394 //                                                      default:                                \r
395 //                                                              return filter_ctx->filter->query_formats;\r
396 //                                                      }\r
397 //                                              }();\r
398 //                                      }\r
399 //                                      \r
400 //                                      THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]"); \r
401 //                                      \r
402 //                                      BOOST_FOREACH(auto filter_ctx, boost::make_iterator_range(graph_->filters, graph_->filters + graph_->filter_count) | yadif_filter)                                              \r
403 //                                              parallel_yadif_ctx_ = make_parallel_yadif(filter_ctx);                                          \r
404 //                              }\r
405 //                              catch(...)\r
406 //                              {\r
407 //                                      graph_ = nullptr;\r
408 //                                      throw;\r
409 //                              }\r
410 //                      }\r
411 //              \r
412 //                      THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");\r
413 //              }\r
414 //              catch(ffmpeg_error&)\r
415 //              {\r
416 //                      throw;\r
417 //              }\r
418 //              catch(...)\r
419 //              {\r
420 //                      BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
421 //              }\r
422 //      }\r
423 //\r
424 //      std::shared_ptr<AVFrame> poll()\r
425 //      {\r
426 //              if(filters_.empty())\r
427 //              {\r
428 //                      if(bypass_.empty())\r
429 //                              return nullptr;\r
430 //                      auto frame = bypass_.front();\r
431 //                      bypass_.pop();\r
432 //                      return frame;\r
433 //              }\r
434 //\r
435 //              if(!graph_)\r
436 //                      return nullptr;\r
437 //              \r
438 //              try\r
439 //              {\r
440 //                      if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
441 //                      {\r
442 //                              AVFilterBufferRef *picref;\r
443 //                              THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");\r
444 //\r
445 //                              if (!picref) \r
446 //                                      return nullptr;\r
447 //                              \r
448 //                              safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
449 //                              {\r
450 //                                      av_free(p);\r
451 //                                      avfilter_unref_buffer(picref);\r
452 //                              });\r
453 //\r
454 //                              avcodec_get_frame_defaults(frame.get());        \r
455 //\r
456 //                              memcpy(frame->data,     picref->data,     sizeof(frame->data));\r
457 //                              memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));\r
458 //                              frame->format                           = picref->format;\r
459 //                              frame->width                            = picref->video->w;\r
460 //                              frame->height                           = picref->video->h;\r
461 //                              frame->pkt_pos                          = picref->pos;\r
462 //                              frame->interlaced_frame         = picref->video->interlaced;\r
463 //                              frame->top_field_first          = picref->video->top_field_first;\r
464 //                              frame->key_frame                        = picref->video->key_frame;\r
465 //                              frame->pict_type                        = picref->video->pict_type;\r
466 //                              frame->sample_aspect_ratio      = picref->video->sample_aspect_ratio;\r
467 //                                      \r
468 //                              return frame;                           \r
469 //                      }\r
470 //\r
471 //                      return nullptr;\r
472 //              }\r
473 //              catch(ffmpeg_error&)\r
474 //              {\r
475 //                      throw;\r
476 //              }\r
477 //              catch(...)\r
478 //              {\r
479 //                      BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
480 //              }\r
481 //      }\r
482 };\r
483 \r
484 filter::filter(\r
485                 int in_width,\r
486                 int in_height,\r
487                 boost::rational<int> in_time_base,\r
488                 boost::rational<int> in_frame_rate,\r
489                 boost::rational<int> in_sample_aspect_ratio,\r
490                 AVPixelFormat in_pix_fmt,\r
491                 std::vector<AVPixelFormat> out_pix_fmts,\r
492                 const std::string& filtergraph) \r
493                 : impl_(new implementation(\r
494                         in_width,\r
495                         in_height,\r
496                         in_time_base,\r
497                         in_frame_rate,\r
498                         in_sample_aspect_ratio,\r
499                         in_pix_fmt,\r
500                         out_pix_fmts,\r
501                         filtergraph)){}\r
502 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
503 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
504 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
505 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
506 std::wstring filter::filter_str() const{return impl_->filters_;}\r
507 std::vector<safe_ptr<AVFrame>> filter::poll_all()\r
508 {       \r
509         std::vector<safe_ptr<AVFrame>> frames;\r
510         for(auto frame = poll(); frame; frame = poll())\r
511                 frames.push_back(make_safe_ptr(frame));\r
512         return frames;\r
513 }\r
514 \r
515 }}