]> git.sesse.net Git - casparcg/commitdiff
2.0.0.2:
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Wed, 11 May 2011 15:28:43 +0000 (15:28 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Wed, 11 May 2011 15:28:43 +0000 (15:28 +0000)
 - decklink_consumer: Optimized performance. Removed uneccessary frame copy.
 - decklink_consumer: Further reduced latency by 1 frame.

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

modules/decklink/consumer/decklink_consumer.cpp

index 054aec5a4b5b8746a66465819aa93251fcd3bb44..5218dc834c757afa5532310c3c13d5db6ea03514 100644 (file)
@@ -83,6 +83,38 @@ struct configuration
                , latency(default_latency){}\r
 };\r
 \r
+class decklink_frame_adapter : public IDeckLinkVideoFrame\r
+{\r
+       safe_ptr<const core::read_frame> frame_;\r
+       core::video_format_desc format_desc_;\r
+public:\r
+       decklink_frame_adapter(const safe_ptr<const core::read_frame>& frame, const core::video_format_desc& format_desc)\r
+               : frame_(frame)\r
+               , format_desc_(format_desc){}\r
+       \r
+       STDMETHOD (QueryInterface(REFIID, LPVOID*))     {return E_NOINTERFACE;}\r
+       STDMETHOD_(ULONG, AddRef())                                     {return 1;}\r
+       STDMETHOD_(ULONG, Release())                            {return 1;}\r
+\r
+       STDMETHOD_(long, GetWidth())                            {return format_desc_.width;}        \r
+    STDMETHOD_(long, GetHeight())                              {return format_desc_.height;}        \r
+    STDMETHOD_(long, GetRowBytes())                            {return format_desc_.width*4;}        \r
+       STDMETHOD_(BMDPixelFormat, GetPixelFormat()){return bmdFormat8BitBGRA;}        \r
+    STDMETHOD_(BMDFrameFlags, GetFlags())              {return bmdFrameFlagDefault;}\r
+        \r
+    STDMETHOD(GetBytes(void** buffer))\r
+       {\r
+               static std::vector<unsigned char> zeros(1920*1080*4, 0);\r
+               *buffer = const_cast<unsigned char*>(frame_->image_data().begin());\r
+               if(static_cast<size_t>(frame_->image_data().size()) != format_desc_.size)\r
+                       *buffer = zeros.data();\r
+               return S_OK;\r
+       }\r
+        \r
+    STDMETHOD(GetTimecode(BMDTimecodeFormat format, IDeckLinkTimecode** timecode)){return S_FALSE;}        \r
+    STDMETHOD(GetAncillaryData(IDeckLinkVideoFrameAncillary** ancillary))                {return S_FALSE;}\r
+};\r
+\r
 struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable\r
 {              \r
        const configuration config_;\r
@@ -98,11 +130,12 @@ struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLink
                \r
        const std::wstring model_name_;\r
        const core::video_format_desc format_desc_;\r
+       const size_t buffer_size_;\r
 \r
        unsigned long frames_scheduled_;\r
        unsigned long audio_scheduled_;\r
                \r
-       std::vector<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>> reserved_frames_;\r
+       std::list<decklink_frame_adapter> frame_container_; // Must be std::list in order to guarantee that pointers are always valid.\r
        boost::circular_buffer<std::vector<short>> audio_container_;\r
 \r
        tbb::concurrent_bounded_queue<std::shared_ptr<const core::read_frame>> video_frame_buffer_;\r
@@ -111,7 +144,6 @@ struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLink
        std::shared_ptr<diagnostics::graph> graph_;\r
        boost::timer tick_timer_;\r
 \r
-       size_t buffer_size_;\r
 \r
 public:\r
        decklink_consumer(const configuration& config, const core::video_format_desc& format_desc) \r
@@ -122,12 +154,16 @@ public:
                , keyer_(decklink_)\r
                , model_name_(get_model_name(decklink_))\r
                , format_desc_(format_desc)\r
+               , buffer_size_(config.embedded_audio ? 5 : 4) // Minimum buffer-size (3 + 1 tolerance).\r
                , frames_scheduled_(0)\r
                , audio_scheduled_(0)\r
-               , buffer_size_(4) // Minimum buffer-size (3 + 1 tolerance).\r
+               , audio_container_(buffer_size_+1)\r
        {\r
                is_running_ = true;\r
-                                               \r
+                               \r
+               video_frame_buffer_.set_capacity(1);\r
+               audio_frame_buffer_.set_capacity(1);\r
+\r
                graph_ = diagnostics::create_graph(narrow(print()));\r
                graph_->add_guide("tick-time", 0.5);\r
                graph_->set_color("tick-time", diagnostics::color(0.1f, 0.7f, 0.8f));\r
@@ -142,17 +178,9 @@ public:
 \r
                set_latency(config.latency);                            \r
                set_keyer(config.keyer);\r
-                                                       \r
-               audio_container_.set_capacity(buffer_size_+1);\r
-               allocate_frames(buffer_size_+1);\r
-                               \r
-               CASPAR_LOG(info) << print() << L" Buffer-depth: " << buffer_size_;\r
-               \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(2);\r
-               audio_frame_buffer_.set_capacity(2);\r
                \r
                if(config.embedded_audio)\r
                        output_->BeginAudioPreroll();\r
@@ -162,6 +190,7 @@ public:
                                BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
                }\r
                \r
+               CASPAR_LOG(info) << print() << L" Buffer depth: " << buffer_size_;              \r
                CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name;  \r
        }\r
 \r
@@ -231,8 +260,6 @@ public:
                        BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback."));\r
 \r
                CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
-\r
-               buffer_size_ = 5; //  Minimum buffer-size with embedded-audio (4 + 1 tolerance).\r
        }\r
 \r
        void enable_video(BMDDisplayMode display_mode)\r
@@ -243,41 +270,39 @@ public:
                if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
                        BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback."));\r
        }\r
-\r
-       void allocate_frames(size_t count)\r
-       {\r
-               std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>> frame;\r
-               std::generate_n(std::back_inserter(reserved_frames_), count, [&]() -> decltype(frame)\r
-               {\r
-                       if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &frame.second)))\r
-                               BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to create frame."));\r
-\r
-                       if(FAILED(frame.second->GetBytes(&frame.first)) || frame.first == nullptr)\r
-                               BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes."));\r
-\r
-                       return frame;\r
-               });\r
-       }\r
-\r
+       \r
        STDMETHOD (QueryInterface(REFIID, LPVOID*))     {return E_NOINTERFACE;}\r
        STDMETHOD_(ULONG, AddRef())                                     {return 1;}\r
        STDMETHOD_(ULONG, Release())                            {return 1;}\r
        \r
-       STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult result))\r
+       STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* completed_frame, BMDOutputFrameCompletionResult result))\r
        {\r
                if(!is_running_)\r
                        return E_FAIL;\r
+               \r
+               try\r
+               {\r
+                       if(result == bmdOutputFrameDisplayedLate)\r
+                               graph_->add_tag("late-frame");\r
+                       else if(result == bmdOutputFrameDropped)\r
+                               graph_->add_tag("dropped-frame");\r
+                       else if(result == bmdOutputFrameFlushed)\r
+                               graph_->add_tag("flushed-frame");\r
+\r
+                       frame_container_.erase(std::remove_if(frame_container_.begin(), frame_container_.end(), [&](const decklink_frame_adapter& frame)\r
+                       {\r
+                               return &frame == completed_frame;\r
+                       }), frame_container_.end());\r
 \r
-               if(result == bmdOutputFrameDisplayedLate)\r
-                       graph_->add_tag("late-frame");\r
-               else if(result == bmdOutputFrameDropped)\r
-                       graph_->add_tag("dropped-frame");\r
-               else if(result == bmdOutputFrameFlushed)\r
-                       graph_->add_tag("flushed-frame");\r
-\r
-               std::shared_ptr<const core::read_frame> frame;  \r
-               video_frame_buffer_.pop(frame);         \r
-               schedule_next_video(safe_ptr<const core::read_frame>(frame));\r
+                       std::shared_ptr<const core::read_frame> frame;  \r
+                       video_frame_buffer_.pop(frame);         \r
+                       schedule_next_video(safe_ptr<const core::read_frame>(frame));                   \r
+               }\r
+               catch(...)\r
+               {\r
+                       exception_ = std::current_exception();\r
+                       return E_FAIL;\r
+               }\r
 \r
                return S_OK;\r
        }\r
@@ -330,15 +355,10 @@ public:
                        \r
        void schedule_next_video(const safe_ptr<const core::read_frame>& frame)\r
        {\r
-               if(static_cast<size_t>(frame->image_data().size()) == format_desc_.size)\r
-                       fast_memcpy(reserved_frames_.front().first, frame->image_data().begin(), frame->image_data().size());\r
-               else\r
-                       fast_memclr(reserved_frames_.front().first, format_desc_.size);\r
-\r
-               if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\r
+               frame_container_.push_back(decklink_frame_adapter(frame, format_desc_));\r
+               if(FAILED(output_->ScheduleVideoFrame(&frame_container_.back(), (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.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", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
                tick_timer_.restart();\r
        }\r
@@ -350,10 +370,10 @@ public:
 \r
                if(!is_running_)\r
                        BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Is not running."));\r
-\r
-               video_frame_buffer_.push(frame);\r
+               \r
                if(config_.embedded_audio)\r
-                       audio_frame_buffer_.push(frame);                \r
+                       audio_frame_buffer_.push(frame);        \r
+               video_frame_buffer_.push(frame);        \r
        }\r
        \r
        std::wstring print() const\r