]> git.sesse.net Git - casparcg/commitdiff
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Mon, 20 Jun 2011 08:27:50 +0000 (08:27 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Mon, 20 Jun 2011 08:27:50 +0000 (08:27 +0000)
12 files changed:
Casparcg 2.0 Documentation.txt
common/env.cpp
modules/decklink/interop/DeckLinkAPI_h.h
modules/decklink/interop/DeckLinkAPI_i.c
modules/ffmpeg/ffmpeg.vcxproj
modules/ffmpeg/ffmpeg.vcxproj.filters
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/filter/filter.cpp [new file with mode: 0644]
modules/ffmpeg/producer/filter/filter.h [new file with mode: 0644]
modules/ffmpeg/producer/video/pix_fmt.h [new file with mode: 0644]
modules/ffmpeg/producer/video/video_decoder.cpp
shell/main.cpp

index f8dd38e45320367e1b20aae8a1828aa2c38ecdb0..c319fa9307545a636286741a1261f8e09a00714c 100644 (file)
@@ -106,8 +106,6 @@ CasparCG 2.0 currently (fully or partially) supports the following producers:
 \r
        decklink        Plays video and/or audio  to a decklink-device.\r
                \r
-       ffmpeg          Plays video and/or audio to a file.                             \r
-                                               \r
        oal             Plays audio to the default system audio-device.         \r
        \r
        ogl             Plays video to a windowed or fullscreen window.         \r
index 085de6f40e2ae7cb89190102621d2c13be9b3df8..571797609f2c99b58455e93832a672e5c04f0cd3 100644 (file)
@@ -98,7 +98,7 @@ const std::wstring& data_folder()
 \r
 const std::wstring& version()\r
 {\r
-       static std::wstring ver = std::wstring(L"") + CASPAR_GEN + L"." + CASPAR_MAYOR + L"." + CASPAR_MINOR + L"." + CASPAR_REV + L" UNSTABLE";\r
+       static std::wstring ver = std::wstring(L"") + CASPAR_GEN + L"." + CASPAR_MAYOR + L"." + CASPAR_MINOR + L"." + CASPAR_REV + L" ALPHA";\r
        return ver;\r
 }\r
 \r
index 6042be118bc8fe5037f75a723b1d05c7e5baed6f..a0231a41b092f6563f62cbf75f3fdbb221c0bffe 100644 (file)
@@ -4,7 +4,7 @@
 \r
 \r
  /* File created by MIDL compiler version 7.00.0555 */\r
-/* at Sat Jun 18 14:20:31 2011\r
+/* at Mon Jun 20 10:23:35 2011\r
  */\r
 /* Compiler settings for interop\DeckLinkAPI.idl:\r
     Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 \r
index 778f32b94e460afc3bcebe0783977e363d0aca28..817db1f271360dc377c4147dd62efd6eb9fbee98 100644 (file)
@@ -6,7 +6,7 @@
 \r
 \r
  /* File created by MIDL compiler version 7.00.0555 */\r
-/* at Sat Jun 18 14:20:31 2011\r
+/* at Mon Jun 20 10:23:35 2011\r
  */\r
 /* Compiler settings for interop\DeckLinkAPI.idl:\r
     Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 7.00.0555 \r
index 492768cf7489edcec4d548ea33ca27da2fb8f75f..3d459fb019555090a8887c30c50867852664aaf2 100644 (file)
       <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\filter.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\input.cpp">\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../StdAfx.h</PrecompiledHeaderFile>\r
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>\r
     </ClCompile>\r
     <ClCompile Include="tbb_avcodec.cpp" />\r
-    <ClCompile Include="util\filter.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
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="consumer\ffmpeg_consumer.h" />\r
     <ClInclude Include="ffmpeg_error.h" />\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\input.h" />\r
+    <ClInclude Include="producer\video\pix_fmt.h" />\r
     <ClInclude Include="producer\video\video_decoder.h" />\r
     <ClInclude Include="StdAfx.h" />\r
     <ClInclude Include="tbb_avcodec.h" />\r
-    <ClInclude Include="util\filter.h" />\r
-    <ClInclude Include="util\util.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ProjectReference Include="..\..\common\common.vcxproj">\r
index b8d401abef6a966d3dac7a7b23d939d28b39dc25..3ddd555367705e13c5921e0e757133b4da1b47fd 100644 (file)
@@ -16,8 +16,8 @@
     <Filter Include="source\producer\audio">\r
       <UniqueIdentifier>{6937307b-550f-48f8-9cc0-509de0e18ddc}</UniqueIdentifier>\r
     </Filter>\r
-    <Filter Include="source\util">\r
-      <UniqueIdentifier>{dbf0125b-6087-4b63-94b0-8d652b12a2ca}</UniqueIdentifier>\r
+    <Filter Include="source\producer\filter">\r
+      <UniqueIdentifier>{0d46b4fa-2b19-44b7-82ea-52cbd9ad24a4}</UniqueIdentifier>\r
     </Filter>\r
   </ItemGroup>\r
   <ItemGroup>\r
@@ -43,8 +43,8 @@
     <ClCompile Include="ffmpeg.cpp">\r
       <Filter>source</Filter>\r
     </ClCompile>\r
-    <ClCompile Include="util\filter.cpp">\r
-      <Filter>source\util</Filter>\r
+    <ClCompile Include="producer\filter\filter.cpp">\r
+      <Filter>source\producer\filter</Filter>\r
     </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="ffmpeg.h">\r
       <Filter>source</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="util\util.h">\r
-      <Filter>source\util</Filter>\r
+    <ClInclude Include="producer\filter\filter.h">\r
+      <Filter>source\producer\filter</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="util\filter.h">\r
-      <Filter>source\util</Filter>\r
+    <ClInclude Include="producer\video\pix_fmt.h">\r
+      <Filter>source\producer\video</Filter>\r
     </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 8a963d8c2955694b4cadf6647cfe159c9a1fee32..15717ce2f17f6b9d74b29c2648d0bc5e5010935a 100644 (file)
@@ -149,6 +149,7 @@ public:
                        {\r
                                auto frame2 = std::move(video_frames_.front().second);  \r
                                video_frames_.pop_front();\r
+                               frame2->get_audio_transform().set_has_audio(false);     \r
 \r
                                return core::basic_frame::interlace(frame, frame2, frame_factory_->get_video_format_desc().mode);\r
                        }\r
diff --git a/modules/ffmpeg/producer/filter/filter.cpp b/modules/ffmpeg/producer/filter/filter.cpp
new file mode 100644 (file)
index 0000000..3c86868
--- /dev/null
@@ -0,0 +1,169 @@
+#include "../../stdafx.h"\r
+\r
+#include "filter.h"\r
+\r
+#include "../../ffmpeg_error.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 <boost/circular_buffer.hpp>\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 filter::implementation\r
+{\r
+       std::string                                                             filters_;\r
+       std::shared_ptr<AVFilterGraph>                  graph_;\r
+       AVFilterContext*                                                video_in_filter_;\r
+       AVFilterContext*                                                video_out_filter_;\r
+\r
+       boost::circular_buffer<std::shared_ptr<AVFilterBufferRef>> buffers_;\r
+               \r
+       implementation(const std::string& filters) \r
+               : filters_(filters)\r
+       {\r
+               std::transform(filters_.begin(), filters_.end(), filters_.begin(), ::tolower);\r
+\r
+               buffers_.set_capacity(3);\r
+       }\r
+\r
+       void push(const safe_ptr<AVFrame>& frame)\r
+       {               \r
+               int errn = 0;   \r
+\r
+               if(!graph_)\r
+               {\r
+                       graph_.reset(avfilter_graph_alloc(), [](AVFilterGraph* p){avfilter_graph_free(&p);});\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 || !video_in_filter_)\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 || !video_out_filter_)\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
+                       AVFilterInOut* outputs = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
+                       AVFilterInOut* inputs  = reinterpret_cast<AVFilterInOut*>(av_malloc(sizeof(AVFilterInOut)));\r
+\r
+                       outputs->name                   = av_strdup("in");\r
+                       outputs->filter_ctx             = video_in_filter_;\r
+                       outputs->pad_idx                = 0;\r
+                       outputs->next                   = NULL;\r
+\r
+                       inputs->name                    = av_strdup("out");\r
+                       inputs->filter_ctx              = video_out_filter_;\r
+                       inputs->pad_idx                 = 0;\r
+                       inputs->next                    = NULL;\r
+                       \r
+                       errn = avfilter_graph_parse(graph_.get(), filters_.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(), AV_VSRC_BUF_FLAG_OVERWRITE);\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
+\r
+       std::vector<safe_ptr<AVFrame>> poll()\r
+       {\r
+               int 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::vector<safe_ptr<AVFrame>> result;\r
+\r
+               std::generate_n(std::back_inserter(result), errn, [&]{return request_frame();});\r
+\r
+               return result;\r
+       }\r
+               \r
+       safe_ptr<AVFrame> request_frame()\r
+       {               \r
+               auto link = video_out_filter_->inputs[0];\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 pic = reinterpret_cast<AVPicture*>(link->cur_buf->buf);\r
+               \r
+               safe_ptr<AVFrame> frame(avcodec_alloc_frame(), av_free);\r
+               avcodec_get_frame_defaults(frame.get());        \r
+\r
+               for(size_t n = 0; n < 4; ++n)\r
+               {\r
+                       frame->data[n]          = pic->data[n];\r
+                       frame->linesize[n]      = pic->linesize[n];\r
+               }\r
+\r
+               // FIXME\r
+               frame->width                    = link->cur_buf->video->w;\r
+               frame->height                   = link->cur_buf->video->h;\r
+               frame->format                   = link->cur_buf->format;\r
+               frame->interlaced_frame = link->cur_buf->video->interlaced;\r
+               frame->top_field_first  = link->cur_buf->video->top_field_first;\r
+               frame->key_frame                = link->cur_buf->video->key_frame;\r
+\r
+               buffers_.push_back(std::shared_ptr<AVFilterBufferRef>(link->cur_buf, avfilter_unref_buffer));\r
+\r
+               return frame;\r
+       }\r
+};\r
+\r
+filter::filter(const std::string& filters) : impl_(new implementation(filters)){}\r
+void filter::push(const safe_ptr<AVFrame>& frame) {impl_->push(frame);}\r
+std::vector<safe_ptr<AVFrame>> filter::poll() {return impl_->poll();}\r
+\r
+}
\ No newline at end of file
diff --git a/modules/ffmpeg/producer/filter/filter.h b/modules/ffmpeg/producer/filter/filter.h
new file mode 100644 (file)
index 0000000..cbfb818
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma once\r
+\r
+#include <common/memory/safe_ptr.h>\r
+\r
+#include <vector>\r
+\r
+struct AVFrame;\r
+\r
+namespace caspar {\r
+               \r
+class filter\r
+{\r
+public:\r
+       filter(const std::string& filters);\r
+\r
+       void push(const safe_ptr<AVFrame>& frame);\r
+       std::vector<safe_ptr<AVFrame>> poll();\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/producer/video/pix_fmt.h b/modules/ffmpeg/producer/video/pix_fmt.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
index 3f2c41d798f6d191e8ee0d8bd2a55818b67d1a62..a541c5d763bf1e140b9d7a2fab8e6811a06a9da9 100644 (file)
 #include "../../stdafx.h"\r
 \r
 #include "video_decoder.h"\r
+#include "pix_fmt.h"\r
+\r
 #include "../../ffmpeg_error.h"\r
-#include "../../util/util.h"\r
-#include "../../util/filter.h"\r
+#include "../filter/filter.h"\r
 \r
 #include <common/memory/memcpy.h>\r
 \r
@@ -66,8 +67,9 @@ struct video_decoder::implementation : boost::noncopyable
        std::shared_ptr<filter>                                         filter_;\r
        size_t                                                                          filter_delay_;\r
 \r
-       safe_ptr<AVFrame>                                                       last_frame_;\r
-       std::string filter_str_;\r
+       std::shared_ptr<AVFrame>                                        last_frame_;\r
+       std::shared_ptr<AVFrame>                                        decoded_frame_;\r
+       std::string                                                                     filter_str_;\r
 \r
 public:\r
        explicit implementation(input& input, const safe_ptr<core::frame_factory>& frame_factory, const std::string& filter_str) \r
@@ -96,63 +98,114 @@ public:
        std::deque<std::pair<int, safe_ptr<core::write_frame>>> decode(const std::shared_ptr<AVPacket>& video_packet)\r
        {                       \r
                std::deque<std::pair<int, safe_ptr<core::write_frame>>> result;\r
-\r
+               \r
                if(!video_packet) // eof\r
-               {       \r
-                       for(size_t n = 0; n < filter_delay_; ++n)\r
-                               boost::range::push_back(result, get_frames(last_frame_));\r
+                       return flush();\r
+\r
+               if(filter_)\r
+               {\r
+                       if(decoded_frame_)\r
+                               push_filter_frames(make_safe(decoded_frame_));  \r
                        \r
-                       // FIXME: Unnecessary reinitialization\r
-                       filter_.reset(filter_str_.empty() ? nullptr : new filter(filter_str_));\r
+                       std::shared_ptr<AVFrame> frame;\r
 \r
-                       frame_number_ = 0;\r
-                       filter_delay_ = 0;\r
-                       avcodec_flush_buffers(&codec_context_);\r
+                       tbb::parallel_invoke(\r
+                       [&]\r
+                       {\r
+                               frame = decode_frame(video_packet);\r
+                       },\r
+                       [&]\r
+                       {\r
+                               if(decoded_frame_)\r
+                                       result = poll_filter_frames();\r
+                       });             \r
 \r
-                       return result;\r
+                       decoded_frame_ = frame;\r
+                       if(frame)\r
+                               last_frame_ = frame;                    \r
                }\r
-\r
-               int frame_finished = 0;\r
-               const int errn = avcodec_decode_video2(&codec_context_, last_frame_.get(), &frame_finished, video_packet.get());\r
-               \r
-               if(errn < 0)\r
+               else\r
                {\r
-                       BOOST_THROW_EXCEPTION(\r
-                               invalid_operation() <<\r
-                               msg_info(av_error_str(errn)) <<\r
-                               boost::errinfo_api_function("avcodec_decode_video") <<\r
-                               boost::errinfo_errno(AVUNERROR(errn)));\r
+                       auto frame = decode_frame(video_packet);\r
+                       \r
+                       if(frame)\r
+                               result.push_back(std::make_pair(frame_number_++, make_write_frame(make_safe(frame))));\r
                }\r
-               \r
-               if(frame_finished != 0)         \r
-                       result = get_frames(last_frame_);\r
 \r
                return result;\r
        }\r
 \r
-       std::deque<std::pair<int, safe_ptr<core::write_frame>>> get_frames(const safe_ptr<AVFrame>& frame)\r
+       std::deque<std::pair<int, safe_ptr<core::write_frame>>> flush()\r
        {\r
                std::deque<std::pair<int, safe_ptr<core::write_frame>>> result;\r
-                       \r
+\r
                if(filter_)\r
                {\r
-                       auto frames = filter_->execute(frame);\r
+                       // Get all buffered frames\r
+                       boost::range::push_back(result, poll_filter_frames());\r
 \r
-                       boost::range::transform(frames, std::back_inserter(result), [this](const safe_ptr<AVFrame>& frame)\r
-                       {\r
-                               return std::make_pair(frame_number_, make_write_frame(frame));\r
-                       });\r
+                       for(size_t n = 0; n < filter_delay_; ++n)\r
+                       {                                       \r
+                               push_filter_frames(make_safe(last_frame_));     \r
+                               boost::range::push_back(result, poll_filter_frames());\r
+                       }\r
 \r
-                       if(!frames.empty())\r
-                               ++frame_number_;\r
-                       else\r
-                               ++filter_delay_;\r
+                       // FIXME: Unnecessary reinitialization\r
+                       filter_.reset(filter_str_.empty() ? nullptr : new filter(filter_str_));\r
+                       filter_delay_ = 0;\r
                }\r
-               else\r
-                       result.push_back(std::make_pair(frame_number_++, make_write_frame(frame)));\r
 \r
+               frame_number_ = 0;\r
+               avcodec_flush_buffers(&codec_context_);\r
+\r
+               return result;\r
+       }\r
+\r
+       void push_filter_frames(const safe_ptr<AVFrame>& frame)\r
+       {               \r
+               filter_->push(frame);           \r
+       }\r
+\r
+       std::deque<std::pair<int, safe_ptr<core::write_frame>>> poll_filter_frames()\r
+       {\r
+               std::deque<std::pair<int, safe_ptr<core::write_frame>>> result;\r
+\r
+               auto frames = filter_->poll(); \r
+\r
+               boost::range::transform(frames, std::back_inserter(result), [&](const safe_ptr<AVFrame>& frame)\r
+               {\r
+                       return std::make_pair(frame_number_, make_write_frame(frame));\r
+               });\r
+\r
+               if(!frames.empty())\r
+                       ++frame_number_;\r
+               else            \r
+                       ++filter_delay_;\r
+               \r
                return result;\r
        }\r
+       \r
+       std::shared_ptr<AVFrame> decode_frame(const std::shared_ptr<AVPacket>& video_packet)\r
+       {\r
+               std::shared_ptr<AVFrame> decoded_frame(avcodec_alloc_frame(), av_free);\r
+\r
+               int frame_finished = 0;\r
+               const int errn = avcodec_decode_video2(&codec_context_, decoded_frame.get(), &frame_finished, video_packet.get());\r
+               \r
+               if(errn < 0)\r
+               {\r
+                       BOOST_THROW_EXCEPTION(\r
+                               invalid_operation() <<\r
+                               msg_info(av_error_str(errn)) <<\r
+                               boost::errinfo_api_function("avcodec_decode_video") <<\r
+                               boost::errinfo_errno(AVUNERROR(errn)));\r
+               }\r
+\r
+               if(frame_finished == 0)         \r
+                       decoded_frame = nullptr;\r
+\r
+               return decoded_frame;\r
+       }\r
 \r
        safe_ptr<core::write_frame> make_write_frame(safe_ptr<AVFrame> decoded_frame)\r
        {                       \r
index 9d739a4640a3d326651fa2ec01c78dad9fec2d36..cfdd86cacd62f5b37f61c1600ca8fa7ecc878fee 100644 (file)
@@ -129,7 +129,7 @@ int main(int argc, wchar_t* argv[])
 {      \r
        static_assert(sizeof(void*) == 4, "64-bit code generation is not supported.");\r
        \r
-       CASPAR_LOG(info) << L"THIS IS AN UNSTABLE BUILD";\r
+       CASPAR_LOG(info) << L"THIS IS AN ALPHA BUILD";\r
 \r
        // Set debug mode.\r
        #ifdef _DEBUG\r