Skip to content

Commit

Permalink
Improve high quality audio support
Browse files Browse the repository at this point in the history
High quality audio now works on IPv6 and remote connections
  • Loading branch information
cgutman committed Nov 5, 2022
1 parent 7d9df5b commit 50c0a51
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre

Limelog("Starting RTSP handshake...");
ListenerCallbacks.stageStarting(STAGE_RTSP_HANDSHAKE);
err = performRtspHandshake();
err = performRtspHandshake(serverInfo);
if (err != 0) {
Limelog("failed: %d\n", err);
ListenerCallbacks.stageFailed(STAGE_RTSP_HANDSHAKE, err);
Expand Down
2 changes: 1 addition & 1 deletion src/Limelight-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ void connectionLostPackets(int lastReceivedPacket, int nextReceivedPacket);
int sendInputPacketOnControlStream(unsigned char* data, int length);
bool isControlDataInTransit(void);

int performRtspHandshake(void);
int performRtspHandshake(PSERVER_INFORMATION serverInfo);

void initializeVideoDepacketizer(int pktSize);
void destroyVideoDepacketizer(void);
Expand Down
95 changes: 82 additions & 13 deletions src/RtspConnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -657,32 +657,101 @@ static int parseOpusConfigurations(PRTSP_MESSAGE response) {
return 0;
}

// Perform RTSP Handshake with the streaming server machine as part of the connection process
int performRtspHandshake(void) {
int ret;
static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination) {
char* rtspUrlScratchBuffer;
char* portSeparator;
char* v6EscapeEndChar;
char* urlPathSeparator;
int prefixLen;

// Create a copy that we can modify
rtspUrlScratchBuffer = strdup(rtspUrlString);
if (rtspUrlScratchBuffer == NULL) {
return false;
}

// HACK: In order to get GFE to respect our request for a lower audio bitrate, we must
// fake our target address so it doesn't match any of the PC's local interfaces. It seems
// that the only way to get it to give you "low quality" stereo audio nowadays is if it
// thinks you are remote (target address != any local address).
if (OriginalVideoBitrate >= HIGH_AUDIO_BITRATE_THRESHOLD &&
(AudioCallbacks.capabilities & CAPABILITY_SLOW_OPUS_DECODER) == 0) {
addrToUrlSafeString(&RemoteAddr, urlAddr);
// If we have a v6 address, we want to stop one character after the closing ]
// If we have a v4 address, we want to stop at the port separator
portSeparator = strrchr(rtspUrlScratchBuffer, ':');
v6EscapeEndChar = strchr(rtspUrlScratchBuffer, ']');

// Count the prefix length to skip past the initial rtsp:// or rtspru:// part
for (prefixLen = 2; rtspUrlScratchBuffer[prefixLen - 2] != 0 && (rtspUrlScratchBuffer[prefixLen - 2] != '/' || rtspUrlScratchBuffer[prefixLen - 1] != '/'); prefixLen++);

// If we hit the end of the string prior to parsing the prefix, we cannot proceed
if (rtspUrlScratchBuffer[prefixLen - 2] == 0) {
free(rtspUrlScratchBuffer);
return false;
}
else {
strcpy(urlAddr, "0.0.0.0");

// Look for a slash at the end of the host portion of the URL (may not be present)
urlPathSeparator = strchr(rtspUrlScratchBuffer + prefixLen, '/');

// Check for a v6 address first since they also have colons
if (v6EscapeEndChar) {
// Terminate the string at the next character
*(v6EscapeEndChar + 1) = 0;
}
else if (portSeparator) {
// Terminate the string prior to the port separator
*portSeparator = 0;
}
else if (urlPathSeparator) {
// Terminate the string prior to the path separator
*urlPathSeparator = 0;
}

strcpy(destination, rtspUrlScratchBuffer + prefixLen);

free(rtspUrlScratchBuffer);
return true;
}

// Perform RTSP Handshake with the streaming server machine as part of the connection process
int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
int ret;

LC_ASSERT(RtspPortNumber != 0);

// Initialize global state
useEnet = (AppVersionQuad[0] >= 5) && (AppVersionQuad[0] <= 7) && (AppVersionQuad[2] < 404);
sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
currentSeqNumber = 1;
hasSessionId = false;
controlStreamId = APP_VERSION_AT_LEAST(7, 1, 431) ? "streamid=control/13/0" : "streamid=control/1/0";
AudioEncryptionEnabled = false;

// HACK: In order to get GFE to respect our request for a lower audio bitrate, we must
// fake our target address so it doesn't match any of the PC's local interfaces. It seems
// that the only way to get it to give you "low quality" stereo audio nowadays is if it
// thinks you are remote (target address != any local address).
//
// We will enable high quality audio if the following are all true:
// 1. Video bitrate is higher than 15 Mbps (to ensure most bandwidth is reserved for video)
// 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 &&
(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, use that string
if (serverInfo->rtspSessionUrl != NULL && parseUrlAddrFromRtspUrlString(serverInfo->rtspSessionUrl, urlAddr)) {
strcpy(rtspTargetUrl, serverInfo->rtspSessionUrl);
}
else {
// If an RTSP URL string was not provided or failed to parse, we will construct one now as best we can.
//
// NB: If the remote address is not a LAN address, the host will likely not enable high quality
// audio since it only does that for local streaming normally. We can avoid this limitation,
// but only if the caller gave us the RTSP session URL that it received from the host during launch.
addrToUrlSafeString(&RemoteAddr, urlAddr);
sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
}
}
else {
strcpy(urlAddr, "0.0.0.0");
sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
}

switch (AppVersionQuad[0]) {
case 3:
rtspClientVersion = 10;
Expand Down

0 comments on commit 50c0a51

Please sign in to comment.