2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG (www.casparcg.com).
\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
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
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
19 * Author: Robert Nagy, ronag89@gmail.com
\r
22 #include "../../stdafx.h"
\r
26 #include "parallel_yadif.h"
\r
28 #include "../../ffmpeg_error.h"
\r
30 #include <common/exception/exceptions.h>
\r
32 #include <boost/assign.hpp>
\r
37 #if defined(_MSC_VER)
\r
38 #pragma warning (push)
\r
39 #pragma warning (disable : 4244)
\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
51 #if defined(_MSC_VER)
\r
52 #pragma warning (pop)
\r
55 namespace caspar { namespace ffmpeg {
\r
57 static int query_formats_444(AVFilterContext *ctx)
\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
64 static int query_formats_422(AVFilterContext *ctx)
\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
71 static int query_formats_420(AVFilterContext *ctx)
\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
78 struct filter::implementation
\r
80 std::string filters_;
\r
81 std::shared_ptr<AVFilterGraph> graph_;
\r
82 AVFilterContext* buffersink_ctx_;
\r
83 AVFilterContext* buffersrc_ctx_;
\r
84 std::shared_ptr<void> parallel_yadif_ctx_;
\r
85 std::vector<PixelFormat> pix_fmts_;
\r
86 std::queue<std::shared_ptr<AVFrame>> bypass_;
\r
88 implementation(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts)
\r
89 : filters_(narrow(filters))
\r
90 , parallel_yadif_ctx_(nullptr)
\r
91 , pix_fmts_(pix_fmts)
\r
93 if(pix_fmts_.empty())
\r
95 pix_fmts_.push_back(PIX_FMT_YUVA420P);
\r
96 pix_fmts_.push_back(PIX_FMT_YUV444P);
\r
97 pix_fmts_.push_back(PIX_FMT_YUV422P);
\r
98 pix_fmts_.push_back(PIX_FMT_YUV420P);
\r
99 pix_fmts_.push_back(PIX_FMT_YUV411P);
\r
100 pix_fmts_.push_back(PIX_FMT_BGRA);
\r
101 pix_fmts_.push_back(PIX_FMT_ARGB);
\r
102 pix_fmts_.push_back(PIX_FMT_RGBA);
\r
103 pix_fmts_.push_back(PIX_FMT_ABGR);
\r
104 pix_fmts_.push_back(PIX_FMT_GRAY8);
\r
105 pix_fmts_.push_back(PIX_FMT_NONE);
\r
108 pix_fmts_.push_back(PIX_FMT_NONE);
\r
110 std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);
\r
113 void push(const std::shared_ptr<AVFrame>& frame)
\r
118 if(frame->data[0] == nullptr || frame->width < 1)
\r
119 BOOST_THROW_EXCEPTION(invalid_argument());
\r
121 if(filters_.empty())
\r
123 bypass_.push(frame);
\r
133 graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});
\r
136 std::stringstream args;
\r
137 args << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio
\r
138 THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersrc_ctx_, avfilter_get_by_name("buffer"), "src", args.str().c_str(), NULL, graph_.get()), "[filter]");
\r
140 #if FF_API_OLD_VSINK_API
\r
141 THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, pix_fmts_.data(), graph_.get()), "[filter]");
\r
143 safe_ptr<AVBufferSinkParams> buffersink_params(av_buffersink_params_alloc(), av_free);
\r
144 buffersink_params->pixel_fmts = pix_fmts_.data();
\r
145 THROW_ON_ERROR2(avfilter_graph_create_filter(&buffersink_ctx_, avfilter_get_by_name("buffersink"), "out", NULL, buffersink_params.get(), graph_.get()), "[filter]");
\r
147 AVFilterInOut* outputs = avfilter_inout_alloc();
\r
148 AVFilterInOut* inputs = avfilter_inout_alloc();
\r
150 outputs->name = av_strdup("in");
\r
151 outputs->filter_ctx = buffersrc_ctx_;
\r
152 outputs->pad_idx = 0;
\r
153 outputs->next = NULL;
\r
155 inputs->name = av_strdup("out");
\r
156 inputs->filter_ctx = buffersink_ctx_;
\r
157 inputs->pad_idx = 0;
\r
158 inputs->next = NULL;
\r
160 THROW_ON_ERROR2(avfilter_graph_parse(graph_.get(), filters_.c_str(), &inputs, &outputs, NULL), "[filter]");
\r
162 for(size_t n = 0; n < graph_->filter_count; ++n)
\r
164 auto filter_name = graph_->filters[n]->name;
\r
165 if(strstr(filter_name, "yadif") != 0)
\r
167 if(frame->format == PIX_FMT_UYVY422)
\r
168 graph_->filters[n]->filter->query_formats = query_formats_422;
\r
169 else if(frame->format == PIX_FMT_YUV420P10)
\r
170 graph_->filters[n]->filter->query_formats = query_formats_420;
\r
171 else if(frame->format == PIX_FMT_YUV422P10)
\r
172 graph_->filters[n]->filter->query_formats = query_formats_422;
\r
173 else if(frame->format == PIX_FMT_YUV444P10)
\r
174 graph_->filters[n]->filter->query_formats = query_formats_444;
\r
178 avfilter_inout_free(&inputs);
\r
179 avfilter_inout_free(&outputs);
\r
181 THROW_ON_ERROR2(avfilter_graph_config(graph_.get(), NULL), "[filter]");
\r
183 for(size_t n = 0; n < graph_->filter_count; ++n)
\r
185 auto filter_name = graph_->filters[n]->name;
\r
186 if(strstr(filter_name, "yadif") != 0)
\r
187 parallel_yadif_ctx_ = make_parallel_yadif(graph_->filters[n]);
\r
197 THROW_ON_ERROR2(av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0), "[filter]");
\r
199 catch(ffmpeg_error&)
\r
205 BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));
\r
209 std::shared_ptr<AVFrame> poll()
\r
211 if(filters_.empty())
\r
213 if(bypass_.empty())
\r
215 auto frame = bypass_.front();
\r
225 if(avfilter_poll_frame(buffersink_ctx_->inputs[0]))
\r
227 AVFilterBufferRef *picref;
\r
228 THROW_ON_ERROR2(av_buffersink_get_buffer_ref(buffersink_ctx_, &picref, 0), "[filter]");
\r
232 safe_ptr<AVFrame> frame(avcodec_alloc_frame(), [=](AVFrame* p)
\r
235 avfilter_unref_buffer(picref);
\r
238 avcodec_get_frame_defaults(frame.get());
\r
240 memcpy(frame->data, picref->data, sizeof(frame->data));
\r
241 memcpy(frame->linesize, picref->linesize, sizeof(frame->linesize));
\r
242 frame->format = picref->format;
\r
243 frame->width = picref->video->w;
\r
244 frame->height = picref->video->h;
\r
245 frame->pkt_pos = picref->pos;
\r
246 frame->interlaced_frame = picref->video->interlaced;
\r
247 frame->top_field_first = picref->video->top_field_first;
\r
248 frame->key_frame = picref->video->key_frame;
\r
249 frame->pict_type = picref->video->pict_type;
\r
250 frame->sample_aspect_ratio = picref->video->sample_aspect_ratio;
\r
256 catch(ffmpeg_error&)
\r
262 BOOST_THROW_EXCEPTION(ffmpeg_error() << boost::errinfo_nested_exception(boost::current_exception()));
\r
269 filter::filter(const std::wstring& filters, const std::vector<PixelFormat>& pix_fmts) : impl_(new implementation(filters, pix_fmts)){}
\r
270 filter::filter(filter&& other) : impl_(std::move(other.impl_)){}
\r
271 filter& filter::operator=(filter&& other){impl_ = std::move(other.impl_); return *this;}
\r
272 void filter::push(const std::shared_ptr<AVFrame>& frame){impl_->push(frame);}
\r
273 std::shared_ptr<AVFrame> filter::poll(){return impl_->poll();}
\r
274 std::wstring filter::filter_str() const{return widen(impl_->filters_);}
\r
275 std::vector<safe_ptr<AVFrame>> filter::poll_all()
\r
277 std::vector<safe_ptr<AVFrame>> frames;
\r
278 for(auto frame = poll(); frame; frame = poll())
\r
279 frames.push_back(make_safe_ptr(frame));
\r