Tests
This commit is contained in:
@@ -4,11 +4,14 @@
|
||||
#include "OpenGLVideoIOBridge.h"
|
||||
#include "HealthTelemetry.h"
|
||||
#include "RenderEngine.h"
|
||||
#include "RuntimeEventDispatcher.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <windows.h>
|
||||
|
||||
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
|
||||
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry, RuntimeEventDispatcher& runtimeEventDispatcher) :
|
||||
mHealthTelemetry(healthTelemetry),
|
||||
mRuntimeEventDispatcher(runtimeEventDispatcher),
|
||||
mVideoIODevice(std::make_unique<DeckLinkSession>()),
|
||||
mBridge(std::make_unique<OpenGLVideoIOBridge>(renderEngine))
|
||||
{
|
||||
@@ -54,12 +57,16 @@ bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool exte
|
||||
|
||||
bool VideoBackend::Start()
|
||||
{
|
||||
return mVideoIODevice->Start();
|
||||
const bool started = mVideoIODevice->Start();
|
||||
PublishBackendStateChanged(started ? "started" : "start-failed", started ? "Video backend started." : StatusMessage());
|
||||
return started;
|
||||
}
|
||||
|
||||
bool VideoBackend::Stop()
|
||||
{
|
||||
return mVideoIODevice->Stop();
|
||||
const bool stopped = mVideoIODevice->Stop();
|
||||
PublishBackendStateChanged(stopped ? "stopped" : "stop-failed", stopped ? "Video backend stopped." : StatusMessage());
|
||||
return stopped;
|
||||
}
|
||||
|
||||
const VideoIOState& VideoBackend::State() const
|
||||
@@ -191,6 +198,7 @@ void VideoBackend::PublishStatus(bool externalKeyingConfigured, const std::strin
|
||||
externalKeyingConfigured,
|
||||
ExternalKeyingActive(),
|
||||
StatusMessage());
|
||||
PublishBackendStateChanged("status", StatusMessage());
|
||||
}
|
||||
|
||||
void VideoBackend::ReportNoInputDeviceSignalStatus()
|
||||
@@ -200,12 +208,15 @@ void VideoBackend::ReportNoInputDeviceSignalStatus()
|
||||
InputFrameWidth(),
|
||||
InputFrameHeight(),
|
||||
InputDisplayModeName());
|
||||
PublishBackendStateChanged("no-input-device", "No input device is available.");
|
||||
}
|
||||
|
||||
void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
||||
{
|
||||
const VideoIOState& state = mVideoIODevice->State();
|
||||
mHealthTelemetry.TryReportSignalStatus(!frame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
||||
PublishInputSignalChanged(frame, state);
|
||||
PublishInputFrameArrived(frame);
|
||||
|
||||
if (mBridge)
|
||||
mBridge->UploadInputFrame(frame, state);
|
||||
@@ -214,6 +225,7 @@ void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
||||
void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completion)
|
||||
{
|
||||
RecordFramePacing(completion.result);
|
||||
PublishOutputFrameCompleted(completion);
|
||||
|
||||
VideoIOOutputFrame outputFrame;
|
||||
if (!BeginOutputFrame(outputFrame))
|
||||
@@ -228,7 +240,8 @@ void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completi
|
||||
|
||||
// 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);
|
||||
if (ScheduleOutputFrame(outputFrame))
|
||||
PublishOutputFrameScheduled(outputFrame);
|
||||
}
|
||||
|
||||
void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
||||
@@ -260,4 +273,152 @@ void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
||||
mLateFrameCount,
|
||||
mDroppedFrameCount,
|
||||
mFlushedFrameCount);
|
||||
PublishTimingSample("VideoBackend", "completionInterval", mCompletionIntervalMilliseconds, "ms");
|
||||
PublishTimingSample("VideoBackend", "smoothedCompletionInterval", mSmoothedCompletionIntervalMilliseconds, "ms");
|
||||
}
|
||||
|
||||
void VideoBackend::PublishBackendStateChanged(const std::string& state, const std::string& message)
|
||||
{
|
||||
try
|
||||
{
|
||||
BackendStateChangedEvent event;
|
||||
event.backendName = "decklink";
|
||||
event.state = state;
|
||||
event.message = message;
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||
OutputDebugStringA("BackendStateChanged event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("BackendStateChanged event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackend::PublishInputSignalChanged(const VideoIOFrame& frame, const VideoIOState& state)
|
||||
{
|
||||
const bool hasSignal = !frame.hasNoInputSource;
|
||||
const unsigned width = state.inputFrameSize.width;
|
||||
const unsigned height = state.inputFrameSize.height;
|
||||
if (mHasLastInputSignal &&
|
||||
mLastInputSignal == hasSignal &&
|
||||
mLastInputSignalWidth == width &&
|
||||
mLastInputSignalHeight == height &&
|
||||
mLastInputSignalModeName == state.inputDisplayModeName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mHasLastInputSignal = true;
|
||||
mLastInputSignal = hasSignal;
|
||||
mLastInputSignalWidth = width;
|
||||
mLastInputSignalHeight = height;
|
||||
mLastInputSignalModeName = state.inputDisplayModeName;
|
||||
|
||||
try
|
||||
{
|
||||
InputSignalChangedEvent event;
|
||||
event.hasSignal = hasSignal;
|
||||
event.width = width;
|
||||
event.height = height;
|
||||
event.modeName = state.inputDisplayModeName;
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||
OutputDebugStringA("InputSignalChanged event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("InputSignalChanged event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackend::PublishInputFrameArrived(const VideoIOFrame& frame)
|
||||
{
|
||||
try
|
||||
{
|
||||
InputFrameArrivedEvent event;
|
||||
event.frameIndex = ++mInputFrameIndex;
|
||||
event.width = frame.width;
|
||||
event.height = frame.height;
|
||||
event.rowBytes = frame.rowBytes;
|
||||
event.pixelFormat = PixelFormatName(frame.pixelFormat);
|
||||
event.hasNoInputSource = frame.hasNoInputSource;
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||
OutputDebugStringA("InputFrameArrived event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("InputFrameArrived event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackend::PublishOutputFrameScheduled(const VideoIOOutputFrame& frame)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputFrameScheduledEvent event;
|
||||
event.frameIndex = ++mOutputFrameScheduleIndex;
|
||||
(void)frame;
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||
OutputDebugStringA("OutputFrameScheduled event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("OutputFrameScheduled event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackend::PublishOutputFrameCompleted(const VideoIOCompletion& completion)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputFrameCompletedEvent event;
|
||||
event.frameIndex = ++mOutputFrameCompletionIndex;
|
||||
event.result = CompletionResultName(completion.result);
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||
OutputDebugStringA("OutputFrameCompleted event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("OutputFrameCompleted event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackend::PublishTimingSample(const std::string& subsystem, const std::string& metric, double value, const std::string& unit)
|
||||
{
|
||||
try
|
||||
{
|
||||
TimingSampleRecordedEvent event;
|
||||
event.subsystem = subsystem;
|
||||
event.metric = metric;
|
||||
event.value = value;
|
||||
event.unit = unit;
|
||||
if (!mRuntimeEventDispatcher.PublishPayload(event, "HealthTelemetry"))
|
||||
OutputDebugStringA("TimingSampleRecorded event publish failed.\n");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("TimingSampleRecorded event publish threw.\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::string VideoBackend::CompletionResultName(VideoIOCompletionResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case VideoIOCompletionResult::Completed:
|
||||
return "Completed";
|
||||
case VideoIOCompletionResult::DisplayedLate:
|
||||
return "DisplayedLate";
|
||||
case VideoIOCompletionResult::Dropped:
|
||||
return "Dropped";
|
||||
case VideoIOCompletionResult::Flushed:
|
||||
return "Flushed";
|
||||
case VideoIOCompletionResult::Unknown:
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string VideoBackend::PixelFormatName(VideoIOPixelFormat pixelFormat)
|
||||
{
|
||||
return std::string(VideoIOPixelFormatName(pixelFormat));
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
class HealthTelemetry;
|
||||
class OpenGLVideoIOBridge;
|
||||
class RenderEngine;
|
||||
class RuntimeEventDispatcher;
|
||||
class VideoIODevice;
|
||||
|
||||
class VideoBackend
|
||||
{
|
||||
public:
|
||||
VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry);
|
||||
VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry, RuntimeEventDispatcher& runtimeEventDispatcher);
|
||||
~VideoBackend();
|
||||
|
||||
void ReleaseResources();
|
||||
@@ -57,10 +58,27 @@ private:
|
||||
void HandleInputFrame(const VideoIOFrame& frame);
|
||||
void HandleOutputFrameCompletion(const VideoIOCompletion& completion);
|
||||
void RecordFramePacing(VideoIOCompletionResult completionResult);
|
||||
void PublishBackendStateChanged(const std::string& state, const std::string& message);
|
||||
void PublishInputSignalChanged(const VideoIOFrame& frame, const VideoIOState& state);
|
||||
void PublishInputFrameArrived(const VideoIOFrame& frame);
|
||||
void PublishOutputFrameScheduled(const VideoIOOutputFrame& frame);
|
||||
void PublishOutputFrameCompleted(const VideoIOCompletion& completion);
|
||||
void PublishTimingSample(const std::string& subsystem, const std::string& metric, double value, const std::string& unit);
|
||||
static std::string CompletionResultName(VideoIOCompletionResult result);
|
||||
static std::string PixelFormatName(VideoIOPixelFormat pixelFormat);
|
||||
|
||||
HealthTelemetry& mHealthTelemetry;
|
||||
RuntimeEventDispatcher& mRuntimeEventDispatcher;
|
||||
std::unique_ptr<VideoIODevice> mVideoIODevice;
|
||||
std::unique_ptr<OpenGLVideoIOBridge> mBridge;
|
||||
uint64_t mInputFrameIndex = 0;
|
||||
uint64_t mOutputFrameScheduleIndex = 0;
|
||||
uint64_t mOutputFrameCompletionIndex = 0;
|
||||
bool mHasLastInputSignal = false;
|
||||
bool mLastInputSignal = false;
|
||||
unsigned mLastInputSignalWidth = 0;
|
||||
unsigned mLastInputSignalHeight = 0;
|
||||
std::string mLastInputSignalModeName;
|
||||
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
|
||||
double mCompletionIntervalMilliseconds = 0.0;
|
||||
double mSmoothedCompletionIntervalMilliseconds = 0.0;
|
||||
|
||||
Reference in New Issue
Block a user