]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/util/filter.cpp
2.0: ffmpeg_producer: Re-added and fixed filter capabilities.
[casparcg] / modules / ffmpeg / util / filter.cpp
1 #include "../stdafx.h"\r
2 \r
3 #include "filter.h"\r
4 \r
5 #include "../ffmpeg_error.h"\r
6 #include "util.h"\r
7 \r
8 #include <common/exception/exceptions.h>\r
9 #include <core/producer/frame/basic_frame.h>\r
10 #include <core/producer/frame/frame_factory.h>\r
11 #include <core/mixer/write_frame.h>\r
12 \r
13 #include <cstdio>\r
14 #include <sstream>\r
15 \r
16 extern "C" \r
17 {\r
18         #define __STDC_CONSTANT_MACROS\r
19         #define __STDC_LIMIT_MACROS\r
20         #include <libavutil/avutil.h>\r
21         #include <libavutil/imgutils.h>\r
22         #include <libavfilter/avfilter.h>\r
23         #include <libavfilter/avcodec.h>\r
24         #include <libavfilter/vsrc_buffer.h>\r
25         #include <libavfilter/avfiltergraph.h>\r
26 }\r
27 \r
28 namespace caspar {\r
29         \r
30 struct filter::implementation\r
31 {\r
32         std::string                                                             filters_;\r
33         std::shared_ptr<AVFilterGraph>                  graph_;\r
34         AVFilterContext*                                                video_in_filter_;\r
35         AVFilterContext*                                                video_out_filter_;\r
36                 \r
37         implementation(const std::string& filters) \r
38                 : filters_(filters)\r
39         {\r
40                 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
41         }\r
42 \r
43         std::vector<safe_ptr<AVFrame>> execute(const safe_ptr<AVFrame>& frame)\r
44         {               \r
45                 int errn = 0;   \r
46 \r
47                 if(!graph_)\r
48                 {\r
49                         graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
50                         \r
51                         // Input\r
52                         std::stringstream buffer_ss;\r
53                         buffer_ss << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
54                         errn = avfilter_graph_create_filter(&video_in_filter_, avfilter_get_by_name("buffer"), "src", buffer_ss.str().c_str(), NULL, graph_.get());\r
55                         if(errn < 0)\r
56                         {\r
57                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
58                                         boost::errinfo_api_function("avfilter_graph_create_filter") <<  boost::errinfo_errno(AVUNERROR(errn)));\r
59                         }\r
60 \r
61                         // Output\r
62                         errn = avfilter_graph_create_filter(&video_out_filter_, avfilter_get_by_name("nullsink"), "out", NULL, NULL, graph_.get());\r
63                         if(errn < 0)\r
64                         {\r
65                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
66                                         boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
67                         }\r
68                         \r
69                         AVFilterInOut* outputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
70                         AVFilterInOut* inputs  = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
71 \r
72                         outputs->name                   = av_strdup("in");\r
73                         outputs->filter_ctx             = video_in_filter_;\r
74                         outputs->pad_idx                = 0;\r
75                         outputs->next                   = NULL;\r
76 \r
77                         inputs->name                    = av_strdup("out");\r
78                         inputs->filter_ctx              = video_out_filter_;\r
79                         inputs->pad_idx                 = 0;\r
80                         inputs->next                    = NULL;\r
81                         \r
82                         errn = avfilter_graph_parse(graph_.get(), filters_.c_str(), inputs, outputs, NULL);\r
83                         if(errn < 0)\r
84                         {\r
85                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
86                                         boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));\r
87                         }\r
88 \r
89 //                      av_free(outputs);\r
90 //                      av_free(inputs);\r
91 \r
92                         errn = avfilter_graph_config(graph_.get(), NULL);\r
93                         if(errn < 0)\r
94                         {\r
95                                 BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) \r
96                                         <<      boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));\r
97                         }\r
98                 }\r
99         \r
100                 errn = av_vsrc_buffer_add_frame(video_in_filter_, frame.get(), 0);\r
101                 if(errn < 0)\r
102                 {\r
103                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
104                                 boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
105                 }\r
106 \r
107                 errn = avfilter_poll_frame(video_out_filter_->inputs[0]);\r
108                 if(errn < 0)\r
109                 {\r
110                         BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
111                                 boost::errinfo_api_function("avfilter_poll_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
112                 }\r
113 \r
114                 std::vector<safe_ptr<AVFrame>> result;\r
115 \r
116                 std::generate_n(std::back_inserter(result), errn, [&]{return get_frame();});\r
117 \r
118                 return result;\r
119         }\r
120                 \r
121         safe_ptr<AVFrame> get_frame()\r
122         {               \r
123                 auto link = video_out_filter_->inputs[0];\r
124 \r
125                 int errn = avfilter_request_frame(link);                        \r
126                 if(errn < 0)\r
127                 {\r
128                         BOOST_THROW_EXCEPTION(caspar_exception() <<     msg_info(av_error_str(errn)) <<\r
129                                 boost::errinfo_api_function("avfilter_request_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
130                 }\r
131                 \r
132                 auto pic = reinterpret_cast<AVPicture*>(link->cur_buf->buf);\r
133                 \r
134                 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), av_free);\r
135                 avcodec_get_frame_defaults(frame.get());        \r
136 \r
137                 for(size_t n = 0; n < 4; ++n)\r
138                 {\r
139                         frame->data[n]          = pic->data[n];\r
140                         frame->linesize[n]      = pic->linesize[n];\r
141                 }\r
142 \r
143                 // FIXME\r
144                 frame->width                    = link->cur_buf->video->w;\r
145                 frame->height                   = link->cur_buf->video->h;\r
146                 frame->format                   = link->cur_buf->format;\r
147                 frame->interlaced_frame = link->cur_buf->video->interlaced;\r
148                 frame->top_field_first  = link->cur_buf->video->top_field_first;\r
149                 frame->key_frame                = link->cur_buf->video->key_frame;\r
150 \r
151                 return frame;\r
152         }\r
153 };\r
154 \r
155 filter::filter(const std::string& filters) : impl_(new implementation(filters)){}\r
156 std::vector<safe_ptr<AVFrame>> filter::execute(const safe_ptr<AVFrame>& frame) {return impl_->execute(frame);}\r
157 \r
158 }