--- /dev/null
+#include "../stdafx.h"\r
+\r
+#include "deinterlacer.h"\r
+\r
+#include "../ffmpeg_error.h"\r
+#include "util.h"\r
+\r
+#include <common/exception/exceptions.h>\r
+#include <core/producer/frame/basic_frame.h>\r
+#include <core/producer/frame/frame_factory.h>\r
+#include <core/mixer/write_frame.h>\r
+\r
+#include <cstdio>\r
+#include <sstream>\r
+\r
+extern "C" \r
+{\r
+ #define __STDC_CONSTANT_MACROS\r
+ #define __STDC_LIMIT_MACROS\r
+ #include <libavutil/avutil.h>\r
+ #include <libavutil/imgutils.h>\r
+ #include <libavfilter/avfilter.h>\r
+ #include <libavfilter/avcodec.h>\r
+ #include <libavfilter/vsrc_buffer.h>\r
+ #include <libavfilter/avfiltergraph.h>\r
+}\r
+\r
+namespace caspar {\r
+ \r
+struct deinterlacer::implementation\r
+{\r
+ std::shared_ptr<AVFilterGraph> graph_;\r
+ safe_ptr<core::frame_factory> frame_factory_;\r
+ AVFilterContext* video_in_filter_;\r
+ AVFilterContext* video_out_filter_;\r
+ \r
+ implementation(const safe_ptr<core::frame_factory>& frame_factory)\r
+ : frame_factory_(frame_factory)\r
+ { \r
+ }\r
+\r
+ std::vector<safe_ptr<core::write_frame>> execute(const safe_ptr<AVFrame>& frame)\r
+ {\r
+ std::vector<safe_ptr<core::write_frame>> result;\r
+ \r
+ int errn = 0; \r
+\r
+ if(!graph_)\r
+ {\r
+ graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\r
+ \r
+\r
+ // Input\r
+ std::stringstream buffer_ss;\r
+ buffer_ss << frame->width << ":" << frame->height << ":" << frame->format << ":" << 0 << ":" << 0 << ":" << 0 << ":" << 0; // don't care about pts and aspect_ratio\r
+ errn = avfilter_graph_create_filter(&video_in_filter_, avfilter_get_by_name("buffer"), "src", buffer_ss.str().c_str(), NULL, graph_.get());\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+\r
+ // Output\r
+ errn = avfilter_graph_create_filter(&video_out_filter_, avfilter_get_by_name("nullsink"), "out", NULL, NULL, graph_.get());\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("avfilter_graph_create_filter") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+
+ AVFilterInOut* outputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));
+ AVFilterInOut* inputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));
+
+ outputs->name = av_strdup("in");
+ outputs->filter_ctx = video_in_filter_;
+ outputs->pad_idx = 0;
+ outputs->next = NULL;
+
+ inputs->name = av_strdup("out");
+ inputs->filter_ctx = video_out_filter_;
+ inputs->pad_idx = 0;
+ inputs->next = NULL;
+ \r
+ std::stringstream yadif_ss;\r
+ yadif_ss << "yadif=" << "1:" << frame->top_field_first;
+
+ errn = avfilter_graph_parse(graph_.get(), yadif_ss.str().c_str(), inputs, outputs, NULL);\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("avfilter_graph_parse") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+\r
+// av_free(outputs);\r
+// av_free(inputs);\r
+\r
+ errn = avfilter_graph_config(graph_.get(), NULL);\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) \r
+ << boost::errinfo_api_function("avfilter_graph_config") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+ }\r
+ \r
+ errn = av_vsrc_buffer_add_frame(video_in_filter_, frame.get(), 0);\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("av_vsrc_buffer_add_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+\r
+ errn = avfilter_poll_frame(video_out_filter_->inputs[0]);\r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("avfilter_poll_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+\r
+ std::generate_n(std::back_inserter(result), errn, [&]{return get_frame(video_out_filter_->inputs[0]);});\r
+\r
+ return result;\r
+ }\r
+ \r
+ safe_ptr<core::write_frame> get_frame(AVFilterLink* link)\r
+ { \r
+ int errn = avfilter_request_frame(link); \r
+ if(errn < 0)\r
+ {\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(av_error_str(errn)) <<\r
+ boost::errinfo_api_function("avfilter_request_frame") << boost::errinfo_errno(AVUNERROR(errn)));\r
+ }\r
+ \r
+ auto buf = link->cur_buf->buf;\r
+ auto pic = reinterpret_cast<AVPicture*>(link->cur_buf->buf);\r
+ auto desc = get_pixel_format_desc(static_cast<PixelFormat>(buf->format), link->w, link->h);\r
+ auto write = frame_factory_->create_frame(this, desc);\r
+\r
+ tbb::parallel_for(0, static_cast<int>(desc.planes.size()), 1, [&](int n)\r
+ {\r
+ auto plane = desc.planes[n];\r
+ auto result = write->image_data(n).begin();\r
+ auto decoded = pic->data[n];\r
+ auto decoded_linesize = pic->linesize[n];\r
+ \r
+ // Copy line by line since ffmpeg sometimes pads each line.\r
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, static_cast<int>(desc.planes[n].height)), [&](const tbb::blocked_range<size_t>& r)\r
+ {\r
+ for(size_t y = r.begin(); y != r.end(); ++y)\r
+ memcpy(result + y*plane.linesize, decoded + y*decoded_linesize, plane.linesize);\r
+ });\r
+\r
+ write->commit(n);\r
+ });\r
+ \r
+ return write;\r
+ }\r
+};\r
+\r
+deinterlacer::deinterlacer(const safe_ptr<core::frame_factory>& factory) : impl_(implementation(factory)) {}\r
+std::vector<safe_ptr<core::write_frame>> deinterlacer::execute(const safe_ptr<AVFrame>& frame) {return impl_->execute(frame);}\r
+\r
+}
\ No newline at end of file
--- /dev/null
+#pragma once\r
+\r
+#include <core/video_format.h>\r
+#include <core/producer/frame/pixel_format.h>\r
+\r
+namespace caspar {\r
+ \r
+static core::pixel_format::type get_pixel_format(PixelFormat pix_fmt)\r
+{\r
+ switch(pix_fmt)\r
+ {\r
+ case PIX_FMT_GRAY8: return core::pixel_format::gray;\r
+ case PIX_FMT_BGRA: return core::pixel_format::bgra;\r
+ case PIX_FMT_ARGB: return core::pixel_format::argb;\r
+ case PIX_FMT_RGBA: return core::pixel_format::rgba;\r
+ case PIX_FMT_ABGR: return core::pixel_format::abgr;\r
+ case PIX_FMT_YUV444P: return core::pixel_format::ycbcr;\r
+ case PIX_FMT_YUV422P: return core::pixel_format::ycbcr;\r
+ case PIX_FMT_YUV420P: return core::pixel_format::ycbcr;\r
+ case PIX_FMT_YUV411P: return core::pixel_format::ycbcr;\r
+ case PIX_FMT_YUV410P: return core::pixel_format::ycbcr;\r
+ case PIX_FMT_YUVA420P: return core::pixel_format::ycbcra;\r
+ default: return core::pixel_format::invalid;\r
+ }\r
+}\r
+\r
+static core::pixel_format_desc get_pixel_format_desc(PixelFormat pix_fmt, size_t width, size_t height)\r
+{\r
+ // Get linesizes\r
+ AVPicture dummy_pict; \r
+ avpicture_fill(&dummy_pict, nullptr, pix_fmt, width, height);\r
+\r
+ core::pixel_format_desc desc;\r
+ desc.pix_fmt = get_pixel_format(pix_fmt);\r
+ \r
+ switch(desc.pix_fmt)\r
+ {\r
+ case core::pixel_format::gray:\r
+ {\r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 1)); \r
+ return desc;\r
+ }\r
+ case core::pixel_format::bgra:\r
+ case core::pixel_format::argb:\r
+ case core::pixel_format::rgba:\r
+ case core::pixel_format::abgr:\r
+ {\r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0]/4, height, 4)); \r
+ return desc;\r
+ }\r
+ case core::pixel_format::ycbcr:\r
+ case core::pixel_format::ycbcra:\r
+ { \r
+ // Find chroma height\r
+ size_t size2 = dummy_pict.data[2] - dummy_pict.data[1];\r
+ size_t h2 = size2/dummy_pict.linesize[1]; \r
+\r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[0], height, 1));\r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[1], h2, 1));\r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[2], h2, 1));\r
+\r
+ if(desc.pix_fmt == core::pixel_format::ycbcra) \r
+ desc.planes.push_back(core::pixel_format_desc::plane(dummy_pict.linesize[3], height, 1)); \r
+ return desc;\r
+ } \r
+ default: \r
+ desc.pix_fmt = core::pixel_format::invalid;\r
+ return desc;\r
+ }\r
+}\r
+\r
+}
\ No newline at end of file