<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
</ClCompile>\r
+ <ClCompile Include="producer\filter\scalable_yadif.cpp">\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>\r
+ </ClCompile>\r
<ClCompile Include="producer\frame_muxer.cpp">\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
<ClInclude Include="producer\audio\audio_decoder.h" />\r
<ClInclude Include="producer\ffmpeg_producer.h" />\r
<ClInclude Include="producer\filter\filter.h" />\r
+ <ClInclude Include="producer\filter\scalable_yadif.h" />\r
<ClInclude Include="producer\frame_muxer.h" />\r
<ClInclude Include="producer\input.h" />\r
<ClInclude Include="producer\util.h" />\r
\r
#include "filter.h"\r
\r
+#include "scalable_yadif.h"\r
+\r
#include "../../ffmpeg_error.h"\r
\r
#include <common/exception/exceptions.h>\r
std::shared_ptr<AVFilterGraph> graph_; \r
AVFilterContext* buffersink_ctx_;\r
AVFilterContext* buffersrc_ctx_;\r
-\r
- filter::flags flags_;\r
- std::vector<safe_ptr<AVFrame>> frames_;\r
- tbb::task_group tasks_;\r
- \r
- implementation(const std::wstring& filters, filter::flags filter_flags) \r
+ int scalable_yadif_tag_;\r
+ \r
+ implementation(const std::wstring& filters) \r
: filters_(filters.empty() ? "null" : narrow(filters))\r
- , flags_(filter_flags)\r
+ , scalable_yadif_tag_(-1)\r
{\r
std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
}\r
\r
- std::vector<safe_ptr<AVFrame>> execute(const std::shared_ptr<AVFrame>& frame)\r
+ ~implementation()\r
{\r
- if((flags_ | filter::low_latency) != 0)\r
- {\r
- push(frame);\r
- return poll();\r
- }\r
+ release_scalable_yadif(scalable_yadif_tag_);\r
+ }\r
\r
- tasks_.wait();\r
- \r
+ std::vector<safe_ptr<AVFrame>> execute(const std::shared_ptr<AVFrame>& frame)\r
+ {\r
push(frame);\r
-\r
- auto frames = std::move(frames_);\r
-\r
- tasks_.run([=]\r
- {\r
- frames_ = poll();\r
- });\r
-\r
- return frames;\r
+ return poll();\r
}\r
\r
void push(const std::shared_ptr<AVFrame>& frame)\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
+ for(size_t n = 0; n < graph_->filter_count; ++n)\r
+ {\r
+ auto filter_name = graph_->filters[n]->name;\r
+ if(strstr(filter_name, "yadif") != 0)\r
+ scalable_yadif_tag_ = make_scalable_yadif(graph_->filters[n]);\r
+ }\r
}\r
\r
errn = av_vsrc_buffer_add_frame(buffersrc_ctx_, frame.get(), 0);\r
}\r
};\r
\r
-filter::filter(const std::wstring& filters, flags filter_flags) : impl_(new implementation(filters, filter_flags)){}\r
+filter::filter(const std::wstring& filters) : impl_(new implementation(filters)){}\r
std::vector<safe_ptr<AVFrame>> filter::execute(const std::shared_ptr<AVFrame>& frame) {return impl_->execute(frame);}\r
}
\ No newline at end of file
--- /dev/null
+#include "../../StdAfx.h"\r
+\r
+#include "scalable_yadif.h"\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/avfiltergraph.h>\r
+ #include <libavfilter/vsink_buffer.h>\r
+ #include <libavfilter/vsrc_buffer.h>\r
+}\r
+\r
+#include <boost/thread/once.hpp>\r
+
+typedef struct {
+ int mode;
+ int parity;
+ int frame_pending;
+ int auto_enable;
+ AVFilterBufferRef *cur;
+ AVFilterBufferRef *next;
+ AVFilterBufferRef *prev;
+ AVFilterBufferRef *out;
+ void (*filter_line)(uint8_t *dst,
+ uint8_t *prev, uint8_t *cur, uint8_t *next,
+ int w, int prefs, int mrefs, int parity, int mode);
+ const AVPixFmtDescriptor *csp;
+} YADIFContext;\r
+\r
+struct scalable_yadif_context\r
+{\r
+ std::vector<std::function<void()>> calls;\r
+ int end_prefs;\r
+\r
+ scalable_yadif_context() : end_prefs(std::numeric_limits<int>::max()){}\r
+};\r
+\r
+void (*org_yadif_filter_line)(uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int prefs, int mrefs, int parity, int mode) = 0;\r
+\r
+void scalable_yadif_filter_line(scalable_yadif_context& ctx, uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int prefs, int mrefs, int parity, int mode)\r
+{\r
+ if(ctx.end_prefs == std::numeric_limits<int>::max())\r
+ ctx.end_prefs = -prefs;\r
+\r
+ ctx.calls.push_back([=]\r
+ {\r
+ org_yadif_filter_line(dst, prev, cur, next, w, prefs, mrefs, parity, mode);\r
+ });\r
+\r
+ if(prefs == ctx.end_prefs)\r
+ { \r
+ tbb::parallel_for(tbb::blocked_range<size_t>(0, ctx.calls.size()), [=](const tbb::blocked_range<size_t>& r)\r
+ {\r
+ for(auto n = r.begin(); n != r.end(); ++n)\r
+ ctx.calls[n]();\r
+ });\r
+ ctx.calls = std::vector<std::function<void()>>();\r
+ ctx.end_prefs = std::numeric_limits<int>::max();\r
+ }\r
+}\r
+\r
+#define RENAME(a) f ## a\r
+\r
+#define ff(x) \\r
+void RENAME(x)(uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int prefs, int mrefs, int parity, int mode) \\r
+{\\r
+ static scalable_yadif_context ctx;\\r
+ scalable_yadif_filter_line(ctx, dst, prev, cur, next, w, prefs, mrefs, parity, mode);\\r
+}\r
+\r
+ff(0); ff(1); ff(2); ff(3); ff(4); ff(5); ff(6); ff(7); ff(8); ff(9); ff(10); ff(11); ff(12); ff(13); ff(14); ff(15); ff(16); ff(17);\r
+\r
+void (*fs[])(uint8_t *dst, uint8_t *prev, uint8_t *cur, uint8_t *next, int w, int prefs, int mrefs, int parity, int mode) = \r
+\r
+{f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17};\r
+\r
+namespace caspar {\r
+ \r
+tbb::concurrent_bounded_queue<int> tags;\r
+\r
+void init()\r
+{\r
+ for(int n = 0; n < 18; ++n)\r
+ tags.push(n);\r
+}\r
+\r
+int make_scalable_yadif(AVFilterContext* ctx)\r
+{\r
+ static boost::once_flag flag = BOOST_ONCE_INIT;\r
+ boost::call_once(&init, flag);\r
+\r
+ YADIFContext* yadif = (YADIFContext*)ctx->priv;\r
+ org_yadif_filter_line = yadif->filter_line;\r
+\r
+ int tag;\r
+ if(!tags.try_pop(tag))\r
+ {\r
+ CASPAR_LOG(warning) << "Not enough scalable-yadif instances. Running non-scalable";\r
+ return -1;\r
+ }\r
+\r
+ yadif->filter_line = fs[tag]; // hmm, will only work for one concurrent instance...\r
+ return tag;\r
+}\r
+\r
+void release_scalable_yadif(int tag)\r
+{\r
+ if(tag != -1)\r
+ tags.push(tag);\r
+}\r
+\r
+}
\ No newline at end of file