]> git.sesse.net Git - casparcg/blobdiff - modules/decklink/consumer/decklink_consumer.cpp
Clarified logic around memcpy or not in Linux decklink consumer.
[casparcg] / modules / decklink / consumer / decklink_consumer.cpp
index c9fb588fbdac57c386b25d23eb472b4fcdc6cbd1..e453e33382d1444ef8c39aa59d64062460e5b9b0 100644 (file)
@@ -40,6 +40,8 @@
 #include <common/diagnostics/graph.h>
 #include <common/except.h>
 #include <common/memshfl.h>
+#include <common/memcpy.h>
+#include <common/no_init_proxy.h>
 #include <common/array.h>
 #include <common/future.h>
 #include <common/cache_aligned_vector.h>
@@ -62,14 +64,14 @@ struct configuration
                internal_keyer,
                external_keyer,
                external_separate_device_keyer,
-               default_keyer
+               default_keyer                                   = external_keyer
        };
 
        enum class latency_t
        {
                low_latency,
                normal_latency,
-               default_latency
+               default_latency = normal_latency
        };
 
        int                                                     device_index            = 1;
@@ -168,19 +170,32 @@ void set_keyer(
 
 class decklink_frame : public IDeckLinkVideoFrame
 {
-       tbb::atomic<int>                                ref_count_;
-       core::const_frame                               frame_;
-       const core::video_format_desc   format_desc_;
+       tbb::atomic<int>                                                                ref_count_;
+       core::const_frame                                                               frame_;
+       const core::video_format_desc                                   format_desc_;
 
-       const bool                                              key_only_;
-       cache_aligned_vector<uint8_t>   data_;
+       const bool                                                                              key_only_;
+       bool                                                                                    needs_to_copy_;
+       cache_aligned_vector<no_init_proxy<uint8_t>>    data_;
 public:
-       decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only)
+       decklink_frame(core::const_frame frame, const core::video_format_desc& format_desc, bool key_only, bool will_attempt_dma)
                : frame_(frame)
                , format_desc_(format_desc)
                , key_only_(key_only)
        {
                ref_count_ = 0;
+
+               bool dma_transfer_from_gl_buffer_impossible;
+
+#if !defined(_MSC_VER)
+               // On Linux Decklink cannot DMA transfer from memory returned by glMapBuffer (at least on nvidia)
+               dma_transfer_from_gl_buffer_impossible = true;
+#else
+               // On Windows it is possible.
+               dma_transfer_from_gl_buffer_impossible = false;
+#endif
+
+               needs_to_copy_ = will_attempt_dma && dma_transfer_from_gl_buffer_impossible;
        }
        
        // IUnknown
@@ -216,7 +231,7 @@ public:
                {
                        if(static_cast<int>(frame_.image_data().size()) != format_desc_.size)
                        {
-                               data_.resize(format_desc_.size, 0);
+                               data_.resize(format_desc_.size);
                                *buffer = data_.data();
                        }
                        else if(key_only_)
@@ -229,7 +244,16 @@ public:
                                *buffer = data_.data();
                        }
                        else
+                       {
                                *buffer = const_cast<uint8_t*>(frame_.image_data().begin());
+
+                               if (needs_to_copy_)
+                               {
+                                       data_.resize(frame_.image_data().size());
+                                       fast_memcpy(data_.data(), *buffer, frame_.image_data().size());
+                                       *buffer = data_.data();
+                               }
+                       }
                }
                catch(...)
                {
@@ -278,7 +302,7 @@ struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyab
 
                if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
                        CASPAR_THROW_EXCEPTION(caspar_exception()
-                                       << msg_info(u8(print) + " Failed to set key playback completion callback.")
+                                       << msg_info(print + L" Failed to set key playback completion callback.")
                                        << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
        }
 
@@ -286,11 +310,11 @@ struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyab
        void enable_video(BMDDisplayMode display_mode, const Print& print)
        {
                if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable key video output."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable key video output."));
 
                if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
                        CASPAR_THROW_EXCEPTION(caspar_exception()
-                                       << msg_info(u8(print()) + " Failed to set key playback completion callback.")
+                                       << msg_info(print() + L" Failed to set key playback completion callback.")
                                        << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
        }
 
@@ -343,6 +367,7 @@ struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLink
        tbb::atomic<bool>                                                                       is_running_;
                
        const std::wstring                                                                      model_name_                             = get_model_name(decklink_);
+       bool                                                                                            will_attempt_dma_;
        const core::video_format_desc                                           format_desc_;
        const core::audio_channel_layout                                        in_channel_layout_;
        const core::audio_channel_layout                                        out_channel_layout_             = config_.get_adjusted_layout(in_channel_layout_);
@@ -360,9 +385,10 @@ struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLink
        tbb::concurrent_bounded_queue<core::const_frame>        audio_frame_buffer_;
        
        spl::shared_ptr<diagnostics::graph>                                     graph_;
-       tbb::atomic<int64_t>                                                            current_presentation_delay_;
        caspar::timer                                                                           tick_timer_;
        retry_task<bool>                                                                        send_completion_;
+       reference_signal_detector                                                       reference_signal_detector_      { output_ };
+       tbb::atomic<int64_t>                                                            current_presentation_delay_;
        tbb::atomic<int64_t>                                                            scheduled_frames_completed_;
        std::unique_ptr<key_video_context>                                      key_context_;
 
@@ -406,7 +432,7 @@ public:
                graph_->set_text(print());
                diagnostics::register_graph(graph_);
                
-               enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));
+               enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, will_attempt_dma_));
                                
                if(config.embedded_audio)
                        enable_audio();
@@ -442,10 +468,10 @@ public:
        void enable_audio()
        {
                if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType32bitInteger, out_channel_layout_.num_channels, bmdAudioOutputStreamTimestamped)))
-                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable audio output."));
+                               CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable audio output."));
                                
                if(FAILED(output_->SetAudioCallback(this)))
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not set audio callback."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not set audio callback."));
 
                CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";
        }
@@ -453,11 +479,11 @@ public:
        void enable_video(BMDDisplayMode display_mode)
        {
                if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) 
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Could not enable fill video output."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Could not enable fill video output."));
                
                if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))
                        CASPAR_THROW_EXCEPTION(caspar_exception() 
-                                                                       << msg_info(u8(print()) + " Failed to set fill playback completion callback.")
+                                                                       << msg_info(print() + L" Failed to set fill playback completion callback.")
                                                                        << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
 
                if (key_context_)
@@ -467,10 +493,10 @@ public:
        void start_playback()
        {
                if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0))) 
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule fill playback."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule fill playback."));
 
                if (key_context_ && FAILED(key_context_->output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Failed to schedule key playback."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to schedule key playback."));
        }
        
        virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*)       {return E_NOINTERFACE;}
@@ -517,14 +543,14 @@ public:
                        else if(result == bmdOutputFrameFlushed)
                                graph_->set_tag(diagnostics::tag_severity::WARNING, "flushed-frame");
 
-                       auto frame = core::const_frame::empty();        
+                       UINT32 buffered;
+                       output_->GetBufferedVideoFrameCount(&buffered);
+                       graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
+
+                       auto frame = core::const_frame::empty();
                        video_frame_buffer_.pop(frame);
                        send_completion_.try_completion();
                        schedule_next_video(frame);     
-                       
-                       UINT32 buffered;
-                       output_->GetBufferedVideoFrameCount(&buffered);
-                       graph_->set_value("buffered-video", static_cast<double>(buffered)/format_desc_.fps);
                }
                catch(...)
                {
@@ -563,14 +589,14 @@ public:
 
                                while(audio_frame_buffer_.try_pop(frame))
                                {
+                                       UINT32 buffered;
+                                       output_->GetBufferedAudioSampleFrameCount(&buffered);
+                                       graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
+
                                        send_completion_.try_completion();
                                        schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
                                }
                        }
-
-                       UINT32 buffered;
-                       output_->GetBufferedAudioSampleFrameCount(&buffered);
-                       graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
                }
                catch(...)
                {
@@ -599,12 +625,12 @@ public:
        {
                if (key_context_)
                {
-                       auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true));
+                       auto key_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, true, will_attempt_dma_));
                        if (FAILED(key_context_->output_->ScheduleVideoFrame(get_raw(key_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
                                CASPAR_LOG(error) << print() << L" Failed to schedule key video.";
                }
 
-               auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only));
+               auto fill_frame = wrap_raw<com_ptr, IDeckLinkVideoFrame>(new decklink_frame(frame, format_desc_, config_.key_only, will_attempt_dma_));
                if (FAILED(output_->ScheduleVideoFrame(get_raw(fill_frame), video_scheduled_, format_desc_.duration, format_desc_.time_scale)))
                        CASPAR_LOG(error) << print() << L" Failed to schedule fill video.";
 
@@ -612,6 +638,8 @@ public:
 
                graph_->set_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);
                tick_timer_.restart();
+
+               reference_signal_detector_.detect_change([this]() { return print(); });
        }
 
        std::future<bool> send(core::const_frame frame)
@@ -625,7 +653,7 @@ public:
                        std::rethrow_exception(exception);              
 
                if(!is_running_)
-                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(u8(print()) + " Is not running."));
+                       CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Is not running."));
                
                bool audio_ready = !config_.embedded_audio;
                bool video_ready = false;
@@ -832,7 +860,7 @@ spl::shared_ptr<core::frame_consumer> create_consumer(
                auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
 
                if (!found_layout)
-                       CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + channel_layout + L" not found."));
+                       CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found."));
 
                config.out_channel_layout = *found_layout;
        }
@@ -853,7 +881,7 @@ spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
        else if (keyer == L"external_separate_device")
                config.keyer = configuration::keyer_t::external_separate_device_keyer;
 
-       auto latency = ptree.get(L"latency", L"normal");
+       auto latency = ptree.get(L"latency", L"default");
        if(latency == L"low")
                config.latency = configuration::latency_t::low_latency;
        else if(latency == L"normal")
@@ -863,10 +891,12 @@ spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
 
        if (channel_layout)
        {
+               CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
+
                auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
 
                if (!found_layout)
-                       CASPAR_THROW_EXCEPTION(file_not_found() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
+                       CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found."));
 
                config.out_channel_layout = *found_layout;
        }