stutter fix
This commit is contained in:
@@ -30,6 +30,37 @@ OpenGLDeckLinkBridge::OpenGLDeckLinkBridge(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLDeckLinkBridge::RecordFramePacing(BMDOutputFrameCompletionResult 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 == bmdOutputFrameDisplayedLate)
|
||||||
|
++mLateFrameCount;
|
||||||
|
else if (completionResult == bmdOutputFrameDropped)
|
||||||
|
++mDroppedFrameCount;
|
||||||
|
else if (completionResult == bmdOutputFrameFlushed)
|
||||||
|
++mFlushedFrameCount;
|
||||||
|
|
||||||
|
mRuntimeHost.SetFramePacingStats(
|
||||||
|
mCompletionIntervalMilliseconds,
|
||||||
|
mSmoothedCompletionIntervalMilliseconds,
|
||||||
|
mMaxCompletionIntervalMilliseconds,
|
||||||
|
mLateFrameCount,
|
||||||
|
mDroppedFrameCount,
|
||||||
|
mFlushedFrameCount);
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLDeckLinkBridge::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bool hasNoInputSource)
|
void OpenGLDeckLinkBridge::VideoFrameArrived(IDeckLinkVideoInputFrame* inputFrame, bool hasNoInputSource)
|
||||||
{
|
{
|
||||||
mDeckLink.SetInputSourceMissing(hasNoInputSource);
|
mDeckLink.SetInputSourceMissing(hasNoInputSource);
|
||||||
@@ -90,6 +121,8 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
{
|
{
|
||||||
(void)completedFrame;
|
(void)completedFrame;
|
||||||
|
|
||||||
|
RecordFramePacing(completionResult);
|
||||||
|
|
||||||
EnterCriticalSection(&mMutex);
|
EnterCriticalSection(&mMutex);
|
||||||
|
|
||||||
// Get the first frame from the queue
|
// Get the first frame from the queue
|
||||||
@@ -120,6 +153,7 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
IDeckLinkVideoBuffer* outputVideoFrameBuffer;
|
IDeckLinkVideoBuffer* outputVideoFrameBuffer;
|
||||||
if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
|
if (outputVideoFrame->QueryInterface(IID_IDeckLinkVideoBuffer, (void**)&outputVideoFrameBuffer) != S_OK)
|
||||||
{
|
{
|
||||||
|
wglMakeCurrent(NULL, NULL);
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -127,6 +161,7 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK)
|
if (outputVideoFrameBuffer->StartAccess(bmdBufferAccessWrite) != S_OK)
|
||||||
{
|
{
|
||||||
outputVideoFrameBuffer->Release();
|
outputVideoFrameBuffer->Release();
|
||||||
|
wglMakeCurrent(NULL, NULL);
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -136,10 +171,6 @@ void OpenGLDeckLinkBridge::PlayoutFrameCompleted(IDeckLinkVideoFrame* completedF
|
|||||||
|
|
||||||
if (mRenderer.FastTransferAvailable())
|
if (mRenderer.FastTransferAvailable())
|
||||||
{
|
{
|
||||||
// Finished sampling the capture texture for this frame.
|
|
||||||
if (mDeckLink.HasInputSource())
|
|
||||||
VideoFrameTransfer::endTextureInUse(VideoFrameTransfer::CPUtoGPU);
|
|
||||||
|
|
||||||
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
|
if (!mDeckLink.TransferPlayoutFrame(pFrame, mRenderer.OutputTexture()))
|
||||||
OutputDebugStringA("Playback: transferFrame() failed\n");
|
OutputDebugStringA("Playback: transferFrame() failed\n");
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
class DeckLinkSession;
|
class DeckLinkSession;
|
||||||
class OpenGLRenderer;
|
class OpenGLRenderer;
|
||||||
@@ -30,6 +32,8 @@ public:
|
|||||||
void PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult);
|
void PlayoutFrameCompleted(IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult completionResult);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void RecordFramePacing(BMDOutputFrameCompletionResult completionResult);
|
||||||
|
|
||||||
DeckLinkSession& mDeckLink;
|
DeckLinkSession& mDeckLink;
|
||||||
OpenGLRenderer& mRenderer;
|
OpenGLRenderer& mRenderer;
|
||||||
RuntimeHost& mRuntimeHost;
|
RuntimeHost& mRuntimeHost;
|
||||||
@@ -38,4 +42,11 @@ private:
|
|||||||
HGLRC mHglrc;
|
HGLRC mHglrc;
|
||||||
RenderEffectCallback mRenderEffect;
|
RenderEffectCallback mRenderEffect;
|
||||||
PaintCallback mPaint;
|
PaintCallback mPaint;
|
||||||
|
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
|
||||||
|
double mCompletionIntervalMilliseconds = 0.0;
|
||||||
|
double mSmoothedCompletionIntervalMilliseconds = 0.0;
|
||||||
|
double mMaxCompletionIntervalMilliseconds = 0.0;
|
||||||
|
uint64_t mLateFrameCount = 0;
|
||||||
|
uint64_t mDroppedFrameCount = 0;
|
||||||
|
uint64_t mFlushedFrameCount = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -680,6 +680,12 @@ RuntimeHost::RuntimeHost()
|
|||||||
mFrameBudgetMilliseconds(0.0),
|
mFrameBudgetMilliseconds(0.0),
|
||||||
mRenderMilliseconds(0.0),
|
mRenderMilliseconds(0.0),
|
||||||
mSmoothedRenderMilliseconds(0.0),
|
mSmoothedRenderMilliseconds(0.0),
|
||||||
|
mCompletionIntervalMilliseconds(0.0),
|
||||||
|
mSmoothedCompletionIntervalMilliseconds(0.0),
|
||||||
|
mMaxCompletionIntervalMilliseconds(0.0),
|
||||||
|
mLateFrameCount(0),
|
||||||
|
mDroppedFrameCount(0),
|
||||||
|
mFlushedFrameCount(0),
|
||||||
mServerPort(8080),
|
mServerPort(8080),
|
||||||
mAutoReloadEnabled(true),
|
mAutoReloadEnabled(true),
|
||||||
mStartTime(std::chrono::steady_clock::now()),
|
mStartTime(std::chrono::steady_clock::now()),
|
||||||
@@ -1179,6 +1185,18 @@ void RuntimeHost::SetPerformanceStats(double frameBudgetMilliseconds, double ren
|
|||||||
mSmoothedRenderMilliseconds = mSmoothedRenderMilliseconds * 0.9 + mRenderMilliseconds * 0.1;
|
mSmoothedRenderMilliseconds = mSmoothedRenderMilliseconds * 0.9 + mRenderMilliseconds * 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeHost::SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
mCompletionIntervalMilliseconds = std::max(completionIntervalMilliseconds, 0.0);
|
||||||
|
mSmoothedCompletionIntervalMilliseconds = std::max(smoothedCompletionIntervalMilliseconds, 0.0);
|
||||||
|
mMaxCompletionIntervalMilliseconds = std::max(maxCompletionIntervalMilliseconds, 0.0);
|
||||||
|
mLateFrameCount = lateFrameCount;
|
||||||
|
mDroppedFrameCount = droppedFrameCount;
|
||||||
|
mFlushedFrameCount = flushedFrameCount;
|
||||||
|
}
|
||||||
|
|
||||||
void RuntimeHost::AdvanceFrame()
|
void RuntimeHost::AdvanceFrame()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
@@ -1749,6 +1767,12 @@ JsonValue RuntimeHost::BuildStateValue() const
|
|||||||
performance.set("renderMs", JsonValue(mRenderMilliseconds));
|
performance.set("renderMs", JsonValue(mRenderMilliseconds));
|
||||||
performance.set("smoothedRenderMs", JsonValue(mSmoothedRenderMilliseconds));
|
performance.set("smoothedRenderMs", JsonValue(mSmoothedRenderMilliseconds));
|
||||||
performance.set("budgetUsedPercent", JsonValue(mFrameBudgetMilliseconds > 0.0 ? (mSmoothedRenderMilliseconds / mFrameBudgetMilliseconds) * 100.0 : 0.0));
|
performance.set("budgetUsedPercent", JsonValue(mFrameBudgetMilliseconds > 0.0 ? (mSmoothedRenderMilliseconds / mFrameBudgetMilliseconds) * 100.0 : 0.0));
|
||||||
|
performance.set("completionIntervalMs", JsonValue(mCompletionIntervalMilliseconds));
|
||||||
|
performance.set("smoothedCompletionIntervalMs", JsonValue(mSmoothedCompletionIntervalMilliseconds));
|
||||||
|
performance.set("maxCompletionIntervalMs", JsonValue(mMaxCompletionIntervalMilliseconds));
|
||||||
|
performance.set("lateFrameCount", JsonValue(static_cast<double>(mLateFrameCount)));
|
||||||
|
performance.set("droppedFrameCount", JsonValue(static_cast<double>(mDroppedFrameCount)));
|
||||||
|
performance.set("flushedFrameCount", JsonValue(static_cast<double>(mFlushedFrameCount)));
|
||||||
root.set("performance", performance);
|
root.set("performance", performance);
|
||||||
|
|
||||||
JsonValue shaderLibrary = JsonValue::MakeArray();
|
JsonValue shaderLibrary = JsonValue::MakeArray();
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ public:
|
|||||||
void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
void SetDeckLinkOutputStatus(const std::string& modelName, bool supportsInternalKeying, bool supportsExternalKeying,
|
||||||
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
|
bool keyerInterfaceAvailable, bool externalKeyingRequested, bool externalKeyingActive, const std::string& statusMessage);
|
||||||
void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
void SetPerformanceStats(double frameBudgetMilliseconds, double renderMilliseconds);
|
||||||
|
void SetFramePacingStats(double completionIntervalMilliseconds, double smoothedCompletionIntervalMilliseconds,
|
||||||
|
double maxCompletionIntervalMilliseconds, uint64_t lateFrameCount, uint64_t droppedFrameCount, uint64_t flushedFrameCount);
|
||||||
void AdvanceFrame();
|
void AdvanceFrame();
|
||||||
|
|
||||||
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
|
bool BuildLayerFragmentShaderSource(const std::string& layerId, std::string& fragmentShaderSource, std::string& error);
|
||||||
@@ -148,6 +150,12 @@ private:
|
|||||||
double mFrameBudgetMilliseconds;
|
double mFrameBudgetMilliseconds;
|
||||||
double mRenderMilliseconds;
|
double mRenderMilliseconds;
|
||||||
double mSmoothedRenderMilliseconds;
|
double mSmoothedRenderMilliseconds;
|
||||||
|
double mCompletionIntervalMilliseconds;
|
||||||
|
double mSmoothedCompletionIntervalMilliseconds;
|
||||||
|
double mMaxCompletionIntervalMilliseconds;
|
||||||
|
uint64_t mLateFrameCount;
|
||||||
|
uint64_t mDroppedFrameCount;
|
||||||
|
uint64_t mFlushedFrameCount;
|
||||||
DeckLinkOutputStatus mDeckLinkOutputStatus;
|
DeckLinkOutputStatus mDeckLinkOutputStatus;
|
||||||
unsigned short mServerPort;
|
unsigned short mServerPort;
|
||||||
bool mAutoReloadEnabled;
|
bool mAutoReloadEnabled;
|
||||||
|
|||||||
Reference in New Issue
Block a user