class line : public drawable\r
{\r
boost::optional<diagnostics::guide> guide_;\r
- boost::circular_buffer<std::pair<float, bool>> line_data_;\r
+ boost::circular_buffer<std::pair<double, bool>> line_data_;\r
\r
std::vector<float> tick_data_;\r
bool tick_tag_;\r
line_data_.push_back(std::make_pair(-1.0f, false));\r
}\r
\r
- void update(float value)\r
+ void update(double value)\r
{\r
tick_data_.push_back(value);\r
}\r
\r
- void set(float value)\r
+ void set(double value)\r
{\r
tick_data_.clear();\r
tick_data_.push_back(value);\r
glColor4f(c_.red, c_.green, c_.blue, 0.7f); \r
for(size_t n = 0; n < line_data_.size(); ++n) \r
if(line_data_[n].first > -0.5)\r
- glVertex3f(x+n*dx, std::max(0.05f, std::min(0.95f, (1.0f-line_data_[n].first)*0.8f + 0.1f)), 0.0f); \r
+ glVertex3d(x+n*dx, std::max(0.05, std::min(0.95, (1.0f-line_data_[n].first)*0.8 + 0.1f)), 0.0); \r
glEnd();\r
\r
glEnable(GL_LINE_STIPPLE);\r
, name_(parent_printer_ ? narrow(parent_printer_()) : "")\r
, counter_(0){}\r
\r
- void update(const std::string& name, float value)\r
+ void update(const std::string& name, double value)\r
{\r
lines_[name].update(value);\r
}\r
\r
- void set(const std::string& name, float value)\r
+ void set(const std::string& name, double value)\r
{\r
lines_[name].set(value);\r
}\r
lines_[name].set_color(c);\r
}\r
\r
- void guide(const std::string& name, float value)\r
+ void guide(const std::string& name, double value)\r
{\r
lines_[name].guide(diagnostics::guide(value)); \r
}\r
context::register_drawable(impl_);\r
}\r
\r
-void graph::update_value(const std::string& name, float value)\r
+void graph::update_value(const std::string& name, double value)\r
{\r
if(impl_)\r
{ \r
});\r
}\r
}\r
-void graph::set_value(const std::string& name, float value)\r
+void graph::set_value(const std::string& name, double value)\r
{\r
if(impl_)\r
{ \r
});\r
}\r
}\r
-void graph::add_guide(const std::string& name, float value)\r
+void graph::add_guide(const std::string& name, double value)\r
{ \r
if(impl_)\r
{ \r
graph(const std::string& name);\r
graph(const printer& parent_printer);\r
public:\r
- void update_value(const std::string& name, float value);\r
- void set_value(const std::string& name, float value);\r
+ void update_value(const std::string& name, double value);\r
+ void set_value(const std::string& name, double value);\r
void set_color(const std::string& name, color c);\r
void add_tag(const std::string& name);\r
- void add_guide(const std::string& name, float value);\r
+ void add_guide(const std::string& name, double value);\r
private:\r
struct implementation;\r
std::shared_ptr<implementation> impl_;\r
CASPAR_LOG(error) << print() << L" " << it->second->print() << L" Removed.";\r
}\r
}\r
- diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
- diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
tick_timer_.restart();\r
});\r
diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
auto audio = mix_audio(frames);\r
auto image = image_future.get();\r
\r
- diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
output_(make_safe<const gpu_read_frame>(std::move(image), std::move(audio)));\r
\r
- diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
tick_timer_.restart();\r
});\r
diag_->set_value("input-buffer", static_cast<float>(executor_.size())/static_cast<float>(executor_.capacity()));\r
\r
output_timer_.restart();\r
output_(draw());\r
- diag_->update_value("output-time", static_cast<float>(output_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("output-time", static_cast<float>(output_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
executor_.begin_invoke([=]{tick();});\r
}\r
result.insert(map.begin(), map.end());\r
});\r
\r
- diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
- diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/format_desc_.interval*0.5));\r
+ diag_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
tick_timer_.restart();\r
\r
return result;\r
\r
#include <array>\r
\r
-#define DEFINE_VIDEOFORMATDESC(w, h, m, f, s, fmt) { (fmt), (w), (h), (m), (m == video_mode::progressive ? f : f/2.0), (1.0/(m == video_mode::progressive ? f : f/2.0)), ((w)*(h)*4), (s)}\r
+#define DEFINE_VIDEOFORMATDESC(fmt, w, h, m, scale, duration, name) \\r
+{ \\r
+ (fmt), \\r
+ (w), \\r
+ (h), \\r
+ (m), \\r
+ ((double)scale/(double)duration),\\r
+ (scale),\\r
+ (duration),\\r
+ (m == video_mode::progressive ? 1 : 2),\\r
+ ((w)*(h)*4),\\r
+ (name)\\r
+}\r
\r
namespace caspar { namespace core {\r
\r
const video_format_desc format_descs[video_format::count] = \r
{ \r
- DEFINE_VIDEOFORMATDESC(720, 576, video_mode::upper, 50, TEXT("pal"), video_format::pal ), \r
- DEFINE_VIDEOFORMATDESC(720, 486, video_mode::lower, 60/1.001, TEXT("ntsc"), video_format::ntsc ), \r
- DEFINE_VIDEOFORMATDESC(720, 576, video_mode::progressive, 25, TEXT("576p2500"), video_format::x576p2500 ),\r
- DEFINE_VIDEOFORMATDESC(1280, 720, video_mode::progressive, 25, TEXT("720p2500"), video_format::x720p2500 ), \r
- DEFINE_VIDEOFORMATDESC(1280, 720, video_mode::progressive, 50, TEXT("720p5000"), video_format::x720p5000 ), \r
- DEFINE_VIDEOFORMATDESC(1280, 720, video_mode::progressive, 60/1.001, TEXT("720p5994"), video_format::x720p5994 ),\r
- DEFINE_VIDEOFORMATDESC(1280, 720, video_mode::progressive, 60, TEXT("720p6000"), video_format::x720p6000 ),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 24/1.001, TEXT("1080p2397"), video_format::x1080p2397),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 24, TEXT("1080p2400"), video_format::x1080p2400),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::upper, 50, TEXT("1080i5000"), video_format::x1080i5000),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::upper, 60/1.001, TEXT("1080i5994"), video_format::x1080i5994),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::upper, 60, TEXT("1080i6000"), video_format::x1080i6000),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 25, TEXT("1080p2500"), video_format::x1080p2500),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 30/1.001, TEXT("1080p2997"), video_format::x1080p2997),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 30, TEXT("1080p3000"), video_format::x1080p3000),\r
- DEFINE_VIDEOFORMATDESC(1920, 1080, video_mode::progressive, 50, TEXT("1080p5000"), video_format::x1080p5000),\r
- DEFINE_VIDEOFORMATDESC(0, 0, video_mode::invalid, -1, TEXT("invalid"), video_format::invalid )\r
+ DEFINE_VIDEOFORMATDESC(video_format::pal ,720, 576, video_mode::upper, 25, 1, TEXT("pal") ), \r
+ DEFINE_VIDEOFORMATDESC(video_format::ntsc ,720, 486, video_mode::lower, 30000, 1001, TEXT("ntsc") ), \r
+ DEFINE_VIDEOFORMATDESC(video_format::x576p2500 ,720, 576, video_mode::progressive, 25, 1, TEXT("576p2500")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x720p2500 ,1280, 720, video_mode::progressive, 25, 1, TEXT("720p2500")), \r
+ DEFINE_VIDEOFORMATDESC(video_format::x720p5000 ,1280, 720, video_mode::progressive, 50, 1, TEXT("720p5000")), \r
+ DEFINE_VIDEOFORMATDESC(video_format::x720p5994 ,1280, 720, video_mode::progressive, 60000, 1001, TEXT("720p5994")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x720p6000 ,1280, 720, video_mode::progressive, 60, 1, TEXT("720p6000")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p2397 ,1920, 1080, video_mode::progressive, 24000, 1001, TEXT("1080p2398")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p2400 ,1920, 1080, video_mode::progressive, 24, 1, TEXT("1080p2400")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080i5000 ,1920, 1080, video_mode::upper, 25, 1, TEXT("1080i5000")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080i5994 ,1920, 1080, video_mode::upper, 60000, 1001, TEXT("1080i5994")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080i6000 ,1920, 1080, video_mode::upper, 60, 1, TEXT("1080i6000")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p2500 ,1920, 1080, video_mode::progressive, 25, 1, TEXT("1080p2500")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p2997 ,1920, 1080, video_mode::progressive, 30000, 1001, TEXT("1080p2997")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p3000 ,1920, 1080, video_mode::progressive, 30, 1, TEXT("1080p3000")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::x1080p5000 ,1920, 1080, video_mode::progressive, 50, 1, TEXT("1080p5000")),\r
+ DEFINE_VIDEOFORMATDESC(video_format::invalid ,0, 0, video_mode::invalid, 0, 1, TEXT("invalid"))\r
};\r
\r
const video_format_desc& video_format_desc::get(video_format::type format) \r
size_t height; // output frame height\r
video_mode::type mode; // progressive, interlaced upper field first, interlaced lower field first\r
double fps; // actual framerate, e.g. i50 = 25 fps, p50 = 50 fps\r
- double interval; // time between frames\r
+ size_t time_scale;\r
+ size_t duration;\r
+ size_t field_count;\r
size_t size; // output frame size in bytes \r
std::wstring name; // name of output format\r
\r
sync_timer_.restart();\r
unsigned long n_field = 0;\r
blue_->wait_output_video_synch(UPD_FMT_FRAME, n_field);\r
- graph_->update_value("sync-time", static_cast<float>(sync_timer_.elapsed()/format_desc_.interval*0.5));\r
+ graph_->update_value("sync-time", static_cast<float>(sync_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
if(embedded_audio_)\r
{ \r
\r
std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end());\r
\r
- graph_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()/format_desc_.interval*0.5));\r
+ graph_->update_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));\r
\r
- graph_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()/format_desc_.interval*0.5));\r
+ graph_->update_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));\r
tick_timer_.restart();\r
}\r
catch(...)\r
#include <common/exception/exceptions.h>\r
#include <common/memory/memcpy.h>\r
#include <common/memory/memclr.h>\r
-#include <common/utility/timer.h>\r
\r
#include <tbb/concurrent_queue.h>\r
\r
#include <boost/circular_buffer.hpp>\r
#include <boost/timer.hpp>\r
\r
-#include <array>\r
+#include <vector>\r
\r
#pragma warning(push)\r
#pragma warning(disable : 4996)\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
, 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
- , buffer_size_(5) // Minimum buffer-size (4 + 1 tolerance).\r
+ , buffer_size_(4) // Minimum buffer-size (3 + 1 tolerance).\r
{\r
is_running_ = true;\r
\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
- 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
+ enable_video(get_display_mode(output_, format_desc_.format, bmdFormat8BitBGRA, bmdVideoOutputFlagDefault));\r
+ \r
+ if(config.embedded_audio)\r
+ enable_audio();\r
\r
- BMDDisplayModeSupport displayModeSupport;\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
- else if(displayModeSupport == bmdDisplayModeSupportedWithConversion)\r
- CASPAR_LOG(warning) << print() << " Display mode is supported with conversion.";\r
+ set_latency(config.latency); \r
+ set_keyer(config.keyer);\r
+ \r
+ audio_container_.set_capacity(buffer_size_+1);\r
+ allocate_frames(buffer_size_+1);\r
+ \r
+ CASPAR_LOG(info) << print() << L" Buffer-depth: " << buffer_size_;\r
+ \r
+ for(size_t n = 0; n < buffer_size_; ++n)\r
+ schedule_next_video(core::read_frame::empty());\r
\r
+ video_frame_buffer_.set_capacity(2);\r
+ audio_frame_buffer_.set_capacity(2);\r
+ \r
if(config.embedded_audio)\r
+ output_->BeginAudioPreroll();\r
+ else\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
- \r
- if(FAILED(output_->SetAudioCallback(this)))\r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback."));\r
+ if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0))) \r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
+ }\r
+ \r
+ CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name; \r
+ }\r
\r
- CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
+ ~decklink_consumer()\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
- buffer_size_ = 6; // Minimum buffer-size with embedded-audio (5 + 1 tolerance).\r
+ if(output_ != nullptr) \r
+ {\r
+ output_->StopScheduledPlayback(0, nullptr, 0);\r
+ if(config_.embedded_audio)\r
+ output_->DisableAudioOutput();\r
+ output_->DisableVideoOutput();\r
}\r
-\r
- if(config.latency == normal_latency)\r
+ }\r
+ \r
+ void set_latency(latency latency)\r
+ { \r
+ if(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(latency == low_latency)\r
{ \r
configuration_->SetFlag(bmdDeckLinkConfigLowLatencyVideoOutput, true);\r
CASPAR_LOG(info) << print() << L" Enabled low-latency mode";\r
}\r
else\r
CASPAR_LOG(info) << print() << L" Uses driver latency settings."; \r
- \r
- if(config.keyer == internal_key) \r
+ }\r
+\r
+ void set_keyer(key keyer)\r
+ {\r
+ if(keyer == internal_key) \r
{\r
if(FAILED(keyer_->Enable(FALSE))) \r
CASPAR_LOG(error) << print() << L" Failed to enable internal keyer."; \r
else\r
CASPAR_LOG(info) << print() << L" Enabled internal keyer."; \r
}\r
- else if(config.keyer == external_key)\r
+ else if(keyer == external_key)\r
{\r
if(FAILED(keyer_->Enable(TRUE))) \r
CASPAR_LOG(error) << print() << L" Failed to enable external keyer."; \r
}\r
else\r
CASPAR_LOG(info) << print() << L" Uses driver keyer settings."; \r
+ }\r
+ \r
+ void enable_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
+ \r
+ if(FAILED(output_->SetAudioCallback(this)))\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not set audio callback."));\r
+\r
+ CASPAR_LOG(info) << print() << L" Enabled embedded-audio.";\r
+\r
+ buffer_size_ = 5; // Minimum buffer-size with embedded-audio (4 + 1 tolerance).\r
+ }\r
\r
- if(FAILED(output_->EnableVideoOutput(display_mode->GetDisplayMode(), bmdVideoOutputFlagDefault))) \r
+ void enable_video(BMDDisplayMode display_mode)\r
+ {\r
+ if(FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault))) \r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video output."));\r
\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 < buffer_size_+1; ++n)\r
- {\r
- CComPtr<IDeckLinkMutableVideoFrame> frame;\r
+ }\r
\r
- if(FAILED(output_->CreateVideoFrame(format_desc_.width, format_desc_.height, format_desc_.size/format_desc_.height, bmdFormat8BitBGRA, bmdFrameFlagDefault, &frame)))\r
+ void allocate_frames(size_t count)\r
+ {\r
+ std::pair<void*, CComPtr<IDeckLinkMutableVideoFrame>> frame;\r
+ std::generate_n(std::back_inserter(reserved_frames_), count, [&]() -> decltype(frame)\r
+ {\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
- void* bytes = nullptr;\r
-\r
- if(FAILED(frame->GetBytes(&bytes)) || bytes == nullptr)\r
+ if(FAILED(frame.second->GetBytes(&frame.first)) || frame.first == nullptr)\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to get frame bytes."));\r
\r
- reserved_frames_.push_back(std::make_pair(bytes, frame));\r
- }\r
- \r
- CASPAR_LOG(info) << print() << L" Buffer-depth: " << buffer_size_;\r
- \r
- for(size_t n = 0; n < buffer_size_; ++n)\r
- schedule_next_video(core::read_frame::empty());\r
-\r
- video_frame_buffer_.set_capacity(2);\r
- audio_frame_buffer_.set_capacity(2);\r
- \r
- if(config.embedded_audio)\r
- output_->BeginAudioPreroll();\r
- else\r
- {\r
- if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0))) \r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
- }\r
- \r
- CASPAR_LOG(info) << print() << L" Successfully initialized for " << format_desc_.name; \r
+ return frame;\r
+ });\r
}\r
\r
- ~decklink_consumer()\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
- if(output_ != nullptr) \r
- {\r
- output_->StopScheduledPlayback(0, nullptr, 0);\r
- if(config_.embedded_audio)\r
- output_->DisableAudioOutput();\r
- output_->DisableVideoOutput();\r
- }\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
+ STDMETHOD (QueryInterface(REFIID, LPVOID*)) {return E_NOINTERFACE;}\r
+ STDMETHOD_(ULONG, AddRef()) {return 1;}\r
+ STDMETHOD_(ULONG, Release()) {return 1;}\r
\r
- virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult result)\r
+ STDMETHOD(ScheduledFrameCompleted(IDeckLinkVideoFrame* /*completedFrame*/, BMDOutputFrameCompletionResult result))\r
{\r
if(!is_running_)\r
return E_FAIL;\r
return S_OK;\r
}\r
\r
- virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped (void)\r
+ STDMETHOD(ScheduledPlaybackHasStopped())\r
{\r
is_running_ = false;\r
CASPAR_LOG(info) << print() << L" Scheduled playback has stopped.";\r
return S_OK;\r
}\r
\r
- virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(BOOL preroll)\r
+ STDMETHOD(RenderAudioSamples(BOOL preroll))\r
{\r
if(!is_running_)\r
return E_FAIL;\r
{\r
if(preroll)\r
{\r
- if(FAILED(output_->StartScheduledPlayback(0, frame_time_scale_, 1.0)))\r
+ if(FAILED(output_->StartScheduledPlayback(0, format_desc_.time_scale, 1.0)))\r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Failed to schedule playback."));\r
}\r
\r
std::shared_ptr<const core::read_frame> frame;\r
audio_frame_buffer_.pop(frame);\r
- schedule_next_audio(safe_ptr<const core::read_frame>(frame));\r
- \r
+ schedule_next_audio(safe_ptr<const core::read_frame>(frame)); \r
}\r
catch(...)\r
{\r
else\r
fast_memclr(reserved_frames_.front().first, format_desc_.size);\r
\r
- if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * frame_duration_, frame_duration_, frame_time_scale_)))\r
+ if(FAILED(output_->ScheduleVideoFrame(reserved_frames_.front().second, (frames_scheduled_++) * format_desc_.duration, format_desc_.duration, format_desc_.time_scale)))\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>(tick_timer_.elapsed()/format_desc_.interval)*0.5f);\r
+ graph_->update_value("tick-time", tick_timer_.elapsed()*format_desc_.fps*0.5);\r
tick_timer_.restart();\r
}\r
\r
void send(const safe_ptr<const core::read_frame>& frame)\r
{\r
- if(!is_running_)\r
- return;\r
-\r
if(exception_ != nullptr)\r
std::rethrow_exception(exception_);\r
\r
+ if(!is_running_)\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Is not running."));\r
+\r
video_frame_buffer_.push(frame);\r
if(config_.embedded_audio)\r
- audio_frame_buffer_.push(frame);\r
- \r
+ audio_frame_buffer_.push(frame); \r
}\r
-\r
- size_t buffer_depth() const {return 1;}\r
-\r
+ \r
std::wstring print() const\r
{\r
return model_name_ + L" [" + boost::lexical_cast<std::wstring>(config_.device_index) + L"]";\r
{\r
return context_->print();\r
}\r
-\r
- virtual size_t buffer_depth() const \r
- {\r
- return context_->buffer_depth();\r
- }\r
}; \r
\r
safe_ptr<core::frame_consumer> create_decklink_consumer(const std::vector<std::wstring>& params) \r
return make_safe<decklink_consumer_proxy>(config);\r
}\r
\r
-}
\ No newline at end of file
+}\r
+\r
+/*\r
+##############################################################################\r
+Pre-rolling\r
+\r
+Mail: 2011-05-09\r
+\r
+Yoshan\r
+BMD Developer Support\r
+developer@blackmagic-design.com\r
+\r
+-----------------------------------------------------------------------------\r
+\r
+Thanks for your inquiry. The minimum number of frames that you can preroll \r
+for scheduled playback is three frames for video and four frames for audio. \r
+As you mentioned if you preroll less frames then playback will not start or\r
+playback will be very sporadic. From our experience with Media Express, we \r
+recommended that at least seven frames are prerolled for smooth playback. \r
+\r
+Regarding the bmdDeckLinkConfigLowLatencyVideoOutput flag:\r
+There can be around 3 frames worth of latency on scheduled output.\r
+When the bmdDeckLinkConfigLowLatencyVideoOutput flag is used this latency is\r
+reduced or removed for scheduled playback. If the DisplayVideoFrameSync() \r
+method is used, the bmdDeckLinkConfigLowLatencyVideoOutput setting will \r
+guarantee that the provided frame will be output as soon the previous \r
+frame output has been completed.\r
+################################################################################\r
+*/\r
+\r
+/*\r
+##############################################################################\r
+Async DMA Transfer without redundant copying\r
+\r
+Mail: 2011-05-10\r
+\r
+Yoshan\r
+BMD Developer Support\r
+developer@blackmagic-design.com\r
+\r
+-----------------------------------------------------------------------------\r
+\r
+Thanks for your inquiry. You could try subclassing IDeckLinkMutableVideoFrame \r
+and providing a pointer to your video buffer when GetBytes() is called. \r
+This may help to keep copying to a minimum. Please ensure that the pixel \r
+format is in bmdFormat10BitYUV, otherwise the DeckLink API / driver will \r
+have to colourspace convert which may result in additional copying.\r
+################################################################################\r
+*/
\ No newline at end of file
graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));\r
graph_->set_color("output-buffer", diagnostics::color(0.0f, 1.0f, 0.0f));\r
\r
- auto display_mode = get_display_mode(input_, format_desc_.format);\r
- if(!display_mode) \r
- BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Card does not support requested videoformat."));\r
+ auto display_mode = get_display_mode(input_, format_desc_.format, bmdFormat8BitYUV, bmdVideoInputFlagDefault);\r
\r
- // NOTE: For some reason the code below fails even for PAL.\r
- //BMDDisplayModeSupport displayModeSupport;\r
- //if(FAILED(input_->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
- //else if(displayModeSupport == bmdDisplayModeSupportedWithConversion)\r
- // CASPAR_LOG(warning) << print() << " Display mode is supported with conversion.";\r
-\r
// NOTE: bmdFormat8BitARGB is currently not supported by any decklink card. (2011-05-08)\r
- if(FAILED(input_->EnableVideoInput(display_mode->GetDisplayMode(), bmdFormat8BitYUV, 0))) \r
+ if(FAILED(input_->EnableVideoInput(display_mode, bmdFormat8BitYUV, 0))) \r
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(print()) + " Could not enable video input."));\r
\r
if(FAILED(input_->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2))) \r
{ \r
try\r
{\r
- graph_->update_value("tick-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));\r
+ graph_->update_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5);\r
perf_timer_.restart();\r
\r
core::pixel_format_desc desc;\r
\r
namespace caspar { \r
\r
+namespace internal {\r
+\r
static BMDDisplayMode get_decklink_video_format(core::video_format::type fmt) \r
{\r
switch(fmt)\r
default: return (BMDDisplayMode)ULONG_MAX;\r
}\r
}\r
- \r
-template<typename T>\r
-static CComPtr<IDeckLinkDisplayMode> get_display_mode(const T& device, BMDDisplayMode format)\r
+\r
+template<typename T, typename F>\r
+BMDDisplayMode get_display_mode(const T& device, BMDDisplayMode format, BMDPixelFormat pix_fmt, F flag)\r
{\r
CComPtr<IDeckLinkDisplayModeIterator> iterator;\r
CComPtr<IDeckLinkDisplayMode> mode;\r
\r
- if(FAILED(device->GetDisplayModeIterator(&iterator)))\r
- return nullptr;\r
-\r
- while(SUCCEEDED(iterator->Next(&mode)) && mode != nullptr)\r
- { \r
- if(mode->GetDisplayMode() == format)\r
- return mode;\r
+ if(SUCCEEDED(device->GetDisplayModeIterator(&iterator)))\r
+ {\r
+ while(SUCCEEDED(iterator->Next(&mode)) && \r
+ mode != nullptr && \r
+ mode->GetDisplayMode() != format){}\r
}\r
\r
- return nullptr;\r
+ if(!mode)\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Device does not support requested video-format."));\r
+ \r
+ BMDDisplayModeSupport displayModeSupport;\r
+ if(FAILED(device->DoesSupportVideoMode(mode->GetDisplayMode(), pix_fmt, flag, &displayModeSupport, nullptr)) || displayModeSupport == bmdDisplayModeNotSupported)\r
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Device does not support requested video-format."));\r
+ else if(displayModeSupport == bmdDisplayModeSupportedWithConversion)\r
+ CASPAR_LOG(warning) << L"Device supports video-format with conversion.";\r
+\r
+ return mode->GetDisplayMode();\r
}\r
\r
-template<typename T>\r
-static CComPtr<IDeckLinkDisplayMode> get_display_mode(const T& output, core::video_format::type fmt)\r
+} \r
+\r
+template<typename T, typename F>\r
+static BMDDisplayMode get_display_mode(const T& device, core::video_format::type fmt, BMDPixelFormat pix_fmt, F flag)\r
{ \r
- return get_display_mode(output, get_decklink_video_format(fmt));\r
+ return internal::get_display_mode(device, internal::get_decklink_video_format(fmt), pix_fmt, flag);\r
}\r
\r
template<typename T>\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
+ BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Decklink device not found.") << arg_name_info("device_index") << arg_value_info(boost::lexical_cast<std::string>(device_index)));\r
\r
return decklink;\r
}\r
return last_frame_; \r
}\r
\r
- graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()/frame_factory_->get_video_format_desc().interval*0.5));\r
+ graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()*frame_factory_->get_video_format_desc().fps*0.5));\r
\r
auto result = last_frame_;\r
if(!ouput_channel_.empty()) \r
{ \r
const auto& format_desc = frame_factory_->get_video_format_desc();\r
auto frame = core::basic_frame::empty();\r
- if(abs(context_->fps()/2.0 - format_desc.fps) < 0.1) // flash 50fps, format 50i\r
+\r
+ if(abs(context_->fps()/2.0 - format_desc.fps) < 0.1) // flash == 2 * format -> interlace\r
{\r
auto frame1 = context_->render_frame(frame_buffer_.size() < frame_buffer_.capacity());\r
auto frame2 = context_->render_frame(frame_buffer_.size() < frame_buffer_.capacity());\r
frame_buffer_.push(core::basic_frame::interlace(frame1, frame2, format_desc.mode));\r
frame = frame2;\r
}\r
- else if(abs(context_->fps()- format_desc.fps/2.0 ) < 0.1) // flash 25fps, format 50p\r
+ else if(abs(context_->fps()- format_desc.fps/2.0 ) < 0.1) // format == 2 * flash -> duplicate\r
{\r
frame = context_->render_frame(frame_buffer_.size() < frame_buffer_.capacity());\r
frame_buffer_.push(frame);\r
frame_buffer_.push(frame);\r
}\r
- else //if(abs(renderer_->fps() - format_desc_.fps) < 0.1) // flash 25fps, format 50i or flash 50fps, format 50p\r
+ else //if(abs(renderer_->fps() - format_desc_.fps) < 0.1) // format == flash -> simple\r
{\r
frame = context_->render_frame(frame_buffer_.size() < frame_buffer_.capacity());\r
frame_buffer_.push(frame);\r
data.Samples = container_.back().data();\r
data.NbSamples = container_.back().size(); \r
\r
- graph_->update_value("tick-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5)); \r
+ graph_->update_value("tick-time", perf_timer_.elapsed()*format_desc_.fps*0.5); \r
perf_timer_.restart();\r
\r
return is_running_;\r
}\r
render(frame);\r
window_.Display();\r
- graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()/format_desc_.interval*0.5));\r
+ graph_->update_value("frame-time", static_cast<float>(perf_timer_.elapsed()*format_desc_.fps*0.5));\r
});\r
}\r
\r
lbg.AddParameter(*it);\r
if(!lbg.Execute())\r
CASPAR_LOG(warning) << " Failed to play.";\r
+\r
+ CASPAR_LOG(info) << "Playing " << _parameters[0];\r
}\r
\r
GetChannel()->producer()->play(GetLayerIndex());\r
<channel>\r
<videomode>PAL</videomode>\r
<consumers>\r
- <!--<decklink>\r
+ <decklink>\r
<device>1</device>\r
<embedded-audio>true</embedded-audio>\r
<latency>low</latency>\r
<key>external</key>\r
</decklink>\r
- <ogl>\r
+ <!--<ogl>\r
<device>0</device>\r
<stretch>uniform</stretch>\r
<windowed>true</windowed>\r
</ogl>-->\r
<!--<audio/>-->\r
- <bluefish>\r
+ <!--<bluefish>\r
<device>1</device>\r
<embedded-audio>true</embedded-audio>\r
- </bluefish>\r
+ </bluefish>-->\r
</consumers>\r
</channel>\r
</channels>\r