]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
Instead of disabling ffmpeg logging just truncate log level to debug so trace and...
[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
41 #if defined(_MSC_VER)
42 #pragma warning (push)
43 #pragma warning (disable : 4244)
44 #endif
45 extern "C" 
46 {
47         #include <libavutil/avutil.h>
48         #include <libavutil/imgutils.h>
49         #include <libavutil/opt.h>
50         #include <libavfilter/avfilter.h>
51         #include <libavfilter/avcodec.h>
52         #include <libavfilter/buffersink.h>
53         #include <libavfilter/buffersrc.h>
54 }
55 #if defined(_MSC_VER)
56 #pragma warning (pop)
57 #endif
58
59 namespace caspar { namespace ffmpeg {
60         
61 struct filter::implementation
62 {
63         std::string                                             filtergraph_;
64
65         std::shared_ptr<AVFilterGraph>  video_graph_;   
66     AVFilterContext*                            video_graph_in_;  
67     AVFilterContext*                            video_graph_out_; 
68                 
69         implementation(
70                         int in_width,
71                         int in_height,
72                         boost::rational<int> in_time_base,
73                         boost::rational<int> in_frame_rate,
74                         boost::rational<int> in_sample_aspect_ratio,
75                         AVPixelFormat in_pix_fmt,
76                         std::vector<AVPixelFormat> out_pix_fmts,
77                         const std::string& filtergraph) 
78                 : filtergraph_(boost::to_lower_copy(filtergraph))
79         {
80                 if(out_pix_fmts.empty())
81                 {
82                         out_pix_fmts = {
83                                 AV_PIX_FMT_YUVA420P,
84                                 AV_PIX_FMT_YUV444P,
85                                 AV_PIX_FMT_YUV422P,
86                                 AV_PIX_FMT_YUV420P,
87                                 AV_PIX_FMT_YUV411P,
88                                 AV_PIX_FMT_BGRA,
89                                 AV_PIX_FMT_ARGB,
90                                 AV_PIX_FMT_RGBA,
91                                 AV_PIX_FMT_ABGR,
92                                 AV_PIX_FMT_GRAY8
93                         };
94                 }
95
96                 out_pix_fmts.push_back(AV_PIX_FMT_NONE);
97
98                 video_graph_.reset(
99                         avfilter_graph_alloc(), 
100                         [](AVFilterGraph* p)
101                         {
102                                 avfilter_graph_free(&p);
103                         });
104                 
105                 video_graph_->nb_threads  = 0;
106                 video_graph_->thread_type = AVFILTER_THREAD_SLICE;
107                                 
108                 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%")
109                         % in_width % in_height
110                         % in_pix_fmt
111                         % in_time_base.numerator() % in_time_base.denominator()
112                         % in_sample_aspect_ratio.numerator() % in_sample_aspect_ratio.denominator()
113                         % in_frame_rate.numerator() % in_frame_rate.denominator()).str();
114                                         
115                 AVFilterContext* filt_vsrc = nullptr;                   
116                 FF(avfilter_graph_create_filter(
117                         &filt_vsrc,
118                         avfilter_get_by_name("buffer"), 
119                         "filter_buffer",
120                         vsrc_options.c_str(), 
121                         nullptr, 
122                         video_graph_.get()));
123                                 
124                 AVFilterContext* filt_vsink = nullptr;
125                 FF(avfilter_graph_create_filter(
126                         &filt_vsink,
127                         avfilter_get_by_name("buffersink"), 
128                         "filter_buffersink",
129                         nullptr, 
130                         nullptr, 
131                         video_graph_.get()));
132                 
133 #pragma warning (push)
134 #pragma warning (disable : 4245)
135
136                 FF(av_opt_set_int_list(
137                         filt_vsink, 
138                         "pix_fmts", 
139                         out_pix_fmts.data(), 
140                         -1,
141                         AV_OPT_SEARCH_CHILDREN));
142
143 #pragma warning (pop)
144                         
145                 configure_filtergraph(
146                         *video_graph_, 
147                         filtergraph_,
148                         *filt_vsrc,
149                         *filt_vsink);
150
151                 video_graph_in_  = filt_vsrc;
152                 video_graph_out_ = filt_vsink;
153                 
154                 if (is_logging_quiet_for_thread())
155                         CASPAR_LOG(trace)
156                                 <<      u16(std::string("\n") 
157                                         + avfilter_graph_dump(
158                                                         video_graph_.get(), 
159                                                         nullptr));
160                 else
161                         CASPAR_LOG(debug)
162                                 << u16(std::string("\n")
163                                         + avfilter_graph_dump(
164                                                         video_graph_.get(),
165                                                         nullptr));
166         }
167         
168         void configure_filtergraph(
169                 AVFilterGraph& graph, 
170                 const std::string& filtergraph, 
171                 AVFilterContext& source_ctx, 
172                 AVFilterContext& sink_ctx)
173         {
174                 AVFilterInOut* outputs = nullptr;
175                 AVFilterInOut* inputs = nullptr;
176
177                 try
178                 {
179                         if(!filtergraph.empty()) 
180                         {
181                                 outputs = avfilter_inout_alloc();
182                                 inputs  = avfilter_inout_alloc();
183
184                                 CASPAR_VERIFY(outputs && inputs);
185
186                                 outputs->name       = av_strdup("in");
187                                 outputs->filter_ctx = &source_ctx;
188                                 outputs->pad_idx    = 0;
189                                 outputs->next       = nullptr;
190
191                                 inputs->name        = av_strdup("out");
192                                 inputs->filter_ctx  = &sink_ctx;
193                                 inputs->pad_idx     = 0;
194                                 inputs->next        = nullptr;
195
196                                 FF(avfilter_graph_parse(
197                                         &graph, 
198                                         filtergraph.c_str(), 
199                                         inputs,
200                                         outputs,
201                                         nullptr));
202                         } 
203                         else 
204                         {
205                                 FF(avfilter_link(
206                                         &source_ctx, 
207                                         0, 
208                                         &sink_ctx, 
209                                         0));
210                         }
211
212                         FF(avfilter_graph_config(
213                                 &graph, 
214                                 nullptr));
215                 }
216                 catch(...)
217                 {
218                         //avfilter_inout_free(&outputs);
219                         //avfilter_inout_free(&inputs);
220                         throw;
221                 }
222         }
223
224         void push(const std::shared_ptr<AVFrame>& src_av_frame)
225         {               
226                 FF(av_buffersrc_add_frame(
227                         video_graph_in_, 
228                         src_av_frame.get()));
229         }
230
231         std::shared_ptr<AVFrame> poll()
232         {
233                 std::shared_ptr<AVFrame> filt_frame(
234                         av_frame_alloc(), 
235                         [](AVFrame* p)
236                         {
237                                 av_frame_free(&p);
238                         });
239                 
240                 const auto ret = av_buffersink_get_frame(
241                         video_graph_out_, 
242                         filt_frame.get());
243                                 
244                 if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
245                         return nullptr;
246                                         
247                 FF_RET(ret, "poll");
248
249                 return filt_frame;
250         }
251 };
252
253 filter::filter(
254                 int in_width,
255                 int in_height,
256                 boost::rational<int> in_time_base,
257                 boost::rational<int> in_frame_rate,
258                 boost::rational<int> in_sample_aspect_ratio,
259                 AVPixelFormat in_pix_fmt,
260                 std::vector<AVPixelFormat> out_pix_fmts,
261                 const std::string& filtergraph) 
262                 : impl_(new implementation(
263                         in_width,
264                         in_height,
265                         in_time_base,
266                         in_frame_rate,
267                         in_sample_aspect_ratio,
268                         in_pix_fmt,
269                         out_pix_fmts,
270                         filtergraph)){}
271 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
272 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
273 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
274 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
275 std::wstring filter::filter_str() const{return u16(impl_->filtergraph_);}
276 std::vector<spl::shared_ptr<AVFrame>> filter::poll_all()
277 {       
278         std::vector<spl::shared_ptr<AVFrame>> frames;
279         for(auto frame = poll(); frame; frame = poll())
280                 frames.push_back(spl::make_shared_ptr(frame));
281         return frames;
282 }
283
284 }}