~co_init(){CoUninitialize();}\r
} co_;\r
\r
- std::exception_ptr exception_;\r
const configuration config_;\r
\r
- std::wstring model_name_;\r
- tbb::atomic<bool> is_running_;\r
-\r
- std::shared_ptr<diagnostics::graph> graph_;\r
- boost::timer perf_timer_;\r
-\r
- std::array<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>, BUFFER_SIZE+1> reserved_frames_;\r
- boost::circular_buffer<std::vector<short>> audio_container_;\r
- \r
CComPtr<IDeckLink> decklink_;\r
CComQIPtr<IDeckLinkOutput> output_;\r
CComQIPtr<IDeckLinkConfiguration> configuration_;\r
CComQIPtr<IDeckLinkKeyer> keyer_;\r
\r
+ std::exception_ptr exception_;\r
+\r
+ tbb::atomic<bool> is_running_;\r
+ \r
+ const std::wstring model_name_;\r
const core::video_format_desc format_desc_;\r
\r
BMDTimeScale frame_time_scale_;\r
BMDTimeValue frame_duration_;\r
unsigned long frames_scheduled_;\r
unsigned long audio_scheduled_;\r
- \r
+ \r
+ std::array<std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>>, BUFFER_SIZE+1> reserved_frames_;\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
tbb::concurrent_bounded_queue<std::shared_ptr<const core::read_frame>> audio_frame_buffer_;\r
+ \r
+ std::shared_ptr<diagnostics::graph> graph_;\r
+ boost::timer tick_timer_;\r
\r
public:\r
decklink_output(const configuration& config, const core::video_format_desc& format_desc) \r
- : model_name_(L"DECKLINK")\r
- , config_(config)\r
+ : config_(config)\r
+ , decklink_(get_device(config.device_index))\r
+ , output_(decklink_)\r
+ , configuration_(decklink_)\r
+ , keyer_(decklink_)\r
+ , model_name_(get_model_name(decklink_))\r
+ , format_desc_(format_desc)\r
, audio_container_(5)\r
, frames_scheduled_(0)\r
, audio_scheduled_(0)\r
- , format_desc_(format_desc)\r
{\r
is_running_ = true;\r
- CComPtr<IDeckLinkIterator> pDecklinkIterator;\r
- if(FAILED(pDecklinkIterator.CoCreateInstance(CLSID_CDeckLinkIterator)))\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " No Decklink drivers installed."));\r
- \r
- size_t n = 0;\r
- while(n < config_.device_index && pDecklinkIterator->Next(&decklink_) == S_OK){++n;} \r
-\r
- if(n != config_.device_index || !decklink_)\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Decklink card not found.") << arg_name_info("device_index") << arg_value_info(boost::lexical_cast<std::string>(config_.device_index)));\r
- \r
- BSTR pModelName;\r
- decklink_->GetModelName(&pModelName);\r
- model_name_ = std::wstring(pModelName);\r
- \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
+ graph_->set_color("late-frame", diagnostics::color(0.6f, 0.3f, 0.3f));\r
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
+ graph_->set_color("flushed-frame", diagnostics::color(0.3f, 0.3f, 0.6f));\r
\r
- output_ = decklink_;\r
- configuration_ = decklink_;\r
- keyer_ = decklink_;\r
-\r
- auto display_mode = get_display_mode(output_.p, format_desc_.format);\r
+ auto display_mode = get_display_mode(output_, format_desc_.format);\r
if(display_mode == nullptr) \r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat."));\r
\r
display_mode->GetFrameRate(&frame_duration_, &frame_time_scale_);\r
\r
BMDDisplayModeSupport displayModeSupport;\r
- if(FAILED(output_->DoesSupportVideoMode(display_mode->GetDisplayMode(), bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, &displayModeSupport, nullptr)))\r
+ if(FAILED(output_->DoesSupportVideoMode(display_mode->GetDisplayMode(), bmdFormat8BitBGRA, bmdVideoOutputFlagDefault, &displayModeSupport, nullptr)) || displayModeSupport == bmdDisplayModeNotSupported)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat."));\r
- \r
- if(config_.embedded_audio)\r
+ else if(displayModeSupport == bmdDisplayModeSupportedWithConversion)\r
+ CASPAR_LOG(warning) << print() << " Display mode is supported with conversion.";\r
+\r
+ if(config.embedded_audio)\r
{\r
if(FAILED(output_->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2, bmdAudioOutputStreamTimestamped)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable audio output."));\r
CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
}\r
\r
- if(config_.latency == normal_latency)\r
+ if(config.latency == normal_latency)\r
{\r
configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, false);\r
CASPAR_LOG(info) << print() << L" Enabled normal-latency mode";\r
}\r
- else if(config_.latency == low_latency)\r
+ else if(config.latency == low_latency)\r
{ \r
configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);\r
CASPAR_LOG(info) << print() << L" Enabled low-latency mode";\r
else\r
CASPAR_LOG(info) << print() << L" Uses driver latency settings."; \r
\r
- if(config_.keyer == internal_key) \r
+ if(config.keyer == internal_key) \r
{\r
if(FAILED(keyer_->Enable(FALSE))) \r
CASPAR_LOG(error) << print() << L" Failed to enable internal keyer."; \r
if(FAILED(output_->SetScheduledFrameCompletionCallback(this)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to set playback completion callback."));\r
\r
- for(size_t n = 0; n < reserved_frames_.size(); ++n)\r
+ BOOST_FOREACH(auto& frame, reserved_frames_)\r
{\r
- if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &reserved_frames_[n].second)))\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(reserved_frames_[n].second->GetBytes(&reserved_frames_[n].first)))\r
+ if(FAILED(frame.second->GetBytes(&frame.first)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes."));\r
}\r
\r
video_frame_buffer_.set_capacity(2);\r
audio_frame_buffer_.set_capacity(2);\r
\r
- if(config_.embedded_audio)\r
+ if(config.embedded_audio)\r
output_->BeginAudioPreroll();\r
else\r
{\r
~decklink_output()\r
{ \r
is_running_ = false;\r
+ video_frame_buffer_.clear();\r
+ audio_frame_buffer_.clear();\r
video_frame_buffer_.try_push(core::read_frame::empty());\r
audio_frame_buffer_.try_push(core::read_frame::empty());\r
\r
output_->DisableAudioOutput();\r
output_->DisableVideoOutput();\r
}\r
- CASPAR_LOG(info) << print() << L" Shutting down."; \r
}\r
\r
virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID*) {return E_NOINTERFACE;}\r
virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;}\r
virtual ULONG STDMETHODCALLTYPE Release () {return 1;}\r
\r
- virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted (IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult /*result*/)\r
+ virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult result)\r
{\r
if(!is_running_)\r
- return S_OK;\r
+ return E_FAIL;\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
virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped (void)\r
{\r
is_running_ = false;\r
- CASPAR_LOG(info) << print() << L"Scheduled playback has stopped.";\r
+ CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";\r
return S_OK;\r
}\r
\r
- virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples (BOOL preroll)\r
+ virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)\r
{\r
if(!is_running_)\r
- return S_OK;\r
+ return E_FAIL;\r
\r
try\r
{\r
{\r
static std::vector<short> silence(48000, 0);\r
\r
- int audio_samples = static_cast<size_t>(48000.0 / format_desc_.fps);\r
-\r
- auto frame_audio_data = frame->audio_data().empty() ? silence.data() : const_cast<short*>(frame->audio_data().begin());\r
+ int audio_samples = static_cast<size_t>(48000.0 / format_desc_.fps)*2; // Audio samples per channel\r
\r
- audio_container_.push_back(std::vector<short>(frame_audio_data, frame_audio_data+audio_samples*2));\r
+ const short* frame_audio_data = frame->audio_data().size() == audio_samples ? frame->audio_data().begin() : silence.data();\r
+ audio_container_.push_back(std::vector<short>(frame_audio_data, frame_audio_data+audio_samples));\r
\r
- if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), audio_samples, (audio_scheduled_++) * audio_samples, 48000, nullptr)))\r
+ if(FAILED(output_->ScheduleAudioSamples(audio_container_.back().data(), audio_samples/2, (audio_scheduled_++) * audio_samples/2, 48000, nullptr)))\r
CASPAR_LOG(error) << print() << L" Failed to schedule audio.";\r
}\r
\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", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval)*0.5f);\r
- perf_timer_.restart();\r
+ graph_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/format_desc_.interval)*0.5f);\r
+ tick_timer_.restart();\r
}\r
\r
void send(const safe_ptr<const core::read_frame>& frame)\r
\r
decklink_consumer(const configuration& config)\r
: config_(config)\r
- , executor_(L"DECKLINK[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]", true){}\r
+ , executor_(L"decklink_consumer[" + boost::lexical_cast<std::wstring>(config.device_index) + L"]", true){}\r
\r
~decklink_consumer()\r
{\r
*/\r
#pragma once\r
\r
+#include <common/exception/exceptions.h>\r
#include <core/video_format.h>\r
\r
#include "../interop/DeckLinkAPI_h.h"\r
\r
+#include <atlbase.h>\r
+\r
namespace caspar { \r
\r
static BMDDisplayMode GetDecklinkVideoFormat(core::video_format::type fmt) \r
}\r
}\r
\r
-static IDeckLinkDisplayMode* get_display_mode(IDeckLinkOutput* output, BMDDisplayMode format)\r
+template<typename T>\r
+static IDeckLinkDisplayMode* get_display_mode(const T& output, BMDDisplayMode format)\r
{\r
- IDeckLinkDisplayModeIterator* iterator;\r
+ CComPtr<IDeckLinkDisplayModeIterator> iterator;\r
IDeckLinkDisplayMode* mode;\r
\r
if(FAILED(output->GetDisplayModeIterator(&iterator)))\r
if(mode->GetDisplayMode() == format)\r
return mode;\r
}\r
- iterator->Release();\r
+\r
return nullptr;\r
}\r
\r
-static IDeckLinkDisplayMode* get_display_mode(IDeckLinkOutput* output, core::video_format::type fmt)\r
+template<typename T>\r
+static IDeckLinkDisplayMode* get_display_mode(const T& output, core::video_format::type fmt)\r
{\r
return get_display_mode(output, GetDecklinkVideoFormat(fmt));\r
}\r
\r
template<typename T>\r
-static std::wstring get_version(T& deckLinkIterator)\r
+static std::wstring get_version(T& iterator)\r
{\r
- IDeckLinkAPIInformation* deckLinkAPIInformation;\r
- if (FAILED(deckLinkIterator->QueryInterface(IID_IDeckLinkAPIInformation, (void**)&deckLinkAPIInformation)))\r
+ CComQIPtr<IDeckLinkAPIInformation> info = iterator;\r
+ if (!info)\r
return L"Unknown";\r
\r
BSTR ver; \r
- deckLinkAPIInformation->GetString(BMDDeckLinkAPIVersion, &ver);\r
- deckLinkAPIInformation->Release(); \r
+ info->GetString(BMDDeckLinkAPIVersion, &ver);\r
\r
return ver; \r
}\r
\r
+static CComPtr<IDeckLink> get_device(size_t device_index)\r
+{\r
+ CComPtr<IDeckLink> decklink;\r
+\r
+ CComPtr<IDeckLinkIterator> pDecklinkIterator;\r
+ if(FAILED(pDecklinkIterator.CoCreateInstance(CLSID_CDeckLinkIterator)))\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("No Decklink drivers installed."));\r
+ \r
+ size_t n = 0;\r
+ while(n < device_index && pDecklinkIterator->Next(&decklink) == S_OK){++n;} \r
+\r
+ if(n != device_index || !decklink)\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Decklink card not found.") << arg_name_info("device_index") << arg_value_info(boost::lexical_cast<std::string>(device_index)));\r
+ \r
+ return decklink;\r
+}\r
+\r
+template <typename T>\r
+static std::wstring get_model_name(const T& device)\r
+{ \r
+ BSTR pModelName;\r
+ device->GetModelName(&pModelName);\r
+ return std::wstring(pModelName);\r
+}\r
+\r
}
\ No newline at end of file