]> git.sesse.net Git - casparcg/commitdiff
2.0.0.2:
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 7 May 2011 19:30:26 +0000 (19:30 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 7 May 2011 19:30:26 +0000 (19:30 +0000)
 - decklink_consumer: Refactored.
 - decklink_consumer: Rewrote and reduced prerolling to decrease latency. Still has a preroll of 4 frames.

git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@687 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

core/consumer/frame_consumer.h
modules/decklink/consumer/decklink_consumer.cpp
modules/decklink/consumer/decklink_consumer.h
shell/server.cpp

index a5fe5d4e4a4e03141a340c823e1d4a7202969d65..02e504dc967fac8ab04e22c8e7e4216d18e0c15f 100644 (file)
@@ -37,7 +37,7 @@ struct frame_consumer : boost::noncopyable
        virtual ~frame_consumer() {}\r
        \r
        virtual void send(const safe_ptr<const read_frame>& frame) = 0;\r
-       virtual size_t buffer_depth() const = 0;\r
+       virtual size_t buffer_depth() const {return 1;}\r
        virtual void initialize(const video_format_desc& format_desc) = 0;\r
        virtual std::wstring print() const = 0;\r
 \r
index 91515150c5cba369a2136772f4fe5b311a368135..29a39bdff4f63fa99fde6183f23735a718f19ff7 100644 (file)
@@ -33,6 +33,8 @@
 #include <common/concurrency/executor.h>\r
 #include <common/diagnostics/graph.h>\r
 #include <common/exception/exceptions.h>\r
+#include <common/memory/memcpy.h>\r
+#include <common/memory/memclr.h>\r
 #include <common/utility/timer.h>\r
 \r
 #include <tbb/concurrent_queue.h>\r
 \r
 namespace caspar { \r
        \r
+enum key\r
+{\r
+       external_key,\r
+       internal_key,\r
+       default_key\r
+};\r
+\r
+enum latency\r
+{\r
+       low_latency,\r
+       normal_latency,\r
+       default_latency\r
+};\r
+\r
+struct configuration\r
+{\r
+       size_t device_index;\r
+       bool embedded_audio;\r
+       key keyer;\r
+       latency latency;\r
+       \r
+       configuration()\r
+               : device_index(1)\r
+               , embedded_audio(false)\r
+               , keyer(default_key)\r
+               , latency(default_latency)\r
+       {}\r
+       configuration(const boost::property_tree::ptree& ptree)\r
+               : device_index(1)\r
+               , embedded_audio(false)\r
+               , keyer(default_key)\r
+               , latency(default_latency)\r
+       {       \r
+               auto key_str = ptree.get("key", "default");\r
+               if(key_str == "internal")\r
+                       keyer = internal_key;\r
+               else if(key_str == "external")\r
+                       keyer = external_key;\r
+\r
+               auto latency_str = ptree.get("latency", "default");\r
+               if(latency_str == "normal")\r
+                       latency = normal_latency;\r
+               else if(latency_str == "low")\r
+                       latency = low_latency;\r
+\r
+               device_index = ptree.get("device", 0);\r
+               embedded_audio  = ptree.get("embedded-audio", false);\r
+       }\r
+\r
+       configuration(const std::vector<std::wstring>& params)\r
+               : device_index(1)\r
+               , embedded_audio(false)\r
+               , keyer(default_key)\r
+               , latency(default_latency)\r
+       {\r
+               if(params.size() > 0)\r
+                       device_index = lexical_cast_or_default<int>(params[0], device_index);\r
+\r
+               {\r
+                       auto it = std::find(params.begin(), params.end(), L"INTERNAL_KEY");\r
+                       if(it != params.end())\r
+                               keyer = internal_key;\r
+                       else\r
+                       {\r
+                               auto it = std::find(params.begin(), params.end(), L"EXTERNAL_KEY");\r
+                               if(it != params.end())\r
+                                       keyer = external_key;\r
+                       }\r
+               }\r
+               \r
+               embedded_audio = std::find(params.begin(), params.end(), L"EMBED_AUDIO") != params.end();       \r
+       }\r
+};\r
+\r
 struct decklink_output : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
 {              \r
+       static const size_t BUFFER_SIZE = 4;\r
+       \r
        struct co_init\r
        {\r
                co_init(){CoInitialize(nullptr);}\r
                ~co_init(){CoUninitialize();}\r
        } co_;\r
        \r
-       const decklink_consumer::configuration config_;\r
+       std::exception_ptr exception_;\r
+       const configuration config_;\r
 \r
-       std::wstring    model_name_;\r
+       std::wstring model_name_;\r
        tbb::atomic<bool> is_running_;\r
 \r
        std::shared_ptr<diagnostics::graph> graph_;\r
        boost::timer perf_timer_;\r
 \r
-       std::array<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>, 3> reserved_frames_;\r
+       std::array<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>, BUFFER_SIZE+1> reserved_frames_;\r
        boost::circular_buffer<std::vector<short>> audio_container_;\r
        \r
        CComPtr<IDeckLink>                                      decklink_;\r
@@ -88,8 +167,8 @@ struct decklink_output : public IDeckLinkVideoOutputCallback, public IDeckLinkAu
        tbb::concurrent_bounded_queue<std::shared_ptr<const core::read_frame>> audio_frame_buffer_;\r
 \r
 public:\r
-       decklink_output(const decklink_consumer::configuration& config, const core::video_format_desc& format_desc) \r
-               :  model_name_(L"DECKLINK")\r
+       decklink_output(const configuration& config, const core::video_format_desc& format_desc) \r
+               : model_name_(L"DECKLINK")\r
                , config_(config)\r
                , audio_container_(5)\r
                , frames_scheduled_(0)\r
@@ -129,7 +208,7 @@ public:
                if(FAILED(output_->DoesSupportVideoMode(display_mode->GetDisplayMode(), bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, &displayModeSupport, nullptr)))\r
                        BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat."));\r
                \r
-               if(config_.embed_audio)\r
+               if(config_.embedded_audio)\r
                {\r
                        if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
                                BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
@@ -140,38 +219,47 @@ public:
                        CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
                }\r
 \r
-               if(config_.low_latency)\r
+               if(config_.latency == normal_latency)\r
+               {\r
+                       configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);\r
+                       CASPAR_LOG(info) << print() << L" Enabled normal-latency mode";\r
+               }\r
+               else if(config_.latency == low_latency)\r
+               {                       \r
                        configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);\r
+                       CASPAR_LOG(info) << print() << L" Enabled low-latency mode";\r
+               }\r
+               else\r
+                       CASPAR_LOG(info) << print() << L" Uses driver latency settings.";       \r
                \r
-               if(FAILED(output_->EnableVideoOutput(display_mode->GetDisplayMode(), bmdVideoOutputFlagDefault))) \r
-                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));\r
-               \r
-               if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
-                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback."));\r
-                       \r
                CComQIPtr<IDeckLinkKeyer> keyer = decklink_;\r
-               if(config_.keyer == decklink_consumer::internal_key) \r
+               if(config_.keyer == internal_key) \r
                {\r
                        if(FAILED(keyer->Enable(FALSE)))                        \r
                                CASPAR_LOG(error) << print() << L" Failed to enable internal keyer.";                   \r
                        else if(FAILED(keyer->SetLevel(255)))                   \r
                                CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";\r
                        else\r
-                               CASPAR_LOG(info) << print() << L" Successfully configured internal keyer.";             \r
+                               CASPAR_LOG(info) << print() << L" Enabled internal keyer.";             \r
                }\r
-               else if(config.keyer == decklink_consumer::external_key)\r
+               else if(config.keyer == external_key)\r
                {\r
                        if(FAILED(keyer->Enable(TRUE)))                 \r
                                CASPAR_LOG(error) << print() << L" Failed to enable external keyer.";   \r
                        else if(FAILED(keyer->SetLevel(255)))                   \r
                                CASPAR_LOG(error) << print() << L" Failed to set key-level to max.";\r
                        else\r
-                               CASPAR_LOG(info) << print() << L" Successfully configured external keyer.";                     \r
+                               CASPAR_LOG(info) << print() << L" Enabled external keyer.";                     \r
                }\r
                else\r
-                               CASPAR_LOG(info) << print() << L" Uses default keyer settings.";        \r
+                       CASPAR_LOG(info) << print() << L" Uses driver keyer settings."; \r
 \r
+               if(FAILED(output_->EnableVideoOutput(display_mode->GetDisplayMode(), bmdVideoOutputFlagDefault))) \r
+                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));\r
                \r
+               if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
+                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback."));\r
+                                       \r
                for(size_t n = 0; n < reserved_frames_.size(); ++n)\r
                {\r
                        if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &reserved_frames_[n].second)))\r
@@ -181,22 +269,22 @@ public:
                                BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes."));\r
                }\r
                                        \r
-               auto buffer_size = static_cast<size_t>(frame_time_scale_/frame_duration_)/4;\r
-               for(size_t n = 0; n < buffer_size; ++n)\r
+               CASPAR_LOG(info) << print() << L" Buffer-depth: " << BUFFER_SIZE;\r
+               \r
+               for(size_t n = 0; n < BUFFER_SIZE; ++n)\r
                        schedule_next_video(core::read_frame::empty());\r
 \r
-               video_frame_buffer_.set_capacity(buffer_size);\r
-               audio_frame_buffer_.set_capacity(buffer_size);\r
-               for(size_t n = 0; n < std::max<size_t>(2, buffer_size-2); ++n)\r
+               video_frame_buffer_.set_capacity(2);\r
+               audio_frame_buffer_.set_capacity(2);\r
+               \r
+               if(config_.embedded_audio)\r
+                       output_->BeginAudioPreroll();\r
+               else\r
                {\r
-                       video_frame_buffer_.try_push(core::read_frame::empty());\r
-                       if(config_.embed_audio)\r
-                               audio_frame_buffer_.try_push(core::read_frame::empty());\r
+                       if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0))) \r
+                               BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
                }\r
                \r
-               if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0))) \r
-                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
-               \r
                CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name;  \r
        }\r
 \r
@@ -209,7 +297,7 @@ public:
                if(output_ != nullptr) \r
                {\r
                        output_->StopScheduledPlayback(0, nullptr, 0);\r
-                       if(config_.embed_audio)\r
+                       if(config_.embedded_audio)\r
                                output_->DisableAudioOutput();\r
                        output_->DisableVideoOutput();\r
                }\r
@@ -234,17 +322,33 @@ public:
 \r
        virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped (void)\r
        {\r
+               is_running_ = false;\r
                return S_OK;\r
        }\r
                \r
-       virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples (BOOL /*preroll*/)\r
+       virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples (BOOL preroll)\r
        {\r
                if(!is_running_)\r
                        return S_OK;\r
-\r
-               std::shared_ptr<const core::read_frame> frame;\r
-               audio_frame_buffer_.pop(frame);\r
-               schedule_next_audio(safe_ptr<const core::read_frame>(frame));\r
+               \r
+               try\r
+               {\r
+                       if(preroll)\r
+                       {\r
+                               if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0)))\r
+                                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
+                       }\r
+\r
+                       std::shared_ptr<const core::read_frame> frame;\r
+                       audio_frame_buffer_.pop(frame);\r
+                       schedule_next_audio(safe_ptr<const core::read_frame>(frame));\r
+               \r
+               }\r
+               catch(...)\r
+               {\r
+                       exception_ = std::current_exception();\r
+                       return E_FAIL;\r
+               }\r
 \r
                return S_OK;\r
        }\r
@@ -266,22 +370,25 @@ public:
        void schedule_next_video(const safe_ptr<const core::read_frame>& frame)\r
        {\r
                if(!frame->image_data().empty())\r
-                       std::copy(frame->image_data().begin(), frame->image_data().end(), static_cast<char*>(reserved_frames_.front().first));\r
+                       fast_memcpy(reserved_frames_.front().first, frame->image_data().begin(), frame->image_data().size());\r
                else\r
-                       std::fill_n(static_cast<int*>(reserved_frames_.front().first), 0, format_desc_.size/4);\r
+                       fast_memclr(reserved_frames_.front().first, format_desc_.size);\r
 \r
                if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * frame_duration_, frame_duration_, frame_time_scale_)))\r
                        CASPAR_LOG(error) << print() << L" Failed to schedule video.";\r
 \r
                std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end());\r
-               graph_->update_value("tick-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));\r
+               graph_->update_value("tick-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval)*0.5f);\r
                perf_timer_.restart();\r
        }\r
 \r
        void send(const safe_ptr<const core::read_frame>& frame)\r
        {\r
+               if(exception_ != nullptr)\r
+                       std::rethrow_exception(exception_);\r
+\r
                video_frame_buffer_.push(frame);\r
-               if(config_.embed_audio)\r
+               if(config_.embedded_audio)\r
                        audio_frame_buffer_.push(frame);\r
        }\r
 \r
@@ -291,22 +398,19 @@ public:
        }\r
 };\r
 \r
-struct decklink_consumer::implementation\r
+struct decklink_consumer : public core::frame_consumer\r
 {\r
        std::unique_ptr<decklink_output> input_;\r
-       decklink_consumer::configuration config_;\r
+       configuration config_;\r
 \r
        executor executor_;\r
 public:\r
 \r
-       implementation(const decklink_consumer::configuration& config)\r
+       decklink_consumer(const configuration& config)\r
                : config_(config)\r
-               , executor_(L"DECKLINK[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]")\r
-       {\r
-               executor_.start();\r
-       }\r
+               , executor_(L"DECKLINK[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]", true){}\r
 \r
-       ~implementation()\r
+       ~decklink_consumer()\r
        {\r
                executor_.invoke([&]\r
                {\r
@@ -326,47 +430,24 @@ public:
        {\r
                input_->send(frame);\r
        }\r
-\r
-       size_t buffer_depth() const\r
-       {\r
-               return 1;\r
-       }\r
-\r
+       \r
        std::wstring print() const\r
        {\r
                return input_->print();\r
        }\r
-};\r
+};     \r
 \r
-decklink_consumer::decklink_consumer(const configuration& config) : impl_(new implementation(config)){}\r
-decklink_consumer::decklink_consumer(decklink_consumer&& other) : impl_(std::move(other.impl_)){}\r
-void decklink_consumer::initialize(const core::video_format_desc& format_desc){impl_->initialize(format_desc);}\r
-void decklink_consumer::send(const safe_ptr<const core::read_frame>& frame){impl_->send(frame);}\r
-size_t decklink_consumer::buffer_depth() const{return impl_->buffer_depth();}\r
-std::wstring decklink_consumer::print() const{return impl_->print();}\r
-       \r
 safe_ptr<core::frame_consumer> create_decklink_consumer(const std::vector<std::wstring>& params) \r
 {\r
        if(params.size() < 1 || params[0] != L"DECKLINK")\r
                return core::frame_consumer::empty();\r
        \r
-       decklink_consumer::configuration config;\r
-\r
-       if(params.size() > 1) \r
-               config.device_index = lexical_cast_or_default<int>(params[2], config.device_index);\r
-\r
-       if(params.size() > 2)\r
-               config.embed_audio = lexical_cast_or_default<bool>(params[3], config.embed_audio);\r
-       \r
-       if(params.size() > 3) \r
-       {\r
-               if(params[4] == L"INTERNAL_KEY")\r
-                       config.keyer = decklink_consumer::internal_key;\r
-               else if(params[4] == L"EXTERNAL_KEY")\r
-                       config.keyer = decklink_consumer::external_key;\r
-       }\r
+       return make_safe<decklink_consumer>(configuration(std::vector<std::wstring>(params.begin()+1, params.end())));\r
+}\r
 \r
-       return make_safe<decklink_consumer>(config);\r
+safe_ptr<core::frame_consumer> create_decklink_consumer_ptree(const boost::property_tree::ptree& ptree) \r
+{\r
+       return make_safe<decklink_consumer>(configuration(ptree));\r
 }\r
 \r
 }
\ No newline at end of file
index 1da23a7bbebfe9ee18737435857699bdb4d2da76..0d1400a9946d6644d43099a6ec0e97e3472178fc 100644 (file)
 #pragma once\r
 \r
 #include <core/consumer/frame_consumer.h>\r
-\r
 #include <core/video_format.h>\r
 \r
+#include <boost/property_tree/ptree.hpp>\r
+\r
 #include <string>\r
 #include <vector>\r
 \r
 namespace caspar { \r
-\r
-class decklink_consumer : public core::frame_consumer\r
-{\r
-public:\r
-\r
-       enum key\r
-       {\r
-               external_key,\r
-               internal_key,\r
-               default_key\r
-       };\r
-\r
-       struct configuration\r
-       {\r
-               size_t device_index;\r
-               bool embed_audio;\r
-               key keyer;\r
-               bool low_latency;\r
-\r
-               configuration() \r
-                       : device_index(1)\r
-                       , embed_audio(false)\r
-                       , keyer(default_key)\r
-                       , low_latency(false){}\r
-       };\r
-\r
-       explicit decklink_consumer(const configuration& config);\r
-       decklink_consumer(decklink_consumer&& other);\r
        \r
-       virtual void initialize(const core::video_format_desc& format_desc);\r
-       virtual void send(const safe_ptr<const core::read_frame>&);\r
-       virtual size_t buffer_depth() const;\r
-       virtual std::wstring print() const;\r
-\r
-private:\r
-       struct implementation;\r
-       std::tr1::shared_ptr<implementation> impl_;\r
-};\r
-\r
 safe_ptr<core::frame_consumer> create_decklink_consumer(const std::vector<std::wstring>& params);\r
+safe_ptr<core::frame_consumer> create_decklink_consumer_ptree(const boost::property_tree::ptree& ptree);\r
 \r
 }
\ No newline at end of file
index 420d8455e62066d9e5f8fb09a328a0c04d1cb153..7f8b23043eb139a5b771a3e44f3c725be7ca43e2 100644 (file)
@@ -120,22 +120,8 @@ struct server::implementation : boost::noncopyable
                                        else if(name == "bluefish")                                     \r
                                                channels_.back()->consumer()->add(index++, bluefish_consumer(xml_consumer.second.get("device", 0), \r
                                                                                                                                                                        xml_consumer.second.get("embedded-audio", true)));                                      \r
-                                       else if(name == "decklink")\r
-                                       {\r
-                                               decklink_consumer::configuration config;\r
-                                               \r
-                                               auto key_str = xml_consumer.second.get("key", "default");\r
-                                               if(key_str == "internal")\r
-                                                       config.keyer = decklink_consumer::internal_key;\r
-                                               else if(key_str == "external")\r
-                                                       config.keyer = decklink_consumer::external_key;\r
-\r
-                                               config.device_index = xml_consumer.second.get("device", 0);\r
-                                               config.embed_audio = xml_consumer.second.get("embedded-audio", false);\r
-                                               config.low_latency = xml_consumer.second.get("low-latency", false);\r
-\r
-                                               channels_.back()->consumer()->add(index++, decklink_consumer(config));\r
-                                       }\r
+                                       else if(name == "decklink")                                     \r
+                                               channels_.back()->consumer()->add(index++, create_decklink_consumer_ptree(xml_consumer.second));                                        \r
                                        else if(name == "audio")\r
                                                channels_.back()->consumer()->add(index++, oal_consumer());                     \r
                                }\r