]> git.sesse.net Git - casparcg/blob - modules/ffmpeg/producer/filter/filter.cpp
2.0.2: ffmpeg/filter: Use safe_ptr for bypass container.
[casparcg] / modules / ffmpeg / producer / filter / filter.cpp
1 /*\r
2 * Copyright (c) 2011 Sveriges Television AB <info@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 \r
34 #include <cstdio>\r
35 #include <sstream>\r
36 \r
37 #if defined(_MSC_VER)\r
38 #pragma warning (push)\r
39 #pragma warning (disable : 4244)\r
40 #endif\r
41 extern "C" \r
42 {\r
43         #include <libavutil/avutil.h>\r
44         #include <libavutil/imgutils.h>\r
45         #include <libavfilter/avfilter.h>\r
46         #include <libavfilter/avcodec.h>\r
47         #include <libavfilter/avfiltergraph.h>\r
48         #include <libavfilter/buffersink.h>\r
49         #include <libavfilter/vsrc_buffer.h>\r
50 }\r
51 #if defined(_MSC_VER)\r
52 #pragma warning (pop)\r
53 #endif\r
54 \r
55 namespace caspar { namespace ffmpeg {\r
56 \r
57 static int query_formats_444(AVFilterContext *ctx)\r
58 {\r
59     static const int pix_fmts[] = {PIX_FMT_YUV444P, PIX_FMT_NONE};\r
60     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
61     return 0;\r
62 }\r
63 \r
64 static int query_formats_422(AVFilterContext *ctx)\r
65 {\r
66     static const int pix_fmts[] = {PIX_FMT_YUV422P, PIX_FMT_NONE};\r
67     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
68     return 0;\r
69 }\r
70 \r
71 static int query_formats_420(AVFilterContext *ctx)\r
72 {\r
73     static const int pix_fmts[] = {PIX_FMT_YUV420P, PIX_FMT_NONE};\r
74     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
75     return 0;\r
76 }\r
77 \r
78 static int query_formats_411(AVFilterContext *ctx)\r
79 {\r
80     static const int pix_fmts[] = {PIX_FMT_YUV411P, PIX_FMT_NONE};\r
81     avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));\r
82     return 0;\r
83 }\r
84 \r
85 struct filter::implementation\r
86 {\r
87         std::string                                             filters_;\r
88         std::shared_ptr<AVFilterGraph>  graph_; \r
89         AVFilterContext*                                buffersink_ctx_;\r
90         AVFilterContext*                                buffersrc_ctx_;\r
91         std::shared_ptr<void>                   parallel_yadif_ctx_;\r
92         std::vector<PixelFormat>                pix_fmts_;\r
93         std::queue<safe_ptr<AVFrame>>   bypass_;\r
94                 \r
95         implementation(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) \r
96                 : filters_(narrow(filters))\r
97                 , parallel_yadif_ctx_(nullptr)\r
98                 , pix_fmts_(pix_fmts)\r
99         {\r
100                 if(pix_fmts_.empty())\r
101                 {\r
102                         pix_fmts_.push_back(PIX_FMT_YUVA420P);\r
103                         pix_fmts_.push_back(PIX_FMT_YUV444P);\r
104                         pix_fmts_.push_back(PIX_FMT_YUV422P);\r
105                         pix_fmts_.push_back(PIX_FMT_YUV420P);\r
106                         pix_fmts_.push_back(PIX_FMT_YUV411P);\r
107                         pix_fmts_.push_back(PIX_FMT_BGRA);\r
108                         pix_fmts_.push_back(PIX_FMT_ARGB);\r
109                         pix_fmts_.push_back(PIX_FMT_RGBA);\r
110                         pix_fmts_.push_back(PIX_FMT_ABGR);\r
111                         pix_fmts_.push_back(PIX_FMT_GRAY8);\r
112                         pix_fmts_.push_back(PIX_FMT_NONE);\r
113                 }\r
114                 else\r
115                         pix_fmts_.push_back(PIX_FMT_NONE);\r
116 \r
117                 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
118         }\r
119         \r
120         void push(const std::shared_ptr<AVFrame>& frame)\r
121         {               \r
122                 if(!frame)\r
123                         return;\r
124 \r
125                 if(frame->data[0] == nullptr || frame->width < 1)\r
126                         BOOST_THROW_EXCEPTION(invalid_argument());\r
127 \r
128                 if(filters_.empty())\r
129                 {\r
130                         bypass_.push(make_safe_ptr(frame));\r
131                         return;\r
132                 }\r
133                 \r
134                 try\r
135                 {\r
136                         if(!graph_)\r
137                         {\r
138                                 try\r
139                                 {\r
140                                         graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
141                                                                 \r
142                                         // Input\r
143                                         std::stringstream args;\r
144                                         args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
145                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");\r
146                                         \r
147 #if FF_API_OLD_VSINK_API\r
148                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");\r
149 #else\r
150                                         safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);\r
151                                         buffersink_params->pixel_fmts = pix_fmts_.data();\r
152                                         THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");\r
153 #endif\r
154                                         AVFilterInOut* outputs = avfilter_inout_alloc();\r
155                                         AVFilterInOut* inputs  = avfilter_inout_alloc();\r
156                         \r
157                                         outputs->name                   = av_strdup("in");\r
158                                         outputs->filter_ctx             = buffersrc_ctx_;\r
159                                         outputs->pad_idx                = 0;\r
160                                         outputs->next                   = NULL;\r
161 \r
162                                         inputs->name                    = av_strdup("out");\r
163                                         inputs->filter_ctx              = buffersink_ctx_;\r
164                                         inputs->pad_idx                 = 0;\r
165                                         inputs->next                    = NULL;\r
166                         \r
167                                         THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL), "[filter]");\r
168                         \r
169                                         for(size_t n = 0; n < graph_->filter_count; ++n)\r
170                                         {\r
171                                                 auto filter_name = graph_->filters[n]->name;\r
172                                                 if(strstr(filter_name, "yadif") != 0)\r
173                                                 {\r
174                                                         if(frame->format == PIX_FMT_UYVY422)\r
175                                                                 graph_->filters[n]->filter->query_formats = query_formats_422;\r
176                                                         if(frame->format == PIX_FMT_YUYV422)\r
177                                                                 graph_->filters[n]->filter->query_formats = query_formats_422;\r
178                                                         if(frame->format == PIX_FMT_UYYVYY411)\r
179                                                                 graph_->filters[n]->filter->query_formats = query_formats_411;\r
180                                                         else if(frame->format == PIX_FMT_YUV420P10)\r
181                                                                 graph_->filters[n]->filter->query_formats = query_formats_420;\r
182                                                         else if(frame->format == PIX_FMT_YUV422P10)\r
183                                                                 graph_->filters[n]->filter->query_formats = query_formats_422;\r
184                                                         else if(frame->format == PIX_FMT_YUV444P10)\r
185                                                                 graph_->filters[n]->filter->query_formats = query_formats_444;\r
186                                                 }\r
187                                         }\r
188 \r
189                                         avfilter_inout_free(&inputs);\r
190                                         avfilter_inout_free(&outputs);\r
191 \r
192                                         THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]");                 \r
193 \r
194                                         for(size_t n = 0; n < graph_->filter_count; ++n)\r
195                                         {\r
196                                                 auto filter_name = graph_->filters[n]->name;\r
197                                                 if(strstr(filter_name, "yadif") != 0)                                           \r
198                                                         parallel_yadif_ctx_ = make_parallel_yadif(graph_->filters[n]);                                          \r
199                                         }\r
200                                 }\r
201                                 catch(...)\r
202                                 {\r
203                                         graph_ = nullptr;\r
204                                         throw;\r
205                                 }\r
206                         }\r
207                 \r
208                         THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");\r
209                 }\r
210                 catch(ffmpeg_error&)\r
211                 {\r
212                         throw;\r
213                 }\r
214                 catch(...)\r
215                 {\r
216                         BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
217                 }\r
218         }\r
219 \r
220         std::shared_ptr<AVFrame> poll()\r
221         {\r
222                 if(filters_.empty())\r
223                 {\r
224                         if(bypass_.empty())\r
225                                 return nullptr;\r
226                         auto frame = bypass_.front();\r
227                         bypass_.pop();\r
228                         return frame;\r
229                 }\r
230 \r
231                 if(!graph_)\r
232                         return nullptr;\r
233                 \r
234                 try\r
235                 {\r
236                         if(avfilter_poll_frame(buffersink_ctx_->inputs[0])) \r
237                         {\r
238                                 AVFilterBufferRef *picref;\r
239                                 THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");\r
240 \r
241                                 if (picref) \r
242                                 {               \r
243                                         safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)\r
244                                         {\r
245                                                 av_free(p);\r
246                                                 avfilter_unref_buffer(picref);\r
247                                         });\r
248 \r
249                                         avcodec_get_frame_defaults(frame.get());        \r
250 \r
251                                         memcpy(frame->data,     picref->data,     sizeof(frame->data));\r
252                                         memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));\r
253                                         frame->format                           = picref->format;\r
254                                         frame->width                            = picref->video->w;\r
255                                         frame->height                           = picref->video->h;\r
256                                         frame->pkt_pos                          = picref->pos;\r
257                                         frame->interlaced_frame         = picref->video->interlaced;\r
258                                         frame->top_field_first          = picref->video->top_field_first;\r
259                                         frame->key_frame                        = picref->video->key_frame;\r
260                                         frame->pict_type                        = picref->video->pict_type;\r
261                                         frame->sample_aspect_ratio      = picref->video->sample_aspect_ratio;\r
262                                         \r
263                                         return frame;\r
264                                 }\r
265                         }\r
266                 }\r
267                 catch(ffmpeg_error&)\r
268                 {\r
269                         throw;\r
270                 }\r
271                 catch(...)\r
272                 {\r
273                         BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));\r
274                 }\r
275 \r
276                 return nullptr;\r
277         }\r
278 };\r
279 \r
280 filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new implementation(filters, pix_fmts)){}\r
281 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}\r
282 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}\r
283 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}\r
284 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}\r
285 std::wstring filter::filter_str() const{return widen(impl_->filters_);}\r
286 std::vector<safe_ptr<AVFrame>> filter::poll_all()\r
287 {       \r
288         std::vector<safe_ptr<AVFrame>> frames;\r
289         for(auto frame = poll(); frame; frame = poll())\r
290                 frames.push_back(make_safe_ptr(frame));\r
291         return frames;\r
292 }\r
293 \r
294 }}