]> git.sesse.net Git - casparcg/commitdiff
2.0: deinterlacer works, and needs to be integrated with the rest of the server.
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 18 Jun 2011 18:01:52 +0000 (18:01 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 18 Jun 2011 18:01:52 +0000 (18:01 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@911 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

modules/ffmpeg/ffmpeg.vcxproj
modules/ffmpeg/ffmpeg.vcxproj.filters
modules/ffmpeg/util/deinterlacer.cpp [new file with mode: 0644]
modules/ffmpeg/util/deinterlacer.h [new file with mode: 0644]
modules/ffmpeg/util/util.h [new file with mode: 0644]

index 4d4e64301896aa009b31a187f7acc4dea6789173..c19210de6e1a33c33b9d24b1a97afef81c82bd0c 100644 (file)
     <ClInclude Include="StdAfx.h" />\r
     <ClInclude Include="tbb_avcodec.h" />\r
     <ClInclude Include="util\deinterlacer.h" />\r
+    <ClInclude Include="util\util.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ProjectReference Include="..\..\common\common.vcxproj">\r
index 43a8d2601917b31dc0425d0460937f919ce30772..1a5e5656e1d3409cb77ca789db52711c4dea1bc6 100644 (file)
@@ -76,5 +76,8 @@
     <ClInclude Include="util\deinterlacer.h">\r
       <Filter>source\util</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="util\util.h">\r
+      <Filter>source\util</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
diff --git a/modules/ffmpeg/util/deinterlacer.cpp b/modules/ffmpeg/util/deinterlacer.cpp
new file mode 100644 (file)
index 0000000..76595b3
--- /dev/null
@@ -0,0 +1,162 @@
+#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
diff --git a/modules/ffmpeg/util/deinterlacer.h b/modules/ffmpeg/util/deinterlacer.h
new file mode 100644 (file)
index 0000000..8f5519c
--- /dev/null
@@ -0,0 +1,30 @@
+#pragma once\r
+\r
+#include <common/memory/safe_ptr.h>\r
+\r
+#include <vector>\r
+\r
+struct AVFrame;\r
+\r
+namespace caspar {\r
+\r
+namespace core {\r
+\r
+struct frame_factory;\r
+class write_frame;\r
+\r
+}\r
+       \r
+class deinterlacer\r
+{\r
+public:\r
+       deinterlacer(const safe_ptr<core::frame_factory>& factory);\r
+\r
+       std::vector<safe_ptr<core::write_frame>> execute(const safe_ptr<AVFrame>& frame);\r
+\r
+private:\r
+       struct implementation;\r
+       safe_ptr<implementation> impl_;\r
+};\r
+\r
+}
\ No newline at end of file
diff --git a/modules/ffmpeg/util/util.h b/modules/ffmpeg/util/util.h
new file mode 100644 (file)
index 0000000..d24a5f1
--- /dev/null
@@ -0,0 +1,72 @@
+#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