Add protocol extension for multi-client-compatible ping support

This commit is contained in:
Cameron Gutman
2023-02-12 01:23:25 -06:00
parent 5e14dbc15e
commit dc186082a7
6 changed files with 54 additions and 19 deletions
+10 -10
View File
@@ -41,8 +41,7 @@ typedef struct _QUEUED_AUDIO_PACKET {
} QUEUED_AUDIO_PACKET, *PQUEUED_AUDIO_PACKET; } QUEUED_AUDIO_PACKET, *PQUEUED_AUDIO_PACKET;
static void AudioPingThreadProc(void* context) { static void AudioPingThreadProc(void* context) {
// Ping in ASCII char legacyPingData[] = { 0x50, 0x49, 0x4E, 0x47 };
char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
LC_SOCKADDR saddr; LC_SOCKADDR saddr;
LC_ASSERT(AudioPortNumber != 0); LC_ASSERT(AudioPortNumber != 0);
@@ -50,19 +49,20 @@ static void AudioPingThreadProc(void* context) {
memcpy(&saddr, &RemoteAddr, sizeof(saddr)); memcpy(&saddr, &RemoteAddr, sizeof(saddr));
SET_PORT(&saddr, AudioPortNumber); SET_PORT(&saddr, AudioPortNumber);
// Send PING every 500 milliseconds
while (!PltIsThreadInterrupted(&udpPingThread)) {
// We do not check for errors here. Socket errors will be handled // We do not check for errors here. Socket errors will be handled
// on the read-side in ReceiveThreadProc(). This avoids potential // on the read-side in ReceiveThreadProc(). This avoids potential
// issues related to receiving ICMP port unreachable messages due // issues related to receiving ICMP port unreachable messages due
// to sending a packet prior to the host PC binding to that port. // to sending a packet prior to the host PC binding to that port.
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen); int pingCount = 0;
while (!PltIsThreadInterrupted(&udpPingThread)) {
if (AudioPingPayload.payload[0] != 0) {
pingCount++;
AudioPingPayload.sequenceNumber = BE32(pingCount);
if (firstReceiveTime == 0 && isSocketReadable(rtpSocket)) { sendto(rtpSocket, (char*)&AudioPingPayload, sizeof(AudioPingPayload), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
// Remember the time when we got our first incoming audio packet. }
// We will need to adjust for the delay between this event and else {
// when the real receive thread is ready to avoid falling behind. sendto(rtpSocket, legacyPingData, sizeof(legacyPingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
firstReceiveTime = PltGetMillis();
} }
PltSleepMsInterruptible(&udpPingThread, 500); PltSleepMsInterruptible(&udpPingThread, 500);
+2
View File
@@ -29,6 +29,8 @@ uint16_t RtspPortNumber;
uint16_t ControlPortNumber; uint16_t ControlPortNumber;
uint16_t AudioPortNumber; uint16_t AudioPortNumber;
uint16_t VideoPortNumber; uint16_t VideoPortNumber;
SS_PING AudioPingPayload;
SS_PING VideoPingPayload;
// Connection stages // Connection stages
static const char* stageNames[STAGE_MAX] = { static const char* stageNames[STAGE_MAX] = {
+3
View File
@@ -38,6 +38,9 @@ extern uint16_t ControlPortNumber;
extern uint16_t AudioPortNumber; extern uint16_t AudioPortNumber;
extern uint16_t VideoPortNumber; extern uint16_t VideoPortNumber;
extern SS_PING AudioPingPayload;
extern SS_PING VideoPingPayload;
#ifndef UINT24_MAX #ifndef UINT24_MAX
#define UINT24_MAX 0xFFFFFF #define UINT24_MAX 0xFFFFFF
#endif #endif
+16
View File
@@ -893,6 +893,7 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
{ {
RTSP_MESSAGE response; RTSP_MESSAGE response;
char* sessionId; char* sessionId;
char* pingPayload;
int error = -1; int error = -1;
if (!setupStream(&response, if (!setupStream(&response,
@@ -922,6 +923,13 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
Limelog("Audio port: %u\n", AudioPortNumber); Limelog("Audio port: %u\n", AudioPortNumber);
} }
// Parse the Sunshine ping payload protocol extension if present
memset(&AudioPingPayload, 0, sizeof(AudioPingPayload));
pingPayload = getOptionContent(response.options, "X-SS-Ping-Payload");
if (pingPayload != NULL && strlen(pingPayload) == sizeof(AudioPingPayload.payload)) {
memcpy(AudioPingPayload.payload, pingPayload, sizeof(AudioPingPayload.payload));
}
// Let the audio stream know the port number is now finalized. // Let the audio stream know the port number is now finalized.
// NB: This is needed because audio stream init happens before RTSP, // NB: This is needed because audio stream init happens before RTSP,
// which is not the case for the video stream. // which is not the case for the video stream.
@@ -955,6 +963,7 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
{ {
RTSP_MESSAGE response; RTSP_MESSAGE response;
int error = -1; int error = -1;
char* pingPayload;
if (!setupStream(&response, if (!setupStream(&response,
AppVersionQuad[0] >= 5 ? "streamid=video/0/0" : "streamid=video", AppVersionQuad[0] >= 5 ? "streamid=video/0/0" : "streamid=video",
@@ -971,6 +980,13 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
goto Exit; goto Exit;
} }
// Parse the Sunshine ping payload protocol extension if present
memset(&VideoPingPayload, 0, sizeof(VideoPingPayload));
pingPayload = getOptionContent(response.options, "X-SS-Ping-Payload");
if (pingPayload != NULL && strlen(pingPayload) == sizeof(VideoPingPayload.payload)) {
memcpy(VideoPingPayload.payload, pingPayload, sizeof(VideoPingPayload.payload));
}
// Parse the video port out of the RTSP SETUP response // Parse the video port out of the RTSP SETUP response
LC_ASSERT(VideoPortNumber == 0); LC_ASSERT(VideoPortNumber == 0);
if (!parseServerPortFromTransport(&response, &VideoPortNumber)) { if (!parseServerPortFromTransport(&response, &VideoPortNumber)) {
+5
View File
@@ -36,4 +36,9 @@ typedef struct _RTP_PACKET {
uint32_t ssrc; uint32_t ssrc;
} RTP_PACKET, *PRTP_PACKET; } RTP_PACKET, *PRTP_PACKET;
typedef struct _SS_PING {
char payload[16];
uint32_t sequenceNumber;
} SS_PING, *PSS_PING;
#pragma pack(pop) #pragma pack(pop)
+12 -3
View File
@@ -43,7 +43,7 @@ void destroyVideoStream(void) {
// UDP Ping proc // UDP Ping proc
static void VideoPingThreadProc(void* context) { static void VideoPingThreadProc(void* context) {
char pingData[] = { 0x50, 0x49, 0x4E, 0x47 }; char legacyPingData[] = { 0x50, 0x49, 0x4E, 0x47 };
LC_SOCKADDR saddr; LC_SOCKADDR saddr;
LC_ASSERT(VideoPortNumber != 0); LC_ASSERT(VideoPortNumber != 0);
@@ -51,12 +51,21 @@ static void VideoPingThreadProc(void* context) {
memcpy(&saddr, &RemoteAddr, sizeof(saddr)); memcpy(&saddr, &RemoteAddr, sizeof(saddr));
SET_PORT(&saddr, VideoPortNumber); SET_PORT(&saddr, VideoPortNumber);
while (!PltIsThreadInterrupted(&udpPingThread)) {
// We do not check for errors here. Socket errors will be handled // We do not check for errors here. Socket errors will be handled
// on the read-side in ReceiveThreadProc(). This avoids potential // on the read-side in ReceiveThreadProc(). This avoids potential
// issues related to receiving ICMP port unreachable messages due // issues related to receiving ICMP port unreachable messages due
// to sending a packet prior to the host PC binding to that port. // to sending a packet prior to the host PC binding to that port.
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen); int pingCount = 0;
while (!PltIsThreadInterrupted(&udpPingThread)) {
if (VideoPingPayload.payload[0] != 0) {
pingCount++;
VideoPingPayload.sequenceNumber = BE32(pingCount);
sendto(rtpSocket, (char*)&VideoPingPayload, sizeof(VideoPingPayload), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
}
else {
sendto(rtpSocket, legacyPingData, sizeof(legacyPingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
}
PltSleepMsInterruptible(&udpPingThread, 500); PltSleepMsInterruptible(&udpPingThread, 500);
} }