]> git.sesse.net Git - casparcg/blobdiff - modules/decklink/consumer/decklink_consumer.cpp
No more use for retry_task in decklink_consumer now since audio and video are complet...
[casparcg] / modules / decklink / consumer / decklink_consumer.cpp
index 50b5d2c8c3dffe306b90fbed13890d7b1ca64435..37dfeea1b7d057c30e42b0aecd33a9ec237709ff 100644 (file)
@@ -24,6 +24,7 @@
 #include "decklink_consumer.h"
 
 #include "../util/util.h"
+#include "../decklink.h"
 
 #include "../decklink_api.h"
 
 #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>
 #include <common/timer.h>
 #include <common/param.h>
+#include <common/software_version.h>
 
 #include <tbb/concurrent_queue.h>
 
@@ -53,6 +57,8 @@
 #include <boost/circular_buffer.hpp>
 #include <boost/property_tree/ptree.hpp>
 
+#include <future>
+
 namespace caspar { namespace decklink { 
        
 struct configuration
@@ -83,7 +89,7 @@ struct configuration
        
        int buffer_depth() const
        {
-               return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1) + (embedded_audio ? 1 : 0);
+               return base_buffer_depth + (latency == latency_t::low_latency ? 0 : 1);
        }
 
        int key_device_index() const
@@ -117,8 +123,9 @@ struct configuration
        }
 };
 
+template <typename Configuration>
 void set_latency(
-               const com_iface_ptr<IDeckLinkConfiguration>& config,
+               const com_iface_ptr<Configuration>& config,
                configuration::latency_t latency,
                const std::wstring& print)
 {
@@ -168,19 +175,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 +236,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 +249,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(...)
                {
@@ -256,16 +285,17 @@ public:
        }
 };
 
+template <typename Configuration>
 struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyable
 {
-       const configuration                                                                             config_;
-       com_ptr<IDeckLink>                                                                              decklink_                                       = get_device(config_.key_device_index());
-       com_iface_ptr<IDeckLinkOutput>                                                  output_                                         = iface_cast<IDeckLinkOutput>(decklink_);
-       com_iface_ptr<IDeckLinkKeyer>                                                   keyer_                                          = iface_cast<IDeckLinkKeyer>(decklink_);
-       com_iface_ptr<IDeckLinkAttributes>                                              attributes_                                     = iface_cast<IDeckLinkAttributes>(decklink_);
-       com_iface_ptr<IDeckLinkConfiguration>                                   configuration_                          = iface_cast<IDeckLinkConfiguration>(decklink_);
-       tbb::atomic<int64_t>                                                                    current_presentation_delay_;
-       tbb::atomic<int64_t>                                                                    scheduled_frames_completed_;
+       const configuration                                     config_;
+       com_ptr<IDeckLink>                                      decklink_                                       = get_device(config_.key_device_index());
+       com_iface_ptr<IDeckLinkOutput>          output_                                         = iface_cast<IDeckLinkOutput>(decklink_);
+       com_iface_ptr<IDeckLinkKeyer>           keyer_                                          = iface_cast<IDeckLinkKeyer>(decklink_, true);
+       com_iface_ptr<IDeckLinkAttributes>      attributes_                                     = iface_cast<IDeckLinkAttributes>(decklink_);
+       com_iface_ptr<Configuration>            configuration_                          = iface_cast<Configuration>(decklink_);
+       tbb::atomic<int64_t>                            current_presentation_delay_;
+       tbb::atomic<int64_t>                            scheduled_frames_completed_;
 
        key_video_context(const configuration& config, const std::wstring& print)
                : config_(config)
@@ -278,7 +308,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 +316,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"));
        }
 
@@ -326,15 +356,16 @@ struct key_video_context : public IDeckLinkVideoOutputCallback, boost::noncopyab
        }
 };
 
-struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
+template <typename Configuration>
+struct decklink_consumer : public IDeckLinkVideoOutputCallback, boost::noncopyable
 {              
        const int                                                                                       channel_index_;
        const configuration                                                                     config_;
 
        com_ptr<IDeckLink>                                                                      decklink_                               = get_device(config_.device_index);
        com_iface_ptr<IDeckLinkOutput>                                          output_                                 = iface_cast<IDeckLinkOutput>(decklink_);
-       com_iface_ptr<IDeckLinkConfiguration>                           configuration_                  = iface_cast<IDeckLinkConfiguration>(decklink_);
-       com_iface_ptr<IDeckLinkKeyer>                                           keyer_                                  = iface_cast<IDeckLinkKeyer>(decklink_);
+       com_iface_ptr<Configuration>                                            configuration_                  = iface_cast<Configuration>(decklink_);
+       com_iface_ptr<IDeckLinkKeyer>                                           keyer_                                  = iface_cast<IDeckLinkKeyer>(decklink_, true);
        com_iface_ptr<IDeckLinkAttributes>                                      attributes_                             = iface_cast<IDeckLinkAttributes>(decklink_);
 
        tbb::spin_mutex                                                                         exception_mutex_;
@@ -343,6 +374,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_);
@@ -356,16 +388,15 @@ struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLink
                
        boost::circular_buffer<std::vector<int32_t>>            audio_container_                { buffer_size_ + 1 };
 
-       tbb::concurrent_bounded_queue<core::const_frame>        video_frame_buffer_;
-       tbb::concurrent_bounded_queue<core::const_frame>        audio_frame_buffer_;
+       tbb::concurrent_bounded_queue<core::const_frame>        frame_buffer_;
        
        spl::shared_ptr<diagnostics::graph>                                     graph_;
        caspar::timer                                                                           tick_timer_;
-       retry_task<bool>                                                                        send_completion_;
+       std::packaged_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_;
+       std::unique_ptr<key_video_context<Configuration>>       key_context_;
 
 public:
        decklink_consumer(
@@ -382,15 +413,10 @@ public:
                current_presentation_delay_ = 0;
                scheduled_frames_completed_ = 0;
                                
-               video_frame_buffer_.set_capacity(1);
-
-               // Blackmagic calls RenderAudioSamples() 50 times per second
-               // regardless of video mode so we sometimes need to give them
-               // samples from 2 frames in order to keep up
-               audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 1);
+               frame_buffer_.set_capacity(1);
 
                if (config.keyer == configuration::keyer_t::external_separate_device_keyer)
-                       key_context_.reset(new key_video_context(config, print()));
+                       key_context_.reset(new key_video_context<Configuration>(config, print()));
 
                graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
                graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));
@@ -407,7 +433,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();
@@ -415,21 +441,31 @@ public:
                set_latency(configuration_, config.latency, print());                           
                set_keyer(attributes_, keyer_, config.keyer, print());
 
-               if(config.embedded_audio)               
+               if(config.embedded_audio)
                        output_->BeginAudioPreroll();           
                
-               for(int n = 0; n < buffer_size_; ++n)
+               for (int n = 0; n < buffer_size_; ++n)
+               {
+                       if (config.embedded_audio)
+                               schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[n % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
+
                        schedule_next_video(core::const_frame::empty());
+               }
 
-               if(!config.embedded_audio)
-                       start_playback();
+               if (config.embedded_audio)
+               {
+                       // Preroll one extra frame worth of audio
+                       schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[buffer_size_ % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
+                       output_->EndAudioPreroll();
+               }
+
+               start_playback();
        }
 
        ~decklink_consumer()
        {               
                is_running_ = false;
-               video_frame_buffer_.try_push(core::const_frame::empty());
-               audio_frame_buffer_.try_push(core::const_frame::empty());
+               frame_buffer_.try_push(core::const_frame::empty());
 
                if(output_ != nullptr) 
                {
@@ -443,22 +479,19 @@ 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_LOG(info) << print() << L" Enabled embedded-audio.";
        }
 
        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_)
@@ -468,10 +501,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;}
@@ -492,6 +525,12 @@ public:
                
                try
                {
+                       auto tick_time = tick_timer_.elapsed()*format_desc_.fps * 0.5;
+                       graph_->set_value("tick-time", tick_time);
+                       tick_timer_.restart();
+
+                       reference_signal_detector_.detect_change([this]() { return print(); });
+
                        auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
                        current_presentation_delay_ = dframe->get_age_millis();
                        ++scheduled_frames_completed_;
@@ -508,10 +547,7 @@ public:
                        {
                                graph_->set_tag(diagnostics::tag_severity::WARNING, "late-frame");
                                video_scheduled_ += format_desc_.duration;
-                               audio_scheduled_ += dframe->audio_data().size() / out_channel_layout_.num_channels;
-                               //++video_scheduled_;
-                               //audio_scheduled_ += format_desc_.audio_cadence[0];
-                               //++audio_scheduled_;
+                               audio_scheduled_ += dframe->audio_data().size() / in_channel_layout_.num_channels;
                        }
                        else if(result == bmdOutputFrameDropped)
                                graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
@@ -522,10 +558,23 @@ public:
                        output_->GetBufferedVideoFrameCount(&buffered);
                        graph_->set_value("buffered-video", static_cast<double>(buffered) / (config_.buffer_depth()));
 
+                       if (config_.embedded_audio)
+                       {
+                               output_->GetBufferedAudioSampleFrameCount(&buffered);
+                               graph_->set_value("buffered-audio", static_cast<double>(buffered) / (format_desc_.audio_cadence[0] * config_.buffer_depth()));
+                       }
+
                        auto frame = core::const_frame::empty();
-                       video_frame_buffer_.pop(frame);
-                       send_completion_.try_completion();
-                       schedule_next_video(frame);     
+
+                       frame_buffer_.pop(frame);
+
+                       if (send_completion_.valid())
+                               send_completion_();
+
+                       if (config_.embedded_audio)
+                               schedule_next_audio(channel_remapper_.mix_and_rearrange(frame.audio_data()));
+
+                       schedule_next_video(frame);
                }
                catch(...)
                {
@@ -538,50 +587,6 @@ public:
 
                return S_OK;
        }
-               
-       virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)
-       {
-               if(!is_running_)
-                       return E_FAIL;
-               
-               try
-               {       
-                       if(preroll)
-                       {
-                               if(++preroll_count_ >= buffer_size_)
-                               {
-                                       output_->EndAudioPreroll();
-                                       start_playback();                               
-                               }
-                               else
-                               {
-                                       schedule_next_audio(core::mutable_audio_buffer(format_desc_.audio_cadence[preroll % format_desc_.audio_cadence.size()] * out_channel_layout_.num_channels, 0));
-                               }
-                       }
-                       else
-                       {
-                               auto frame = core::const_frame::empty();
-
-                               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()));
-                               }
-                       }
-               }
-               catch(...)
-               {
-                       tbb::spin_mutex::scoped_lock lock(exception_mutex_);
-                       exception_ = std::current_exception();
-                       return E_FAIL;
-               }
-
-               return S_OK;
-       }
 
        template<typename T>
        void schedule_next_audio(const T& audio_data)
@@ -600,21 +605,16 @@ 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.";
 
                video_scheduled_ += format_desc_.duration;
-
-               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)
@@ -628,29 +628,17 @@ 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;
+               if (frame_buffer_.try_push(frame))
+                       return make_ready_future(true);
 
-               auto enqueue_task = [audio_ready, video_ready, frame, this]() mutable -> boost::optional<bool>
+               send_completion_ = std::packaged_task<bool ()>([frame, this] () mutable -> bool
                {
-                       if (!audio_ready)
-                               audio_ready = audio_frame_buffer_.try_push(frame);
-
-                       if (!video_ready)
-                               video_ready = video_frame_buffer_.try_push(frame);
-
-                       if (audio_ready && video_ready)
-                               return true;
-                       else
-                               return boost::optional<bool>();
-               };
-               
-               if (enqueue_task())
-                       return make_ready_future(true);
+                       frame_buffer_.push(frame);
 
-               send_completion_.set_task(enqueue_task);
+                       return true;
+               });
 
                return send_completion_.get_future();
        }
@@ -670,13 +658,14 @@ public:
        }
 };
 
+template <typename Configuration>
 struct decklink_consumer_proxy : public core::frame_consumer
 {
-       core::monitor::subject                          monitor_subject_;
-       const configuration                                     config_;
-       std::unique_ptr<decklink_consumer>      consumer_;
-       core::video_format_desc                         format_desc_;
-       executor                                                        executor_;
+       core::monitor::subject                                                          monitor_subject_;
+       const configuration                                                                     config_;
+       std::unique_ptr<decklink_consumer<Configuration>>       consumer_;
+       core::video_format_desc                                                         format_desc_;
+       executor                                                                                        executor_;
 public:
 
        decklink_consumer_proxy(const configuration& config)
@@ -708,7 +697,7 @@ public:
                executor_.invoke([=]
                {
                        consumer_.reset();
-                       consumer_.reset(new decklink_consumer(config_, format_desc, channel_layout, channel_index));                    
+                       consumer_.reset(new decklink_consumer<Configuration>(config_, format_desc, channel_layout, channel_index));                     
                });
        }
        
@@ -765,7 +754,21 @@ public:
        {
                return monitor_subject_;
        }
-};     
+};
+
+const software_version<3>& get_driver_version()
+{
+       static software_version<3> version(u8(get_version()));
+
+       return version;
+}
+
+const software_version<3> get_new_configuration_api_version()
+{
+       static software_version<3> NEW_CONFIGURATION_API("10.2");
+
+       return NEW_CONFIGURATION_API;
+}
 
 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
 {
@@ -835,12 +838,17 @@ 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;
        }
 
-       return spl::make_shared<decklink_consumer_proxy>(config);
+       bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
+
+       if (old_configuration_api)
+               return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
+       else
+               return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
 }
 
 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
@@ -866,10 +874,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;
        }
@@ -880,7 +890,12 @@ spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
        config.embedded_audio           = ptree.get(L"embedded-audio",  config.embedded_audio);
        config.base_buffer_depth        = ptree.get(L"buffer-depth",    config.base_buffer_depth);
 
-       return spl::make_shared<decklink_consumer_proxy>(config);
+       bool old_configuration_api = get_driver_version() < get_new_configuration_api_version();
+
+       if (old_configuration_api)
+               return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration_v10_2>>(config);
+       else
+               return spl::make_shared<decklink_consumer_proxy<IDeckLinkConfiguration>>(config);
 }
 
 }}