+
+ int64_t get_age_millis() const
+ {
+ return frame_.get_age_millis();
+ }
+};
+
+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_, 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)
+ {
+ current_presentation_delay_ = 0;
+ scheduled_frames_completed_ = 0;
+
+ set_latency(configuration_, config.latency, print);
+ set_keyer(attributes_, keyer_, config.keyer, print);
+
+ if (FAILED(output_->SetScheduledFrameCompletionCallback(this)))
+ CASPAR_THROW_EXCEPTION(caspar_exception()
+ << msg_info(print + L" Failed to set key playback completion callback.")
+ << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
+ }
+
+ template<typename Print>
+ void enable_video(BMDDisplayMode display_mode, const Print& print)
+ {
+ if (FAILED(output_->EnableVideoOutput(display_mode, bmdVideoOutputFlagDefault)))
+ 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(print() + L" Failed to set key playback completion callback.")
+ << boost::errinfo_api_function("SetScheduledFrameCompletionCallback"));
+ }
+
+ virtual ~key_video_context()
+ {
+ if (output_)
+ {
+ output_->StopScheduledPlayback(0, nullptr, 0);
+ output_->DisableVideoOutput();
+ }
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) {return E_NOINTERFACE;}
+ virtual ULONG STDMETHODCALLTYPE AddRef() {return 1;}
+ virtual ULONG STDMETHODCALLTYPE Release() {return 1;}
+
+ virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
+ {
+ return S_OK;
+ }
+
+ virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(
+ IDeckLinkVideoFrame* completed_frame,
+ BMDOutputFrameCompletionResult result)
+ {
+ auto dframe = reinterpret_cast<decklink_frame*>(completed_frame);
+ current_presentation_delay_ = dframe->get_age_millis();
+ ++scheduled_frames_completed_;
+
+ // Let the fill callback keep the pace, so no scheduling here.
+
+ return S_OK;
+ }