Add protocol extension for multi-client-compatible ping support
This commit is contained in:
parent
5e14dbc15e
commit
dc186082a7
6 changed files with 54 additions and 19 deletions
|
|
@ -41,8 +41,7 @@ typedef struct _QUEUED_AUDIO_PACKET {
|
|||
} QUEUED_AUDIO_PACKET, *PQUEUED_AUDIO_PACKET;
|
||||
|
||||
static void AudioPingThreadProc(void* context) {
|
||||
// Ping in ASCII
|
||||
char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
char legacyPingData[] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
LC_SOCKADDR saddr;
|
||||
|
||||
LC_ASSERT(AudioPortNumber != 0);
|
||||
|
|
@ -50,19 +49,20 @@ static void AudioPingThreadProc(void* context) {
|
|||
memcpy(&saddr, &RemoteAddr, sizeof(saddr));
|
||||
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)) {
|
||||
// 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.
|
||||
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
||||
if (AudioPingPayload.payload[0] != 0) {
|
||||
pingCount++;
|
||||
AudioPingPayload.sequenceNumber = BE32(pingCount);
|
||||
|
||||
if (firstReceiveTime == 0 && isSocketReadable(rtpSocket)) {
|
||||
// Remember the time when we got our first incoming audio packet.
|
||||
// We will need to adjust for the delay between this event and
|
||||
// when the real receive thread is ready to avoid falling behind.
|
||||
firstReceiveTime = PltGetMillis();
|
||||
sendto(rtpSocket, (char*)&AudioPingPayload, sizeof(AudioPingPayload), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
||||
}
|
||||
else {
|
||||
sendto(rtpSocket, legacyPingData, sizeof(legacyPingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
||||
}
|
||||
|
||||
PltSleepMsInterruptible(&udpPingThread, 500);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ uint16_t RtspPortNumber;
|
|||
uint16_t ControlPortNumber;
|
||||
uint16_t AudioPortNumber;
|
||||
uint16_t VideoPortNumber;
|
||||
SS_PING AudioPingPayload;
|
||||
SS_PING VideoPingPayload;
|
||||
|
||||
// Connection stages
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ extern uint16_t ControlPortNumber;
|
|||
extern uint16_t AudioPortNumber;
|
||||
extern uint16_t VideoPortNumber;
|
||||
|
||||
extern SS_PING AudioPingPayload;
|
||||
extern SS_PING VideoPingPayload;
|
||||
|
||||
#ifndef UINT24_MAX
|
||||
#define UINT24_MAX 0xFFFFFF
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -893,6 +893,7 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
|
|||
{
|
||||
RTSP_MESSAGE response;
|
||||
char* sessionId;
|
||||
char* pingPayload;
|
||||
int error = -1;
|
||||
|
||||
if (!setupStream(&response,
|
||||
|
|
@ -922,6 +923,13 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
|
|||
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.
|
||||
// NB: This is needed because audio stream init happens before RTSP,
|
||||
// which is not the case for the video stream.
|
||||
|
|
@ -955,6 +963,7 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
|
|||
{
|
||||
RTSP_MESSAGE response;
|
||||
int error = -1;
|
||||
char* pingPayload;
|
||||
|
||||
if (!setupStream(&response,
|
||||
AppVersionQuad[0] >= 5 ? "streamid=video/0/0" : "streamid=video",
|
||||
|
|
@ -971,6 +980,13 @@ int performRtspHandshake(PSERVER_INFORMATION serverInfo) {
|
|||
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
|
||||
LC_ASSERT(VideoPortNumber == 0);
|
||||
if (!parseServerPortFromTransport(&response, &VideoPortNumber)) {
|
||||
|
|
|
|||
|
|
@ -36,4 +36,9 @@ typedef struct _RTP_PACKET {
|
|||
uint32_t ssrc;
|
||||
} RTP_PACKET, *PRTP_PACKET;
|
||||
|
||||
typedef struct _SS_PING {
|
||||
char payload[16];
|
||||
uint32_t sequenceNumber;
|
||||
} SS_PING, *PSS_PING;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ void destroyVideoStream(void) {
|
|||
|
||||
// UDP Ping proc
|
||||
static void VideoPingThreadProc(void* context) {
|
||||
char pingData[] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
char legacyPingData[] = { 0x50, 0x49, 0x4E, 0x47 };
|
||||
LC_SOCKADDR saddr;
|
||||
|
||||
LC_ASSERT(VideoPortNumber != 0);
|
||||
|
|
@ -51,12 +51,21 @@ static void VideoPingThreadProc(void* context) {
|
|||
memcpy(&saddr, &RemoteAddr, sizeof(saddr));
|
||||
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)) {
|
||||
// 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.
|
||||
sendto(rtpSocket, pingData, sizeof(pingData), 0, (struct sockaddr*)&saddr, RemoteAddrLen);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue