#pragma once #include "DeckLinkAPI_h.h" #include "DeckLinkDisplayMode.h" #include "DeckLinkFrameTransfer.h" #include "DeckLinkVideoIOFormat.h" #include "VideoIOFormat.h" #include "VideoIOTypes.h" #include "VideoPlayoutPolicy.h" #include "VideoPlayoutScheduler.h" #include #include #include #include #include #include class OpenGLComposite; struct DeckLinkSessionTelemetry { bool actualBufferedFramesAvailable = false; uint64_t actualBufferedFrames = 0; double scheduleCallMilliseconds = 0.0; uint64_t scheduleFailureCount = 0; bool scheduleLeadAvailable = false; int64_t playbackStreamTime = 0; uint64_t playbackFrameIndex = 0; uint64_t nextScheduleFrameIndex = 0; int64_t scheduleLeadFrames = 0; uint64_t scheduleRealignmentCount = 0; }; class DeckLinkSession { public: DeckLinkSession() = default; ~DeckLinkSession(); using OutputFrameCallback = std::function; void ReleaseResources(); bool DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error); bool SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error); bool ConfigureOutput(OutputFrameCallback callback, const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error); bool PrepareOutputSchedule(); bool StartScheduledPlayback(); bool Stop(); bool HasInputDevice() const { return mState.hasInputDevice; } bool HasInputSource() const { return mState.hasInputSource; } void SetInputSourceMissing(bool missing) { mState.hasInputSource = !missing; } bool InputOutputDimensionsDiffer() const { return mState.inputFrameSize != mState.outputFrameSize; } const FrameSize& InputFrameSize() const { return mState.inputFrameSize; } const FrameSize& OutputFrameSize() const { return mState.outputFrameSize; } unsigned InputFrameWidth() const { return mState.inputFrameSize.width; } unsigned InputFrameHeight() const { return mState.inputFrameSize.height; } unsigned OutputFrameWidth() const { return mState.outputFrameSize.width; } unsigned OutputFrameHeight() const { return mState.outputFrameSize.height; } VideoIOPixelFormat InputPixelFormat() const { return mState.inputPixelFormat; } VideoIOPixelFormat OutputPixelFormat() const { return mState.outputPixelFormat; } bool InputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.inputPixelFormat); } bool OutputIsTenBit() const { return VideoIOPixelFormatIsTenBit(mState.outputPixelFormat); } unsigned InputFrameRowBytes() const { return mState.inputFrameRowBytes; } unsigned OutputFrameRowBytes() const { return mState.outputFrameRowBytes; } unsigned CaptureTextureWidth() const { return mState.captureTextureWidth; } unsigned OutputPackTextureWidth() const { return mState.outputPackTextureWidth; } const std::string& FormatStatusMessage() const { return mState.formatStatusMessage; } const std::string& InputDisplayModeName() const { return mState.inputDisplayModeName; } const std::string& OutputModelName() const { return mState.outputModelName; } bool SupportsInternalKeying() const { return mState.supportsInternalKeying; } bool SupportsExternalKeying() const { return mState.supportsExternalKeying; } bool KeyerInterfaceAvailable() const { return mState.keyerInterfaceAvailable; } bool ExternalKeyingActive() const { return mState.externalKeyingActive; } const std::string& StatusMessage() const { return mState.statusMessage; } void SetStatusMessage(const std::string& message) { mState.statusMessage = message; } const VideoIOState& State() const { return mState; } VideoIOState& MutableState() { return mState; } const DeckLinkSessionTelemetry& Telemetry() const { return mTelemetry; } double FrameBudgetMilliseconds() const; VideoPlayoutRecoveryDecision AccountForCompletionResult(VideoIOCompletionResult completionResult, uint64_t readyQueueDepth); bool BeginOutputFrame(VideoIOOutputFrame& frame); void EndOutputFrame(VideoIOOutputFrame& frame); bool ScheduleOutputFrame(const VideoIOOutputFrame& frame); void HandlePlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult); private: bool AcquireNextOutputVideoFrame(CComPtr& outputVideoFrame); bool PopulateOutputFrame(IDeckLinkMutableVideoFrame* outputVideoFrame, VideoIOOutputFrame& frame); bool ScheduleFrame(IDeckLinkMutableVideoFrame* outputVideoFrame); void UpdateScheduleLeadTelemetry(); void MaybeRealignScheduleCursorForLowLead(); void RealignScheduleCursorToPlayback(); bool ScheduleSystemMemoryFrame(const VideoIOOutputFrame& frame); bool ScheduleBlackFrame(IDeckLinkMutableVideoFrame* outputVideoFrame); void RefreshBufferedVideoFrameCount(); static VideoIOCompletionResult TranslateCompletionResult(BMDOutputFrameCompletionResult completionResult); CComPtr playoutDelegate; CComPtr output; CComPtr keyer; std::deque> outputVideoFrameQueue; std::mutex mScheduledSystemFrameMutex; std::unordered_map mScheduledSystemFrameBuffers; VideoIOState mState; DeckLinkSessionTelemetry mTelemetry; VideoPlayoutPolicy mPlayoutPolicy; VideoPlayoutScheduler mScheduler; bool mScheduleRealignmentPending = false; bool mScheduleRealignmentArmed = true; bool mProactiveScheduleRealignmentArmed = true; OutputFrameCallback mOutputFrameCallback; };