diff --git a/src/Connection.c b/src/Connection.c index 14d0792..0e5b559 100644 --- a/src/Connection.c +++ b/src/Connection.c @@ -231,6 +231,25 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre ListenerCallbacks.stageComplete(STAGE_NAME_RESOLUTION); Limelog("done\n"); + // If STREAM_CFG_AUTO was requested, determine the streamingRemotely value + // now that we have resolved the target address and impose the video packet + // size cap if required. + if (StreamConfig.streamingRemotely == STREAM_CFG_AUTO) { + if (isPrivateNetworkAddress(&RemoteAddr)) { + StreamConfig.streamingRemotely = STREAM_CFG_LOCAL; + } + else { + StreamConfig.streamingRemotely = STREAM_CFG_REMOTE; + + if (StreamConfig.packetSize > 1024) { + // Cap packet size at 1024 for remote streaming to avoid + // MTU problems and fragmentation. + Limelog("Packet size capped at 1KB for remote streaming\n"); + StreamConfig.packetSize = 1024; + } + } + } + Limelog("Starting RTSP handshake..."); ListenerCallbacks.stageStarting(STAGE_RTSP_HANDSHAKE); err = performRtspHandshake(); diff --git a/src/Limelight.h b/src/Limelight.h index d335576..b90d906 100644 --- a/src/Limelight.h +++ b/src/Limelight.h @@ -13,6 +13,11 @@ extern "C" { // Enable this definition during debugging to enable assertions //#define LC_DEBUG +// Values for the 'streamingRemotely' field below +#define STREAM_CFG_LOCAL 0 +#define STREAM_CFG_REMOTE 1 +#define STREAM_CFG_AUTO 2 + typedef struct _STREAM_CONFIGURATION { // Dimensions in pixels of the desired video stream int width; @@ -24,11 +29,16 @@ typedef struct _STREAM_CONFIGURATION { // Bitrate of the desired video stream (audio adds another ~1 Mbps) int bitrate; - // Max video packet size in bytes (use 1024 if unsure) + // Max video packet size in bytes (use 1024 if unsure). If STREAM_CFG_AUTO + // determines the stream is remote (see below), it will cap this value at + // 1024 to avoid MTU-related issues like packet loss and fragmentation. int packetSize; - // Set to non-zero value to enable remote (over the Internet) - // streaming optimizations. If unsure, set to 0. + // Determines whether to enable remote (over the Internet) + // streaming optimizations. If unsure, set to STREAM_CFG_AUTO. + // STREAM_CFG_AUTO uses a heuristic (whether the target address is + // in the RFC 1918 address blocks) to decide whether the stream + // is remote or not. int streamingRemotely; // Specifies the channel configuration of the audio stream. diff --git a/src/PlatformSockets.c b/src/PlatformSockets.c index 67e7c7f..6d1d815 100644 --- a/src/PlatformSockets.c +++ b/src/PlatformSockets.c @@ -348,6 +348,33 @@ int resolveHostName(const char* host, int family, int tcpTestPort, struct sockad #endif } +int isPrivateNetworkAddress(struct sockaddr_storage* address) { + unsigned int addr; + + // We only count IPv4 addresses as possibly private for now + if (address->ss_family != AF_INET) { + return 0; + } + + memcpy(&addr, &((struct sockaddr_in*)address)->sin_addr, sizeof(addr)); + addr = htonl(addr); + + // 10.0.0.0/8 + if ((addr & 0xFF000000) == 0x0A000000) { + return 1; + } + // 172.16.0.0/12 + else if ((addr & 0xFFF00000) == 0xAC100000) { + return 1; + } + // 192.168.0.0/16 + else if ((addr & 0xFFFF0000) == 0xC0A80000) { + return 1; + } + + return 0; +} + int initializePlatformSockets(void) { #if defined(LC_WINDOWS) WSADATA data; diff --git a/src/PlatformSockets.h b/src/PlatformSockets.h index e9176d1..56e46fa 100644 --- a/src/PlatformSockets.h +++ b/src/PlatformSockets.h @@ -81,3 +81,4 @@ void shutdownTcpSocket(SOCKET s); int setNonFatalRecvTimeoutMs(SOCKET s, int timeoutMs); void setRecvTimeout(SOCKET s, int timeoutSec); void closeSocket(SOCKET s); +int isPrivateNetworkAddress(struct sockaddr_storage* address); diff --git a/src/SdpGenerator.c b/src/SdpGenerator.c index 45e235b..ca25060 100644 --- a/src/SdpGenerator.c +++ b/src/SdpGenerator.c @@ -176,6 +176,9 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { int audioChannelMask; int err; + // This must have been resolved to either local or remote by now + LC_ASSERT(StreamConfig.streamingRemotely != STREAM_CFG_AUTO); + optionHead = NULL; err = 0; @@ -259,7 +262,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrateKbps", payloadStr); } else { - if (StreamConfig.streamingRemotely) { + if (StreamConfig.streamingRemotely == STREAM_CFG_REMOTE) { err |= addAttributeString(&optionHead, "x-nv-video[0].averageBitrate", "4"); err |= addAttributeString(&optionHead, "x-nv-video[0].peakBitrate", "4"); } @@ -274,7 +277,7 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) { err |= addAttributeString(&optionHead, "x-nv-vqos[0].videoQualityScoreUpdateTime", "5000"); - if (StreamConfig.streamingRemotely) { + if (StreamConfig.streamingRemotely == STREAM_CFG_REMOTE) { err |= addAttributeString(&optionHead, "x-nv-vqos[0].qosTrafficType", "0"); err |= addAttributeString(&optionHead, "x-nv-aqos.qosTrafficType", "0"); }