Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Limelight.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,23 @@ typedef struct _STREAM_CONFIGURATION {
// in /launch and /resume requests.
char remoteInputAesKey[16];
char remoteInputAesIv[16];

// Fractional frame rate of the video stream (in the form of numerator
// and denominator). Supported by recent versions of Sunshine.
// The non-fractional frame rate still needs to be specified for backward
// compatibility. LiConvertFloatingPointFrameRateToFraction() can be used for
// converting floating-point frame rates to this representation.
int fpsNum;
int fpsDen;
} STREAM_CONFIGURATION, *PSTREAM_CONFIGURATION;

// Use this function to zero the stream configuration when allocated on the stack or heap
void LiInitializeStreamConfiguration(PSTREAM_CONFIGURATION streamConfig);

// Use this function to convert floating-point frame rates to fractional frame
// rates (in the form of numerator and denominator) while trying to maximize precision.
void LiConvertFloatingPointFrameRateToFraction(double value, int* outNum, int* outDen);

// These identify codec configuration data in the buffer lists
// of frames identified as IDR frames for H.264 and HEVC formats.
// For other codecs, all data is marked as BUFFER_TYPE_PICDATA.
Expand Down
29 changes: 29 additions & 0 deletions src/Misc.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "Limelight-internal.h"

#include <math.h>

#define ENET_INTERNAL_TIMEOUT_MS 100

// This function wraps enet_host_service() and hides the fact that it must be called
Expand Down Expand Up @@ -128,6 +130,33 @@ void LiInitializeStreamConfiguration(PSTREAM_CONFIGURATION streamConfig) {
memset(streamConfig, 0, sizeof(*streamConfig));
}

void LiConvertFloatingPointFrameRateToFraction(double value, int* outNum, int* outDen) {
if (fabs(value) < 1.0) {
// Unrealistic scenario, we don't care about perfect precision
*outNum = (int)round(INT32_MAX * value);
*outDen = INT32_MAX;
}
else if (fabs(value) > 1000.0) {
// Unrealistic scenario, we don't care about perfect precision
*outNum = INT32_MAX;
*outDen = (int)round(INT32_MAX / value);
}
else {
// Try different numerators for the best precision
double minError = 1.0;
for (int i = 0; i < value; ++i) {
int num = INT32_MAX - i;
int den = (int)round(num / value);
double error = fabs((double)num / den - value);
if (error < minError) {
minError = error;
*outNum = num;
*outDen = den;
}
}
}
}

void LiInitializeVideoCallbacks(PDECODER_RENDERER_CALLBACKS drCallbacks) {
memset(drCallbacks, 0, sizeof(*drCallbacks));
}
Expand Down
9 changes: 9 additions & 0 deletions src/SdpGenerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,15 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
else {
err |= addAttributeString(&optionHead, "x-ss-video[0].chromaSamplingType", "0");
}

// Specify fractional frame rate if requested
if (StreamConfig.fpsNum > 0 && StreamConfig.fpsDen > 0) {
snprintf(payloadStr, sizeof(payloadStr), "%d", StreamConfig.fpsNum);
err |= addAttributeString(&optionHead, "x-ml-video.targetFrameRateNum", payloadStr);

snprintf(payloadStr, sizeof(payloadStr), "%d", StreamConfig.fpsDen);
err |= addAttributeString(&optionHead, "x-ml-video.targetFrameRateDen", payloadStr);
}
}

snprintf(payloadStr, sizeof(payloadStr), "%d", StreamConfig.width);
Expand Down