mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-06-24 05:31:13 +00:00
Add protocol extension for multi-client-compatible ping support
This commit is contained in:
+13
-13
@@ -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
|
// We do not check for errors here. Socket errors will be handled
|
||||||
|
// on the read-side in ReceiveThreadProc(). This avoids potential
|
||||||
|
// issues related to receiving ICMP port unreachable messages due
|
||||||
|
// to sending a packet prior to the host PC binding to that port.
|
||||||
|
int pingCount = 0;
|
||||||
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
||||||
// We do not check for errors here. Socket errors will be handled
|
if (AudioPingPayload.payload[0] != 0) {
|
||||||
// on the read-side in ReceiveThreadProc(). This avoids potential
|
pingCount++;
|
||||||
// issues related to receiving ICMP port unreachable messages due
|
AudioPingPayload.sequenceNumber = BE32(pingCount);
|
||||||
// to sending a packet prior to the host PC binding to that port.
|
|
||||||
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -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] = {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+15
-6
@@ -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);
|
||||||
|
|
||||||
|
// We do not check for errors here. Socket errors will be handled
|
||||||
|
// on the read-side in ReceiveThreadProc(). This avoids potential
|
||||||
|
// issues related to receiving ICMP port unreachable messages due
|
||||||
|
// to sending a packet prior to the host PC binding to that port.
|
||||||
|
int pingCount = 0;
|
||||||
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
||||||
// We do not check for errors here. Socket errors will be handled
|
if (VideoPingPayload.payload[0] != 0) {
|
||||||
// on the read-side in ReceiveThreadProc(). This avoids potential
|
pingCount++;
|
||||||
// issues related to receiving ICMP port unreachable messages due
|
VideoPingPayload.sequenceNumber = BE32(pingCount);
|
||||||
// to sending a packet prior to the host PC binding to that port.
|
|
||||||
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user