videoIO seperation
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "../frames/InputFrameMailbox.h"
|
||||
#include "DeckLinkAPI_h.h"
|
||||
#include "DeckLinkDisplayMode.h"
|
||||
#include "VideoIOEdges.h"
|
||||
|
||||
#include <atlbase.h>
|
||||
|
||||
@@ -19,16 +20,7 @@ struct DeckLinkInputConfig
|
||||
VideoFormat videoFormat;
|
||||
};
|
||||
|
||||
struct DeckLinkInputMetrics
|
||||
{
|
||||
uint64_t capturedFrames = 0;
|
||||
uint64_t noInputSourceFrames = 0;
|
||||
uint64_t unsupportedFrames = 0;
|
||||
uint64_t submitMisses = 0;
|
||||
double convertMilliseconds = 0.0;
|
||||
double submitMilliseconds = 0.0;
|
||||
const char* captureFormat = "none";
|
||||
};
|
||||
using DeckLinkInputMetrics = VideoInputEdgeMetrics;
|
||||
|
||||
class DeckLinkInput;
|
||||
|
||||
@@ -48,7 +40,7 @@ private:
|
||||
std::atomic<ULONG> mRefCount{ 1 };
|
||||
};
|
||||
|
||||
class DeckLinkInput
|
||||
class DeckLinkInput : public IVideoInputEdge
|
||||
{
|
||||
public:
|
||||
DeckLinkInput(InputFrameMailbox& mailbox);
|
||||
@@ -57,14 +49,14 @@ public:
|
||||
~DeckLinkInput();
|
||||
|
||||
bool Initialize(const DeckLinkInputConfig& config, std::string& error);
|
||||
bool Start(std::string& error);
|
||||
void Stop();
|
||||
void ReleaseResources();
|
||||
bool Start(std::string& error) override;
|
||||
void Stop() override;
|
||||
void ReleaseResources() override;
|
||||
|
||||
bool IsInitialized() const { return mInput != nullptr; }
|
||||
bool IsRunning() const { return mRunning.load(std::memory_order_acquire); }
|
||||
VideoIOPixelFormat CapturePixelFormat() const;
|
||||
DeckLinkInputMetrics Metrics() const;
|
||||
bool IsInitialized() const override { return mInput != nullptr; }
|
||||
bool IsRunning() const override { return mRunning.load(std::memory_order_acquire); }
|
||||
VideoIOPixelFormat CapturePixelFormat() const override;
|
||||
DeckLinkInputMetrics Metrics() const override;
|
||||
|
||||
void HandleFrameArrived(IDeckLinkVideoInputFrame* inputFrame);
|
||||
void HandleFormatChanged();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "DeckLinkDisplayMode.h"
|
||||
#include "DeckLinkSession.h"
|
||||
#include "VideoIOEdges.h"
|
||||
#include "VideoIOTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
@@ -18,28 +19,12 @@ struct DeckLinkOutputConfig
|
||||
bool outputAlphaRequired = false;
|
||||
};
|
||||
|
||||
struct DeckLinkOutputMetrics
|
||||
{
|
||||
uint64_t completions = 0;
|
||||
uint64_t displayedLate = 0;
|
||||
uint64_t dropped = 0;
|
||||
uint64_t flushed = 0;
|
||||
uint64_t scheduleFailures = 0;
|
||||
bool actualBufferedFramesAvailable = false;
|
||||
uint64_t actualBufferedFrames = 0;
|
||||
double scheduleCallMilliseconds = 0.0;
|
||||
bool scheduleLeadAvailable = false;
|
||||
int64_t playbackStreamTime = 0;
|
||||
uint64_t playbackFrameIndex = 0;
|
||||
uint64_t nextScheduleFrameIndex = 0;
|
||||
int64_t scheduleLeadFrames = 0;
|
||||
uint64_t scheduleRealignmentCount = 0;
|
||||
};
|
||||
using DeckLinkOutputMetrics = VideoOutputEdgeMetrics;
|
||||
|
||||
class DeckLinkOutput
|
||||
class DeckLinkOutput : public IVideoOutputEdge
|
||||
{
|
||||
public:
|
||||
using CompletionCallback = std::function<void(const VideoIOCompletion&)>;
|
||||
using CompletionCallback = IVideoOutputEdge::CompletionCallback;
|
||||
|
||||
DeckLinkOutput() = default;
|
||||
DeckLinkOutput(const DeckLinkOutput&) = delete;
|
||||
@@ -47,13 +32,13 @@ public:
|
||||
~DeckLinkOutput();
|
||||
|
||||
bool Initialize(const DeckLinkOutputConfig& config, CompletionCallback completionCallback, std::string& error);
|
||||
bool StartScheduledPlayback(std::string& error);
|
||||
bool ScheduleFrame(const VideoIOOutputFrame& frame);
|
||||
void Stop();
|
||||
void ReleaseResources();
|
||||
bool StartScheduledPlayback(std::string& error) override;
|
||||
bool ScheduleFrame(const VideoIOOutputFrame& frame) override;
|
||||
void Stop() override;
|
||||
void ReleaseResources() override;
|
||||
|
||||
const VideoIOState& State() const;
|
||||
DeckLinkOutputMetrics Metrics() const;
|
||||
const VideoIOState& State() const override;
|
||||
DeckLinkOutputMetrics Metrics() const override;
|
||||
|
||||
private:
|
||||
void HandleCompletion(const VideoIOCompletion& completion);
|
||||
|
||||
@@ -1,127 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../frames/SystemFrameTypes.h"
|
||||
#include "DeckLinkOutput.h"
|
||||
#include "VideoIOTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include "VideoOutputThread.h"
|
||||
|
||||
namespace RenderCadenceCompositor
|
||||
{
|
||||
struct DeckLinkOutputThreadConfig
|
||||
{
|
||||
std::size_t targetBufferedFrames = 4;
|
||||
std::chrono::milliseconds idleSleep = std::chrono::milliseconds(1);
|
||||
};
|
||||
|
||||
struct DeckLinkOutputThreadMetrics
|
||||
{
|
||||
uint64_t scheduledFrames = 0;
|
||||
uint64_t completedPollMisses = 0;
|
||||
uint64_t scheduleFailures = 0;
|
||||
};
|
||||
using DeckLinkOutputThreadConfig = VideoOutputThreadConfig;
|
||||
using DeckLinkOutputThreadMetrics = VideoOutputThreadMetrics;
|
||||
|
||||
template <typename SystemFrameExchange>
|
||||
class DeckLinkOutputThread
|
||||
{
|
||||
public:
|
||||
DeckLinkOutputThread(DeckLinkOutput& output, SystemFrameExchange& exchange, DeckLinkOutputThreadConfig config = DeckLinkOutputThreadConfig()) :
|
||||
mOutput(output),
|
||||
mExchange(exchange),
|
||||
mConfig(config)
|
||||
{
|
||||
}
|
||||
|
||||
DeckLinkOutputThread(const DeckLinkOutputThread&) = delete;
|
||||
DeckLinkOutputThread& operator=(const DeckLinkOutputThread&) = delete;
|
||||
|
||||
~DeckLinkOutputThread()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool Start()
|
||||
{
|
||||
if (mRunning)
|
||||
return true;
|
||||
mStopping = false;
|
||||
mThread = std::thread([this]() { ThreadMain(); });
|
||||
mRunning = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
mStopping = true;
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
DeckLinkOutputThreadMetrics Metrics() const
|
||||
{
|
||||
DeckLinkOutputThreadMetrics metrics;
|
||||
metrics.scheduledFrames = mScheduledFrames.load();
|
||||
metrics.completedPollMisses = mCompletedPollMisses.load();
|
||||
metrics.scheduleFailures = mScheduleFailures.load();
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private:
|
||||
void ThreadMain()
|
||||
{
|
||||
while (!mStopping)
|
||||
{
|
||||
const auto exchangeMetrics = mExchange.Metrics();
|
||||
const auto outputMetrics = mOutput.Metrics();
|
||||
const std::size_t bufferedFrames = outputMetrics.actualBufferedFramesAvailable
|
||||
? static_cast<std::size_t>(outputMetrics.actualBufferedFrames)
|
||||
: exchangeMetrics.scheduledCount;
|
||||
if (bufferedFrames >= mConfig.targetBufferedFrames)
|
||||
{
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
SystemFrame frame;
|
||||
if (!mExchange.ConsumeCompletedForSchedule(frame))
|
||||
{
|
||||
++mCompletedPollMisses;
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
|
||||
VideoIOOutputFrame outputFrame;
|
||||
outputFrame.bytes = frame.bytes;
|
||||
outputFrame.nativeBuffer = frame.bytes;
|
||||
outputFrame.rowBytes = frame.rowBytes;
|
||||
outputFrame.width = frame.width;
|
||||
outputFrame.height = frame.height;
|
||||
outputFrame.pixelFormat = frame.pixelFormat;
|
||||
|
||||
if (!mOutput.ScheduleFrame(outputFrame))
|
||||
{
|
||||
++mScheduleFailures;
|
||||
mExchange.ReleaseScheduledByBytes(frame.bytes);
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
|
||||
++mScheduledFrames;
|
||||
}
|
||||
}
|
||||
|
||||
DeckLinkOutput& mOutput;
|
||||
SystemFrameExchange& mExchange;
|
||||
DeckLinkOutputThreadConfig mConfig;
|
||||
std::thread mThread;
|
||||
std::atomic<bool> mStopping{ false };
|
||||
std::atomic<bool> mRunning{ false };
|
||||
std::atomic<uint64_t> mScheduledFrames{ 0 };
|
||||
std::atomic<uint64_t> mCompletedPollMisses{ 0 };
|
||||
std::atomic<uint64_t> mScheduleFailures{ 0 };
|
||||
};
|
||||
using DeckLinkOutputThread = VideoOutputThread<SystemFrameExchange>;
|
||||
}
|
||||
|
||||
69
src/video/VideoIOEdges.h
Normal file
69
src/video/VideoIOEdges.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "VideoIOFormat.h"
|
||||
#include "VideoIOTypes.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace RenderCadenceCompositor
|
||||
{
|
||||
struct VideoInputEdgeMetrics
|
||||
{
|
||||
uint64_t capturedFrames = 0;
|
||||
uint64_t noInputSourceFrames = 0;
|
||||
uint64_t unsupportedFrames = 0;
|
||||
uint64_t submitMisses = 0;
|
||||
double convertMilliseconds = 0.0;
|
||||
double submitMilliseconds = 0.0;
|
||||
const char* captureFormat = "none";
|
||||
};
|
||||
|
||||
class IVideoInputEdge
|
||||
{
|
||||
public:
|
||||
virtual ~IVideoInputEdge() = default;
|
||||
|
||||
virtual bool Start(std::string& error) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void ReleaseResources() = 0;
|
||||
virtual bool IsInitialized() const = 0;
|
||||
virtual bool IsRunning() const = 0;
|
||||
virtual VideoIOPixelFormat CapturePixelFormat() const = 0;
|
||||
virtual VideoInputEdgeMetrics Metrics() const = 0;
|
||||
};
|
||||
|
||||
struct VideoOutputEdgeMetrics
|
||||
{
|
||||
uint64_t completions = 0;
|
||||
uint64_t displayedLate = 0;
|
||||
uint64_t dropped = 0;
|
||||
uint64_t flushed = 0;
|
||||
uint64_t scheduleFailures = 0;
|
||||
bool actualBufferedFramesAvailable = false;
|
||||
uint64_t actualBufferedFrames = 0;
|
||||
double scheduleCallMilliseconds = 0.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 IVideoOutputEdge
|
||||
{
|
||||
public:
|
||||
using CompletionCallback = std::function<void(const VideoIOCompletion&)>;
|
||||
|
||||
virtual ~IVideoOutputEdge() = default;
|
||||
|
||||
virtual bool StartScheduledPlayback(std::string& error) = 0;
|
||||
virtual bool ScheduleFrame(const VideoIOOutputFrame& frame) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void ReleaseResources() = 0;
|
||||
virtual const VideoIOState& State() const = 0;
|
||||
virtual VideoOutputEdgeMetrics Metrics() const = 0;
|
||||
};
|
||||
}
|
||||
127
src/video/VideoOutputThread.h
Normal file
127
src/video/VideoOutputThread.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "../frames/SystemFrameTypes.h"
|
||||
#include "VideoIOEdges.h"
|
||||
#include "VideoIOTypes.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
|
||||
namespace RenderCadenceCompositor
|
||||
{
|
||||
struct VideoOutputThreadConfig
|
||||
{
|
||||
std::size_t targetBufferedFrames = 4;
|
||||
std::chrono::milliseconds idleSleep = std::chrono::milliseconds(1);
|
||||
};
|
||||
|
||||
struct VideoOutputThreadMetrics
|
||||
{
|
||||
uint64_t scheduledFrames = 0;
|
||||
uint64_t completedPollMisses = 0;
|
||||
uint64_t scheduleFailures = 0;
|
||||
};
|
||||
|
||||
template <typename SystemFrameExchange>
|
||||
class VideoOutputThread
|
||||
{
|
||||
public:
|
||||
VideoOutputThread(IVideoOutputEdge& output, SystemFrameExchange& exchange, VideoOutputThreadConfig config = VideoOutputThreadConfig()) :
|
||||
mOutput(output),
|
||||
mExchange(exchange),
|
||||
mConfig(config)
|
||||
{
|
||||
}
|
||||
|
||||
VideoOutputThread(const VideoOutputThread&) = delete;
|
||||
VideoOutputThread& operator=(const VideoOutputThread&) = delete;
|
||||
|
||||
~VideoOutputThread()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool Start()
|
||||
{
|
||||
if (mRunning)
|
||||
return true;
|
||||
mStopping = false;
|
||||
mThread = std::thread([this]() { ThreadMain(); });
|
||||
mRunning = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
mStopping = true;
|
||||
if (mThread.joinable())
|
||||
mThread.join();
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
VideoOutputThreadMetrics Metrics() const
|
||||
{
|
||||
VideoOutputThreadMetrics metrics;
|
||||
metrics.scheduledFrames = mScheduledFrames.load();
|
||||
metrics.completedPollMisses = mCompletedPollMisses.load();
|
||||
metrics.scheduleFailures = mScheduleFailures.load();
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private:
|
||||
void ThreadMain()
|
||||
{
|
||||
while (!mStopping)
|
||||
{
|
||||
const auto exchangeMetrics = mExchange.Metrics();
|
||||
const auto outputMetrics = mOutput.Metrics();
|
||||
const std::size_t bufferedFrames = outputMetrics.actualBufferedFramesAvailable
|
||||
? static_cast<std::size_t>(outputMetrics.actualBufferedFrames)
|
||||
: exchangeMetrics.scheduledCount;
|
||||
if (bufferedFrames >= mConfig.targetBufferedFrames)
|
||||
{
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
SystemFrame frame;
|
||||
if (!mExchange.ConsumeCompletedForSchedule(frame))
|
||||
{
|
||||
++mCompletedPollMisses;
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
|
||||
VideoIOOutputFrame outputFrame;
|
||||
outputFrame.bytes = frame.bytes;
|
||||
outputFrame.nativeBuffer = frame.bytes;
|
||||
outputFrame.rowBytes = frame.rowBytes;
|
||||
outputFrame.width = frame.width;
|
||||
outputFrame.height = frame.height;
|
||||
outputFrame.pixelFormat = frame.pixelFormat;
|
||||
|
||||
if (!mOutput.ScheduleFrame(outputFrame))
|
||||
{
|
||||
++mScheduleFailures;
|
||||
mExchange.ReleaseScheduledByBytes(frame.bytes);
|
||||
std::this_thread::sleep_for(mConfig.idleSleep);
|
||||
continue;
|
||||
}
|
||||
|
||||
++mScheduledFrames;
|
||||
}
|
||||
}
|
||||
|
||||
IVideoOutputEdge& mOutput;
|
||||
SystemFrameExchange& mExchange;
|
||||
VideoOutputThreadConfig mConfig;
|
||||
std::thread mThread;
|
||||
std::atomic<bool> mStopping{ false };
|
||||
std::atomic<bool> mRunning{ false };
|
||||
std::atomic<uint64_t> mScheduledFrames{ 0 };
|
||||
std::atomic<uint64_t> mCompletedPollMisses{ 0 };
|
||||
std::atomic<uint64_t> mScheduleFailures{ 0 };
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user