diff --git a/src/Connection.c b/src/Connection.c index 02520c4d..b914827a 100644 --- a/src/Connection.c +++ b/src/Connection.c @@ -22,7 +22,6 @@ bool HighQualitySurroundSupported; bool HighQualitySurroundEnabled; OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig; OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig; -int OriginalVideoBitrate; int AudioPacketDuration; bool AudioEncryptionEnabled; bool ReferenceFrameInvalidationSupported; @@ -258,7 +257,6 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre memset(&LocalAddr, 0, sizeof(LocalAddr)); NegotiatedVideoFormat = 0; memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig)); - OriginalVideoBitrate = streamConfig->bitrate; RemoteAddrString = strdup(serverInfo->address); // The values in RTSP SETUP will be used to populate these. diff --git a/src/Limelight-internal.h b/src/Limelight-internal.h index d69b0b38..fd040839 100644 --- a/src/Limelight-internal.h +++ b/src/Limelight-internal.h @@ -29,7 +29,6 @@ extern bool HighQualitySurroundSupported; extern bool HighQualitySurroundEnabled; extern OPUS_MULTISTREAM_CONFIGURATION NormalQualityOpusConfig; extern OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig; -extern int OriginalVideoBitrate; extern int AudioPacketDuration; extern bool AudioEncryptionEnabled; extern bool ReferenceFrameInvalidationSupported; diff --git a/src/Limelight.h b/src/Limelight.h index d65d7275..af50dd89 100644 --- a/src/Limelight.h +++ b/src/Limelight.h @@ -42,7 +42,9 @@ typedef struct _STREAM_CONFIGURATION { // FPS of the desired video stream int fps; - // Bitrate of the desired video stream (audio adds another ~1 Mbps) + // Bitrate of the desired video stream (audio adds another ~1 Mbps). This + // includes error correction data, so the actual encoder bitrate will be + // about 20% lower when using the standard 20% FEC configuration. int bitrate; // Max video packet size in bytes (use 1024 if unsure). If STREAM_CFG_AUTO @@ -65,18 +67,6 @@ typedef struct _STREAM_CONFIGURATION { // See VIDEO_FORMAT constants below. int supportedVideoFormats; - // Specifies the percentage that the specified bitrate will be adjusted - // when an HEVC stream will be delivered. This allows clients to opt to - // reduce bandwidth when HEVC is chosen as the video codec rather than - // (or in addition to) improving image quality. - int hevcBitratePercentageMultiplier; - - // Specifies the percentage that the specified bitrate will be adjusted - // when an AV1 stream will be delivered. This allows clients to opt to - // reduce bandwidth when AV1 is chosen as the video codec rather than - // (or in addition to) improving image quality. - int av1BitratePercentageMultiplier; - // If specified, the client's display refresh rate x 100. For example, // 59.94 Hz would be specified as 5994. This is used by recent versions // of GFE for enhanced frame pacing. diff --git a/src/RtspConnection.c b/src/RtspConnection.c index c2b50539..4e164796 100644 --- a/src/RtspConnection.c +++ b/src/RtspConnection.c @@ -785,7 +785,7 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) { // 2. The audio decoder has not declared that it is slow // 3. The stream is either local or not surround sound (to prevent MTU issues over the Internet) LC_ASSERT(StreamConfig.streamingRemotely != STREAM_CFG_AUTO); - if (OriginalVideoBitrate >= HIGH_AUDIO_BITRATE_THRESHOLD && + if (StreamConfig.bitrate >= HIGH_AUDIO_BITRATE_THRESHOLD && (AudioCallbacks.capabilities & CAPABILITY_SLOW_OPUS_DECODER) == 0 && (StreamConfig.streamingRemotely != STREAM_CFG_REMOTE || CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION(StreamConfig.audioConfiguration) <= 2)) { // If we have an RTSP URL string and it was successfully parsed and copied, use that string @@ -906,12 +906,6 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) { } else { NegotiatedVideoFormat = VIDEO_FORMAT_AV1_MAIN8; - - // Apply bitrate adjustment for SDR AV1 if the client requested one - if (StreamConfig.av1BitratePercentageMultiplier != 0) { - StreamConfig.bitrate *= StreamConfig.av1BitratePercentageMultiplier; - StreamConfig.bitrate /= 100; - } } } else if ((StreamConfig.supportedVideoFormats & VIDEO_FORMAT_MASK_H265) && strstr(response.payload, "sprop-parameter-sets=AAAAAU")) { @@ -926,12 +920,6 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) { } else { NegotiatedVideoFormat = VIDEO_FORMAT_H265; - - // Apply bitrate adjustment for SDR HEVC if the client requested one - if (StreamConfig.hevcBitratePercentageMultiplier != 0) { - StreamConfig.bitrate *= StreamConfig.hevcBitratePercentageMultiplier; - StreamConfig.bitrate /= 100; - } } } else { diff --git a/src/SdpGenerator.c b/src/SdpGenerator.c index c92f3205..2942a66b 100644 --- a/src/SdpGenerator.c +++ b/src/SdpGenerator.c @@ -258,7 +258,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { int audioChannelCount; int audioChannelMask; int err; - int bitrate; + int adjustedBitrate; // This must have been resolved to either local or remote by now LC_ASSERT(StreamConfig.streamingRemotely != STREAM_CFG_AUTO); @@ -289,44 +289,43 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { err |= addAttributeString(&optionHead, "x-nv-video[0].timeoutLengthMs", "7000"); err |= addAttributeString(&optionHead, "x-nv-video[0].framesWithInvalidRefThreshold", "0"); + // 20% of the video bitrate will added to the user-specified bitrate for FEC + adjustedBitrate = (int)(StreamConfig.bitrate * 0.80); + // Use more strict bitrate logic when streaming remotely. The theory here is that remote // streaming is much more bandwidth sensitive. Someone might select 5 Mbps because that's // really all they have, so we need to be careful not to exceed the cap, even counting // things like audio and control data. if (StreamConfig.streamingRemotely == STREAM_CFG_REMOTE) { - // 20% of the video bitrate will added to the user-specified bitrate for FEC - bitrate = (int)(OriginalVideoBitrate * 0.80); - // Subtract 500 Kbps to leave room for audio and control. On remote streams, // GFE will use 96Kbps stereo audio. For local streams, it will choose 512Kbps. - if (bitrate > 500) { - bitrate -= 500; + if (adjustedBitrate > 500) { + adjustedBitrate -= 500; } } - else { - bitrate = StreamConfig.bitrate; - } - - // If the calculated bitrate (with the HEVC multiplier in effect) is less than this, - // use the lower of the two bitrate values. - bitrate = StreamConfig.bitrate < bitrate ? StreamConfig.bitrate : bitrate; // GFE currently imposes a limit of 100 Mbps for the video bitrate. It will automatically // impose that on maximumBitrateKbps but not on initialBitrateKbps. We will impose the cap // ourselves so initialBitrateKbps does not exceed maximumBitrateKbps. - bitrate = bitrate > 100000 ? 100000 : bitrate; + adjustedBitrate = adjustedBitrate > 100000 ? 100000 : adjustedBitrate; // We don't support dynamic bitrate scaling properly (it tends to bounce between min and max and never // settle on the optimal bitrate if it's somewhere in the middle), so we'll just latch the bitrate // to the requested value. if (AppVersionQuad[0] >= 5) { - snprintf(payloadStr, sizeof(payloadStr), "%d", bitrate); + snprintf(payloadStr, sizeof(payloadStr), "%d", adjustedBitrate); err |= addAttributeString(&optionHead, "x-nv-video[0].initialBitrateKbps", payloadStr); err |= addAttributeString(&optionHead, "x-nv-video[0].initialPeakBitrateKbps", payloadStr); err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrateKbps", payloadStr); err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrateKbps", payloadStr); + + // Send the configured bitrate to Sunshine hosts, so they can adjust for dynamic FEC percentage + if (IS_SUNSHINE()) { + snprintf(payloadStr, sizeof(payloadStr), "%u", StreamConfig.bitrate); + err |= addAttributeString(&optionHead, "x-ml-video.configuredBitrateKbps", payloadStr); + } } else { if (StreamConfig.streamingRemotely == STREAM_CFG_REMOTE) { @@ -334,7 +333,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { err |= addAttributeString(&optionHead, "x-nv-video[0].peakBitrate", "4"); } - snprintf(payloadStr, sizeof(payloadStr), "%d", bitrate); + snprintf(payloadStr, sizeof(payloadStr), "%d", adjustedBitrate); err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrate", payloadStr); err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrate", payloadStr); } @@ -447,8 +446,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { } if (AppVersionQuad[0] >= 7) { - // Decide to use HQ audio based on the original video bitrate, not the HEVC-adjusted value - if (OriginalVideoBitrate >= HIGH_AUDIO_BITRATE_THRESHOLD && audioChannelCount > 2 && + if (StreamConfig.bitrate >= HIGH_AUDIO_BITRATE_THRESHOLD && audioChannelCount > 2 && HighQualitySurroundSupported && (AudioCallbacks.capabilities & CAPABILITY_SLOW_OPUS_DECODER) == 0) { // Enable high quality mode for surround sound err |= addAttributeString(&optionHead, "x-nv-audio.surround.AudioQuality", "1"); @@ -466,7 +464,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { if ((AudioCallbacks.capabilities & CAPABILITY_SLOW_OPUS_DECODER) || ((AudioCallbacks.capabilities & CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION) != 0 && - OriginalVideoBitrate < LOW_AUDIO_BITRATE_TRESHOLD)) { + StreamConfig.bitrate < LOW_AUDIO_BITRATE_TRESHOLD)) { // Use 10 ms packets for slow devices and networks to balance latency and bandwidth usage AudioPacketDuration = 10; }