Skip to content

Commit fbbff33

Browse files
committed
Fixes for correctly setting encoder frame rate for various capture modes
1 parent 654f322 commit fbbff33

File tree

10 files changed

+70
-23
lines changed

10 files changed

+70
-23
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Advanced Interactive Streaming SDK is a C++ library that provides building block
44
Virtual and Augmented Reality, etc. using AMD Radeon graphics. It allows you to build a complete streaming solution including video and audio capture, video and audio encoder/decoder/pre-post-processing pipelines, a robust and secure network stack, or use some of its components, while implementing the rest yourself.
55

66
## Changelog:
7+
- v1.0.2 - bug fixes:
8+
- Encoder frame rate wasn't being set correctly when display capture mode was set to *present* or when QoS was turned off
79
- v1.0.1 - bug fixes:
810
- System mouse cursor doesn't get hidden correctly in some games
911
- Deadlock on server exit when streaming at high (8K) resolution

samples/RemoteDesktopServer/AVStreamer.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -464,16 +464,6 @@ void AVStreamer::OnFramerateChangeByQoS(float framerate, ssdk::transport_common:
464464
{
465465
AMFTraceError(AMF_FACILITY, L"QoS failed to set video capture frame rate for stream %lld to %5.2f, result=%s", streamID, framerate, amf::AMFGetResultText(result));
466466
}
467-
468-
result = videoOutput->SetFramerate(framerate);
469-
if (result == AMF_OK)
470-
{
471-
AMFTraceDebug(AMF_FACILITY, L"Encoder frame rate for stream %lld has been changed to %5.2f by QoS", streamID, framerate);
472-
}
473-
else
474-
{
475-
AMFTraceError(AMF_FACILITY, L"QoS failed to set encoder frame rate for stream %lld to %5.2f, result=%s", streamID, framerate, amf::AMFGetResultText(result));
476-
}
477467
}
478468
}
479469

samples/RemoteDesktopServer/RemoteDesktopServer.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ bool RemoteDesktopServer::InitVideoCapture()
482482
break;
483483
case AMF_NOT_FOUND:
484484
case AMF_ACCESS_DENIED:
485-
AMFTraceWarning(AMF_FACILITY, L"Selected video capture method does not support capture at a fixed frame rate, -%s %S ignored", PARAM_NAME_CAPTURE_MODE, CAPTURE_MODE_FRAMERATE);
485+
AMFTraceWarning(AMF_FACILITY, L"Selected video capture method does not support capture at a fixed frame rate, -%s %s ignored", PARAM_NAME_CAPTURE_MODE, CAPTURE_MODE_FRAMERATE);
486486
result = true;
487487
break;
488488
default:
@@ -493,20 +493,22 @@ bool RemoteDesktopServer::InitVideoCapture()
493493
else if (captureMode == CAPTURE_MODE_PRESENT)
494494
{
495495
amfResult = m_VideoCapture->SetProperty(AMF_DISPLAYCAPTURE_MODE, amf_int64(AMF_DISPLAYCAPTURE_MODE_ENUM::AMF_DISPLAYCAPTURE_MODE_WAIT_FOR_PRESENT));
496+
m_VideoStreamDescriptor.SetFramerate(60.0); // Set the initial frame rate to 60fps, it will be adjusted automatically in a few seconds to the actual frame rate determined by capture
496497
}
497498
else if (captureMode == CAPTURE_MODE_ASAP)
498499
{
499500
amfResult = m_VideoCapture->SetProperty(AMF_DISPLAYCAPTURE_MODE, amf_int64(AMF_DISPLAYCAPTURE_MODE_ENUM::AMF_DISPLAYCAPTURE_MODE_GET_CURRENT_SURFACE));
501+
m_VideoStreamDescriptor.SetFramerate(60.0); // Set the initial frame rate to 60fps, it will be adjusted automatically in a few seconds to the actual frame rate determined by capture
500502
}
501503
if (result != true)
502504
{
503505
if (amfResult == AMF_OK)
504506
{
505-
AMFTraceInfo(AMF_FACILITY, L"Capture mode is set to %S", captureMode.c_str());
507+
AMFTraceInfo(AMF_FACILITY, L"Capture mode is set to %s", captureMode.c_str());
506508
}
507509
else
508510
{
509-
AMFTraceError(AMF_FACILITY, L"Failed to set video capture mode to %S, result=%s", captureMode.c_str(), amf::AMFGetResultText(amfResult));
511+
AMFTraceError(AMF_FACILITY, L"Failed to set video capture mode to %s, result=%s", captureMode.c_str(), amf::AMFGetResultText(amfResult));
510512
}
511513
}
512514

sdk/util/stats/ClientStatsManager.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ namespace ssdk::util
5959

6060
void ClientStatsManager::Clear()
6161
{
62-
m_FramerateHistory.Clear();
62+
m_FrameDurationHistory.Clear();
6363
m_FullLatencyHistory.Clear();
6464
m_ClientLatencyHistory.Clear();
6565
m_ServerLatencyHistory.Clear();
@@ -97,9 +97,9 @@ namespace ssdk::util
9797
void ClientStatsManager::UpdateFullLatencyAndFramerate(amf_pts fullLatencyPts)
9898
{
9999
amf_pts now = amf_high_precision_clock();
100-
float framerate = (float)AMF_SECOND / (now - m_LastFrameTime);
100+
float frameDuration = (float)(now - m_LastFrameTime) / AMF_SECOND;
101101
m_LastFrameTime = now;
102-
m_FramerateHistory.Add(framerate);
102+
m_FrameDurationHistory.Add(frameDuration);
103103
float fullLatency = (float)(fullLatencyPts) / AMF_MILLISECOND;
104104
m_FullLatencyHistory.Add(fullLatency);
105105
}
@@ -155,7 +155,12 @@ namespace ssdk::util
155155
float decrypt = m_DecryptHistory.GetAverageAndClear();
156156
int32_t decoderQueueSize = static_cast<int32_t>(m_DecoderQueueTimes.size());
157157
float AVDesync = m_AVDesyncHistory.GetAverageAndClear();
158-
float framerate = m_FramerateHistory.GetAverageAndClear();
158+
float frameDurationAvg = m_FrameDurationHistory.GetAverageAndClear();
159+
float framerate = 0;
160+
if (frameDurationAvg != 0)
161+
{
162+
framerate = 1 / frameDurationAvg;
163+
}
159164

160165
StatsSnapshotImpl statsSnapshotImpl(fullLatency, clientLatency, serverLatency, encoderLatency, networkLatency, decoderLatency, decrypt, decoderQueueSize, AVDesync, framerate);
161166
transport->SendStats(ssdk::transport_common::DEFAULT_STREAM, statsSnapshotImpl);

sdk/util/stats/ClientStatsManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ namespace ssdk::util
111111
ssdk::transport_common::ClientTransport* m_Transport = nullptr;
112112

113113
typedef ssdk::util::ValueAverage<float> FloatValueAverage;
114-
FloatValueAverage m_FramerateHistory;
114+
FloatValueAverage m_FrameDurationHistory;
115115
FloatValueAverage m_FullLatencyHistory;
116116
FloatValueAverage m_ClientLatencyHistory;
117117
FloatValueAverage m_ServerLatencyHistory;

sdk/video/MonoscopicVideoOutput.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,12 @@ namespace ssdk::video
222222
amf::AMFLock lock(&m_Guard);
223223
m_Bitrate = bitrate;
224224
}
225-
return m_Encoder->UpdateBitrate(bitrate);
225+
AMF_RESULT result = m_Encoder->UpdateBitrate(bitrate);
226+
if (result != AMF_OK)
227+
{
228+
AMFTraceError(AMF_FACILITY, L"Failed to set video encoder bitrate rate to %lld", bitrate);
229+
}
230+
return result;
226231
}
227232

228233
float MonoscopicVideoOutput::GetFramerate() const noexcept
@@ -237,7 +242,12 @@ namespace ssdk::video
237242
amf::AMFLock lock(&m_Guard);
238243
m_FrameRate = framerate;
239244
}
240-
return m_Encoder->UpdateFramerate(AMFConstructRate(uint32_t(framerate), 1));
245+
AMF_RESULT result = m_Encoder->UpdateFramerate(AMFConstructRate(uint32_t(framerate), 1));
246+
if (result != AMF_OK)
247+
{
248+
AMFTraceError(AMF_FACILITY, L"Failed to set video encoder frame rate to %5.2f", framerate);
249+
}
250+
return result;
241251
}
242252

243253
bool MonoscopicVideoOutput::IsKeyFrameRequested() noexcept
@@ -302,6 +312,26 @@ namespace ssdk::video
302312
}
303313
converter = m_Converter; // Also saving a pointer to the converter in a local variable to avoid unnecessary locking, only using locals below.
304314
// The encoder is always present because it's passed to a constructor, so no need to worry about it changing on the fly
315+
316+
// Adjust the frame rate setting on the encoder to reflect the actual measured frame rate when frames are captured as Present or ASAP
317+
amf_pts now = amf_high_precision_clock();
318+
if (m_FrameCnt == 0)
319+
{
320+
m_FrameCntStartTime = now;
321+
}
322+
++m_FrameCnt;
323+
constexpr static amf_pts FPS_MEASUREMENT_PERIOD = 3;
324+
amf_pts timeSinceHistoryStart = now - m_FrameCntStartTime;
325+
if (timeSinceHistoryStart > FPS_MEASUREMENT_PERIOD * AMF_SECOND)
326+
{
327+
AMFRate fps = { uint32_t(m_FrameCnt / (timeSinceHistoryStart / AMF_SECOND)), 1 };
328+
float prevFps = m_Encoder->GetFramerate();
329+
if (abs(fps.num - prevFps) > prevFps * 0.1) // We don't want to change the encoder's frame rate too frequently by a small amount because it can reduce image quality, but when the frame changes significantly, we want to reflect that for a more optimal compression
330+
{
331+
m_Encoder->UpdateFramerate(fps);
332+
}
333+
m_FrameCnt = 0;
334+
}
305335
}
306336

307337
input->SetProperty(ORIGIN_PTS_PROPERTY, originPts);

sdk/video/MonoscopicVideoOutput.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,8 @@ namespace ssdk::video
146146

147147
int64_t m_SequenceNumber = 0;
148148
int64_t m_FramesSubmitted = 0;
149+
150+
amf_pts m_FrameCntStartTime = 0;
151+
uint32_t m_FrameCnt = 0;
149152
};
150153
}

sdk/video/encoders/GPUEncoderAV1.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ namespace ssdk::video
155155
AMF_RESULT GPUEncoderAV1::UpdateFramerate(const AMFRate& rate)
156156
{
157157
AMF_RETURN_IF_FALSE(m_Encoder != nullptr, AMF_FAIL, L"Encoder is not initialized");
158-
return m_Encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_FRAMERATE, rate);
158+
AMF_RESULT result = m_Encoder->SetProperty(AMF_VIDEO_ENCODER_AV1_FRAMERATE, rate);
159+
if (result == AMF_OK)
160+
{
161+
m_Framerate = float(rate.num) / float(rate.den);
162+
}
163+
return result;
159164
}
160165

161166
AMF_RESULT GPUEncoderAV1::GetExtraData(amf::AMFBuffer** pExtraData) const

sdk/video/encoders/GPUEncoderH264.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,12 @@ namespace ssdk::video
134134
AMF_RESULT GPUEncoderH264::UpdateFramerate(const AMFRate& rate)
135135
{
136136
AMF_RETURN_IF_FALSE(m_Encoder != nullptr, AMF_FAIL, L"Encoder is not initialized");
137-
return m_Encoder->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, rate);
137+
AMF_RESULT result = m_Encoder->SetProperty(AMF_VIDEO_ENCODER_FRAMERATE, rate);
138+
if (result == AMF_OK)
139+
{
140+
m_Framerate = float(rate.num) / float(rate.den);
141+
}
142+
return result;
138143
}
139144

140145
AMF_RESULT GPUEncoderH264::GetExtraData(amf::AMFBuffer** pExtraData) const

sdk/video/encoders/GPUEncoderHEVC.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,12 @@ namespace ssdk::video
163163
AMF_RESULT GPUEncoderHEVC::UpdateFramerate(const AMFRate& rate)
164164
{
165165
AMF_RETURN_IF_FALSE(m_Encoder != nullptr, AMF_FAIL, L"Encoder is not initialized");
166-
return m_Encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE, rate);
166+
AMF_RESULT result = m_Encoder->SetProperty(AMF_VIDEO_ENCODER_HEVC_FRAMERATE, rate);
167+
if (result == AMF_OK)
168+
{
169+
m_Framerate = float(rate.num) / float(rate.den);
170+
}
171+
return result;
167172
}
168173

169174
AMF_RESULT GPUEncoderHEVC::GetExtraData(amf::AMFBuffer** pExtraData) const

0 commit comments

Comments
 (0)