Let Pacer know what the display Hz and stream FPS are so it can make better drop decisions

This commit is contained in:
Cameron Gutman 2018-08-15 21:10:35 -07:00
parent f171588616
commit f929cffce7
3 changed files with 59 additions and 15 deletions

View File

@ -3,7 +3,9 @@
#define FRAME_HISTORY_ENTRIES 8 #define FRAME_HISTORY_ENTRIES 8
Pacer::Pacer() : Pacer::Pacer() :
m_FrameQueueLock(0) m_FrameQueueLock(0),
m_MaxVideoFps(0),
m_DisplayFps(0)
{ {
} }
@ -13,28 +15,60 @@ Pacer::~Pacer()
drain(); drain();
} }
void Pacer::initialize(SDL_Window* window, int maxVideoFps)
{
m_MaxVideoFps = maxVideoFps;
int displayIndex = SDL_GetWindowDisplayIndex(window);
if (displayIndex < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to get current display: %s",
SDL_GetError());
// Assume display 0 if it fails
displayIndex = 0;
}
SDL_DisplayMode mode;
if (SDL_GetCurrentDisplayMode(displayIndex, &mode) == 0) {
// May be zero if undefined
m_DisplayFps = mode.refresh_rate;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Frame pacing: target %d Hz with %d FPS stream",
m_DisplayFps, m_MaxVideoFps);
}
AVFrame* Pacer::getFrameAtVsync() AVFrame* Pacer::getFrameAtVsync()
{ {
SDL_AtomicLock(&m_FrameQueueLock); // Make sure initialize() has been called
SDL_assert(m_MaxVideoFps != 0);
int frameDropTarget; SDL_AtomicLock(&m_FrameQueueLock);
// If the queue length history entries are large, be strict // If the queue length history entries are large, be strict
// about dropping excess frames. // about dropping excess frames.
frameDropTarget = 1; int frameDropTarget = 1;
for (int i = 0; i < m_FrameQueueHistory.count(); i++) {
if (m_FrameQueueHistory[i] <= 1) { // If we may get more frames per second than we can display, use
// Be lenient as long as the queue length // frame history to drop frames only if consistently above the
// resolves before the end of frame history // one queued frame mark.
frameDropTarget = 3; if (m_MaxVideoFps >= m_DisplayFps) {
for (int i = 0; i < m_FrameQueueHistory.count(); i++) {
if (m_FrameQueueHistory[i] <= 1) {
// Be lenient as long as the queue length
// resolves before the end of frame history
frameDropTarget = 3;
}
} }
}
if (m_FrameQueueHistory.count() == FRAME_HISTORY_ENTRIES) { if (m_FrameQueueHistory.count() == FRAME_HISTORY_ENTRIES) {
m_FrameQueueHistory.dequeue(); m_FrameQueueHistory.dequeue();
} }
m_FrameQueueHistory.enqueue(m_FrameQueue.count()); m_FrameQueueHistory.enqueue(m_FrameQueue.count());
}
// Catch up if we're several frames ahead // Catch up if we're several frames ahead
while (m_FrameQueue.count() > frameDropTarget) { while (m_FrameQueue.count() > frameDropTarget) {
@ -56,6 +90,9 @@ AVFrame* Pacer::getFrameAtVsync()
void Pacer::submitFrame(AVFrame* frame) void Pacer::submitFrame(AVFrame* frame)
{ {
// Make sure initialize() has been called
SDL_assert(m_MaxVideoFps != 0);
SDL_AtomicLock(&m_FrameQueueLock); SDL_AtomicLock(&m_FrameQueueLock);
m_FrameQueue.enqueue(frame); m_FrameQueue.enqueue(frame);
SDL_AtomicUnlock(&m_FrameQueueLock); SDL_AtomicUnlock(&m_FrameQueueLock);

View File

@ -15,10 +15,15 @@ public:
void submitFrame(AVFrame* frame); void submitFrame(AVFrame* frame);
void initialize(SDL_Window* window, int maxVideoFps);
void drain(); void drain();
private: private:
QQueue<AVFrame*> m_FrameQueue; QQueue<AVFrame*> m_FrameQueue;
QQueue<int> m_FrameQueueHistory; QQueue<int> m_FrameQueueHistory;
SDL_SpinLock m_FrameQueueLock; SDL_SpinLock m_FrameQueueLock;
int m_MaxVideoFps;
int m_DisplayFps;
}; };

View File

@ -131,10 +131,12 @@ public:
int videoFormat, int videoFormat,
int, int,
int, int,
int) override int maxFps) override
{ {
int err; int err;
m_Pacer.initialize(window, maxFps);
if (videoFormat & VIDEO_FORMAT_MASK_H264) { if (videoFormat & VIDEO_FORMAT_MASK_H264) {
// Prior to 10.13, we'll just assume everything has // Prior to 10.13, we'll just assume everything has
// H.264 support and fail open to allow VT decode. // H.264 support and fail open to allow VT decode.