#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>
}
};
+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)
{
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
{
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_)
*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(...)
{
}
};
+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)
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"));
}
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"));
}
}
};
+template <typename Configuration>
struct decklink_consumer : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback, boost::noncopyable
{
const int channel_index_;
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_;
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_);
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(
audio_frame_buffer_.set_capacity((format_desc.fps > 50.0) ? 2 : 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));
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();
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.";
}
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_)
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;}
{
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.";
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;
}
};
+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)
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));
});
}
{
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)
{
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(
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;
}
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);
}
}}