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