239 lines
6.1 KiB
C++
239 lines
6.1 KiB
C++
#include "VideoBackend.h"
|
|
|
|
#include "DeckLinkSession.h"
|
|
#include "OpenGLVideoIOBridge.h"
|
|
#include "HealthTelemetry.h"
|
|
#include "RenderEngine.h"
|
|
|
|
#include <chrono>
|
|
|
|
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
|
|
mHealthTelemetry(healthTelemetry),
|
|
mVideoIODevice(std::make_unique<DeckLinkSession>()),
|
|
mBridge(std::make_unique<OpenGLVideoIOBridge>(renderEngine))
|
|
{
|
|
}
|
|
|
|
VideoBackend::~VideoBackend()
|
|
{
|
|
ReleaseResources();
|
|
}
|
|
|
|
void VideoBackend::ReleaseResources()
|
|
{
|
|
if (mVideoIODevice)
|
|
mVideoIODevice->ReleaseResources();
|
|
}
|
|
|
|
bool VideoBackend::DiscoverDevicesAndModes(const VideoFormatSelection& videoModes, std::string& error)
|
|
{
|
|
return mVideoIODevice->DiscoverDevicesAndModes(videoModes, error);
|
|
}
|
|
|
|
bool VideoBackend::SelectPreferredFormats(const VideoFormatSelection& videoModes, bool outputAlphaRequired, std::string& error)
|
|
{
|
|
return mVideoIODevice->SelectPreferredFormats(videoModes, outputAlphaRequired, error);
|
|
}
|
|
|
|
bool VideoBackend::ConfigureInput(const VideoFormat& inputVideoMode, std::string& error)
|
|
{
|
|
return mVideoIODevice->ConfigureInput(
|
|
[this](const VideoIOFrame& frame) { HandleInputFrame(frame); },
|
|
inputVideoMode,
|
|
error);
|
|
}
|
|
|
|
bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool externalKeyingEnabled, std::string& error)
|
|
{
|
|
return mVideoIODevice->ConfigureOutput(
|
|
[this](const VideoIOCompletion& completion) { HandleOutputFrameCompletion(completion); },
|
|
outputVideoMode,
|
|
externalKeyingEnabled,
|
|
error);
|
|
}
|
|
|
|
bool VideoBackend::Start()
|
|
{
|
|
return mVideoIODevice->Start();
|
|
}
|
|
|
|
bool VideoBackend::Stop()
|
|
{
|
|
return mVideoIODevice->Stop();
|
|
}
|
|
|
|
const VideoIOState& VideoBackend::State() const
|
|
{
|
|
return mVideoIODevice->State();
|
|
}
|
|
|
|
VideoIOState& VideoBackend::MutableState()
|
|
{
|
|
return mVideoIODevice->MutableState();
|
|
}
|
|
|
|
bool VideoBackend::BeginOutputFrame(VideoIOOutputFrame& frame)
|
|
{
|
|
return mVideoIODevice->BeginOutputFrame(frame);
|
|
}
|
|
|
|
void VideoBackend::EndOutputFrame(VideoIOOutputFrame& frame)
|
|
{
|
|
mVideoIODevice->EndOutputFrame(frame);
|
|
}
|
|
|
|
bool VideoBackend::ScheduleOutputFrame(const VideoIOOutputFrame& frame)
|
|
{
|
|
return mVideoIODevice->ScheduleOutputFrame(frame);
|
|
}
|
|
|
|
void VideoBackend::AccountForCompletionResult(VideoIOCompletionResult result)
|
|
{
|
|
mVideoIODevice->AccountForCompletionResult(result);
|
|
}
|
|
|
|
bool VideoBackend::HasInputDevice() const
|
|
{
|
|
return mVideoIODevice->HasInputDevice();
|
|
}
|
|
|
|
bool VideoBackend::HasInputSource() const
|
|
{
|
|
return mVideoIODevice->HasInputSource();
|
|
}
|
|
|
|
unsigned VideoBackend::InputFrameWidth() const
|
|
{
|
|
return mVideoIODevice->InputFrameWidth();
|
|
}
|
|
|
|
unsigned VideoBackend::InputFrameHeight() const
|
|
{
|
|
return mVideoIODevice->InputFrameHeight();
|
|
}
|
|
|
|
unsigned VideoBackend::OutputFrameWidth() const
|
|
{
|
|
return mVideoIODevice->OutputFrameWidth();
|
|
}
|
|
|
|
unsigned VideoBackend::OutputFrameHeight() const
|
|
{
|
|
return mVideoIODevice->OutputFrameHeight();
|
|
}
|
|
|
|
unsigned VideoBackend::CaptureTextureWidth() const
|
|
{
|
|
return mVideoIODevice->CaptureTextureWidth();
|
|
}
|
|
|
|
unsigned VideoBackend::OutputPackTextureWidth() const
|
|
{
|
|
return mVideoIODevice->OutputPackTextureWidth();
|
|
}
|
|
|
|
VideoIOPixelFormat VideoBackend::InputPixelFormat() const
|
|
{
|
|
return mVideoIODevice->InputPixelFormat();
|
|
}
|
|
|
|
const std::string& VideoBackend::InputDisplayModeName() const
|
|
{
|
|
return mVideoIODevice->InputDisplayModeName();
|
|
}
|
|
|
|
const std::string& VideoBackend::OutputModelName() const
|
|
{
|
|
return mVideoIODevice->OutputModelName();
|
|
}
|
|
|
|
bool VideoBackend::SupportsInternalKeying() const
|
|
{
|
|
return mVideoIODevice->SupportsInternalKeying();
|
|
}
|
|
|
|
bool VideoBackend::SupportsExternalKeying() const
|
|
{
|
|
return mVideoIODevice->SupportsExternalKeying();
|
|
}
|
|
|
|
bool VideoBackend::KeyerInterfaceAvailable() const
|
|
{
|
|
return mVideoIODevice->KeyerInterfaceAvailable();
|
|
}
|
|
|
|
bool VideoBackend::ExternalKeyingActive() const
|
|
{
|
|
return mVideoIODevice->ExternalKeyingActive();
|
|
}
|
|
|
|
const std::string& VideoBackend::StatusMessage() const
|
|
{
|
|
return mVideoIODevice->StatusMessage();
|
|
}
|
|
|
|
void VideoBackend::SetStatusMessage(const std::string& message)
|
|
{
|
|
mVideoIODevice->SetStatusMessage(message);
|
|
}
|
|
|
|
void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
|
{
|
|
const VideoIOState& state = mVideoIODevice->State();
|
|
mHealthTelemetry.TryReportSignalStatus(!frame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
|
|
|
if (mBridge)
|
|
mBridge->UploadInputFrame(frame, state);
|
|
}
|
|
|
|
void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completion)
|
|
{
|
|
RecordFramePacing(completion.result);
|
|
|
|
VideoIOOutputFrame outputFrame;
|
|
if (!BeginOutputFrame(outputFrame))
|
|
return;
|
|
|
|
const VideoIOState& state = mVideoIODevice->State();
|
|
if (mBridge)
|
|
mBridge->RenderScheduledFrame(state, completion, outputFrame);
|
|
|
|
EndOutputFrame(outputFrame);
|
|
AccountForCompletionResult(completion.result);
|
|
|
|
// Schedule the next frame after render work is complete so device-side
|
|
// bookkeeping stays with the backend seam and the bridge stays render-only.
|
|
ScheduleOutputFrame(outputFrame);
|
|
}
|
|
|
|
void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
|
{
|
|
const auto now = std::chrono::steady_clock::now();
|
|
if (mLastPlayoutCompletionTime != std::chrono::steady_clock::time_point())
|
|
{
|
|
mCompletionIntervalMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(now - mLastPlayoutCompletionTime).count();
|
|
if (mSmoothedCompletionIntervalMilliseconds <= 0.0)
|
|
mSmoothedCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
|
|
else
|
|
mSmoothedCompletionIntervalMilliseconds = mSmoothedCompletionIntervalMilliseconds * 0.9 + mCompletionIntervalMilliseconds * 0.1;
|
|
if (mCompletionIntervalMilliseconds > mMaxCompletionIntervalMilliseconds)
|
|
mMaxCompletionIntervalMilliseconds = mCompletionIntervalMilliseconds;
|
|
}
|
|
mLastPlayoutCompletionTime = now;
|
|
|
|
if (completionResult == VideoIOCompletionResult::DisplayedLate)
|
|
++mLateFrameCount;
|
|
else if (completionResult == VideoIOCompletionResult::Dropped)
|
|
++mDroppedFrameCount;
|
|
else if (completionResult == VideoIOCompletionResult::Flushed)
|
|
++mFlushedFrameCount;
|
|
|
|
mHealthTelemetry.TryRecordFramePacingStats(
|
|
mCompletionIntervalMilliseconds,
|
|
mSmoothedCompletionIntervalMilliseconds,
|
|
mMaxCompletionIntervalMilliseconds,
|
|
mLateFrameCount,
|
|
mDroppedFrameCount,
|
|
mFlushedFrameCount);
|
|
}
|