diff --git a/src/AudioStream.c b/src/AudioStream.c index a8a60c0..80e012c 100644 --- a/src/AudioStream.c +++ b/src/AudioStream.c @@ -93,7 +93,7 @@ int notifyAudioPortNegotiationComplete(void) { // For GFE 3.22 compatibility, we must start the audio ping thread before the RTSP handshake. // It will not reply to our RTSP PLAY request until the audio ping has been received. - rtpSocket = bindUdpSocket(RemoteAddr.ss_family, &LocalAddr, AddrLen, 0); + rtpSocket = bindUdpSocket(RemoteAddr.ss_family, &LocalAddr, AddrLen, 0, SOCK_QOS_TYPE_AUDIO); if (rtpSocket == INVALID_SOCKET) { return LastSocketFail(); } diff --git a/src/PlatformSockets.c b/src/PlatformSockets.c index 4ef27a6..6847ec8 100644 --- a/src/PlatformSockets.c +++ b/src/PlatformSockets.c @@ -235,7 +235,56 @@ void closeSocket(SOCKET s) { #endif } -SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize) { +// These set "safe" host or link-local QoS options that we can unconditionally +// set without having to worry about routers blockholing the traffic. +static void setSocketQos(SOCKET s, int socketQosType) { +#ifdef SO_NET_SERVICE_TYPE + int value; + switch (socketQosType) { + case SOCK_QOS_TYPE_BEST_EFFORT: + value = NET_SERVICE_TYPE_BE; + break; + case SOCK_QOS_TYPE_AUDIO: + value = NET_SERVICE_TYPE_VO; + break; + case SOCK_QOS_TYPE_VIDEO: + value = NET_SERVICE_TYPE_VI; + break; + default: + Limelog("Unknown QoS type: %d\n", socketQosType); + return; + } + + // iOS/macOS + if (setsockopt(s, SOL_SOCKET, SO_NET_SERVICE_TYPE, (char*)&value, sizeof(value)) < 0) { + Limelog("setsockopt(SO_NET_SERVICE_TYPE, %d) failed: %d\n", value, (int)LastSocketError()); + } +#endif +#ifdef SO_PRIORITY + int value; + switch (socketQosType) { + case SOCK_QOS_TYPE_BEST_EFFORT: + value = 0; + break; + case SOCK_QOS_TYPE_AUDIO: + value = 6; + break; + case SOCK_QOS_TYPE_VIDEO: + value = 5; + break; + default: + Limelog("Unknown QoS type: %d\n", socketQosType); + return; + } + + // Linux + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, (char*)&value, sizeof(value)) < 0) { + Limelog("setsockopt(SO_PRIORITY, %d) failed: %d\n", value, (int)LastSocketError()); + } +#endif +} + +SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize, int socketQosType) { SOCKET s; LC_SOCKADDR bindAddr; int err; @@ -294,6 +343,11 @@ SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCK } #endif + // Enable QOS for the socket (best effort) + if (socketQosType != SOCK_QOS_TYPE_BEST_EFFORT) { + setSocketQos(s, socketQosType); + } + if (bufferSize != 0) { // We start at the requested recv buffer value and step down until we find // a value that the OS will accept. diff --git a/src/PlatformSockets.h b/src/PlatformSockets.h index 290461b..0eab7ef 100644 --- a/src/PlatformSockets.h +++ b/src/PlatformSockets.h @@ -95,10 +95,14 @@ typedef struct sockaddr_in LC_SOCKADDR; #endif void addrToUrlSafeString(struct sockaddr_storage* addr, char* string, size_t stringLength); +#define SOCK_QOS_TYPE_BEST_EFFORT 0 +#define SOCK_QOS_TYPE_AUDIO 1 +#define SOCK_QOS_TYPE_VIDEO 2 + SOCKET createSocket(int addressFamily, int socketType, int protocol, bool nonBlocking); SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec); int sendMtuSafe(SOCKET s, char* buffer, int size); -SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize); +SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize, int socketQosType); int enableNoDelay(SOCKET s); int setSocketNonBlocking(SOCKET s, bool enabled); int recvUdpSocket(SOCKET s, char* buffer, int size, bool useSelect); diff --git a/src/SimpleStun.c b/src/SimpleStun.c index 352a5f1..64a93ef 100644 --- a/src/SimpleStun.c +++ b/src/SimpleStun.c @@ -73,7 +73,7 @@ int LiFindExternalAddressIP4(const char* stunServer, unsigned short stunPort, un goto Exit; } - sock = bindUdpSocket(hints.ai_family, NULL, 0, 0); + sock = bindUdpSocket(hints.ai_family, NULL, 0, 0, SOCK_QOS_TYPE_BEST_EFFORT); if (sock == INVALID_SOCKET) { err = LastSocketFail(); Limelog("Failed to connect to STUN server: %d\n", err); diff --git a/src/VideoStream.c b/src/VideoStream.c index c605ef8..28be7cb 100644 --- a/src/VideoStream.c +++ b/src/VideoStream.c @@ -337,7 +337,8 @@ int startVideoStream(void* rendererContext, int drFlags) { } rtpSocket = bindUdpSocket(RemoteAddr.ss_family, &LocalAddr, AddrLen, - RTP_RECV_PACKETS_BUFFERED * (StreamConfig.packetSize + MAX_RTP_HEADER_SIZE)); + RTP_RECV_PACKETS_BUFFERED * (StreamConfig.packetSize + MAX_RTP_HEADER_SIZE), + SOCK_QOS_TYPE_VIDEO); if (rtpSocket == INVALID_SOCKET) { VideoCallbacks.cleanup(); return LastSocketError();