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