#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>
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;
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(...)
{
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"));
}
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_);
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_;
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;}
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(...)
{
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(...)
{
{
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.";
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)
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;
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;
}
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")
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;
}