mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-17 17:05:50 +00:00
Dynamically determine audio, video, and control ports from RTSP DESCRIBE response
This commit is contained in:
parent
2660a05084
commit
56ccd99cc7
@ -14,6 +14,7 @@ static uint32_t avRiKeyId;
|
||||
|
||||
static unsigned short lastSeq;
|
||||
|
||||
static bool pingThreadStarted;
|
||||
static bool receivedDataFromPeer;
|
||||
static uint64_t firstReceiveTime;
|
||||
|
||||
@ -22,8 +23,6 @@ static uint64_t firstReceiveTime;
|
||||
static uint8_t opusHeaderByte;
|
||||
#endif
|
||||
|
||||
#define RTP_PORT 48000
|
||||
|
||||
#define MAX_PACKET_SIZE 1400
|
||||
|
||||
// This is much larger than we should typically have buffered, but
|
||||
@ -47,7 +46,7 @@ static void AudioPingThreadProc(void* context) {
|
||||
LC_SOCKADDR saddr;
|
||||
|
||||
memcpy(&saddr, &RemoteAddr, sizeof(saddr));
|
||||
SET_PORT(&saddr, RTP_PORT);
|
||||
SET_PORT(&saddr, AudioPortNumber);
|
||||
|
||||
// Send PING every 500 milliseconds
|
||||
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
||||
@ -74,6 +73,7 @@ int initializeAudioStream(void) {
|
||||
RtpaInitializeQueue(&rtpAudioQueue);
|
||||
lastSeq = 0;
|
||||
receivedDataFromPeer = false;
|
||||
pingThreadStarted = false;
|
||||
firstReceiveTime = 0;
|
||||
audioDecryptionCtx = PltCreateCryptoContext();
|
||||
#ifdef LC_DEBUG
|
||||
@ -91,15 +91,23 @@ int initializeAudioStream(void) {
|
||||
return LastSocketFail();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is called when the RTSP DESCRIBE message is parsed and the audio port
|
||||
// number is parsed out of it. Alternatively, it's also called if parsing fails
|
||||
// and will use the well known audio port instead.
|
||||
int notifyAudioPortNegotiationComplete(void) {
|
||||
LC_ASSERT(!pingThreadStarted);
|
||||
|
||||
// We may receive audio before our threads are started, but that's okay. We'll
|
||||
// drop the first 1 second of audio packets to catch up with the backlog.
|
||||
int err = PltCreateThread("AudioPing", AudioPingThreadProc, NULL, &udpPingThread);
|
||||
if (err != 0) {
|
||||
closeSocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
return err;
|
||||
}
|
||||
|
||||
pingThreadStarted = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -119,9 +127,11 @@ static void freePacketList(PLINKED_BLOCKING_QUEUE_ENTRY entry) {
|
||||
// Tear down the audio stream once we're done with it
|
||||
void destroyAudioStream(void) {
|
||||
if (rtpSocket != INVALID_SOCKET) {
|
||||
if (pingThreadStarted) {
|
||||
PltInterruptThread(&udpPingThread);
|
||||
PltJoinThread(&udpPingThread);
|
||||
PltCloseThread(&udpPingThread);
|
||||
}
|
||||
|
||||
closeSocket(rtpSocket);
|
||||
rtpSocket = INVALID_SOCKET;
|
||||
|
@ -24,6 +24,10 @@ OPUS_MULTISTREAM_CONFIGURATION HighQualityOpusConfig;
|
||||
int OriginalVideoBitrate;
|
||||
int AudioPacketDuration;
|
||||
bool AudioEncryptionEnabled;
|
||||
uint16_t RtspPortNumber;
|
||||
uint16_t ControlPortNumber;
|
||||
uint16_t AudioPortNumber;
|
||||
uint16_t VideoPortNumber;
|
||||
|
||||
// Connection stages
|
||||
static const char* stageNames[STAGE_MAX] = {
|
||||
@ -208,6 +212,12 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre
|
||||
OriginalVideoBitrate = streamConfig->bitrate;
|
||||
RemoteAddrString = strdup(serverInfo->address);
|
||||
|
||||
// Configure default ports
|
||||
VideoPortNumber = 47998;
|
||||
ControlPortNumber = 47999;
|
||||
AudioPortNumber = 48000;
|
||||
RtspPortNumber = 48010; // TODO: Parse this out of RTSP session URL
|
||||
|
||||
alreadyTerminated = false;
|
||||
ConnectionInterrupted = false;
|
||||
|
||||
|
@ -1143,7 +1143,7 @@ int startControlStream(void) {
|
||||
ENetEvent event;
|
||||
|
||||
enet_address_set_address(&address, (struct sockaddr *)&RemoteAddr, RemoteAddrLen);
|
||||
enet_address_set_port(&address, 47999);
|
||||
enet_address_set_port(&address, ControlPortNumber);
|
||||
|
||||
// Create a client that can use 1 outgoing connection and 1 channel
|
||||
client = enet_host_create(address.address.ss_family, NULL, 1, 1, 0, 0);
|
||||
@ -1166,7 +1166,7 @@ int startControlStream(void) {
|
||||
// Wait for the connect to complete
|
||||
if (serviceEnetHost(client, &event, CONTROL_STREAM_TIMEOUT_SEC * 1000) <= 0 ||
|
||||
event.type != ENET_EVENT_TYPE_CONNECT) {
|
||||
Limelog("Failed to connect to UDP port 47999\n");
|
||||
Limelog("Failed to connect to UDP port %u\n", ControlPortNumber);
|
||||
stopping = true;
|
||||
enet_peer_reset(peer);
|
||||
peer = NULL;
|
||||
@ -1182,6 +1182,7 @@ int startControlStream(void) {
|
||||
enet_peer_timeout(peer, 2, 10000, 10000);
|
||||
}
|
||||
else {
|
||||
// NB: Do NOT use ControlPortNumber here. 47995 is correct for these old versions.
|
||||
ctlSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen,
|
||||
47995, CONTROL_STREAM_TIMEOUT_SEC);
|
||||
if (ctlSock == INVALID_SOCKET) {
|
||||
|
@ -32,6 +32,11 @@ extern int OriginalVideoBitrate;
|
||||
extern int AudioPacketDuration;
|
||||
extern bool AudioEncryptionEnabled;
|
||||
|
||||
extern uint16_t RtspPortNumber;
|
||||
extern uint16_t ControlPortNumber;
|
||||
extern uint16_t AudioPortNumber;
|
||||
extern uint16_t VideoPortNumber;
|
||||
|
||||
#ifndef UINT24_MAX
|
||||
#define UINT24_MAX 0xFFFFFF
|
||||
#endif
|
||||
@ -100,6 +105,7 @@ int startVideoStream(void* rendererContext, int drFlags);
|
||||
void stopVideoStream(void);
|
||||
|
||||
int initializeAudioStream(void);
|
||||
int notifyAudioPortNegotiationComplete(void);
|
||||
void destroyAudioStream(void);
|
||||
int startAudioStream(void* audioContext, int arFlags);
|
||||
void stopAudioStream(void);
|
||||
|
@ -228,7 +228,7 @@ static bool transactRtspMessageTcp(PRTSP_MESSAGE request, PRTSP_MESSAGE response
|
||||
// returns HTTP 200 OK for the /launch request before the RTSP handshake port
|
||||
// is listening.
|
||||
do {
|
||||
sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, 48010, RTSP_TIMEOUT_SEC);
|
||||
sock = connectTcpSocket(&RemoteAddr, RemoteAddrLen, RtspPortNumber, RTSP_TIMEOUT_SEC);
|
||||
if (sock == INVALID_SOCKET) {
|
||||
*error = LastSocketError();
|
||||
if (*error == ECONNREFUSED) {
|
||||
@ -525,6 +525,57 @@ static int parseOpusConfigFromParamString(char* paramStr, int channelCount, POPU
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool parseMediaEntry(PRTSP_MESSAGE response, const char* mediaType, const char* transport, uint16_t* port) {
|
||||
char paramsPrefix[128];
|
||||
char paramsSuffix[128];
|
||||
char* paramStart;
|
||||
|
||||
sprintf(paramsPrefix, "m=%s ", mediaType);
|
||||
sprintf(paramsSuffix, " %s", transport);
|
||||
|
||||
// Look for the next match
|
||||
paramStart = response->payload;
|
||||
while ((paramStart = strstr(paramStart, paramsPrefix)) != NULL) {
|
||||
// Skip the prefix
|
||||
paramStart += strlen(paramsPrefix);
|
||||
|
||||
// The first part should be the port number
|
||||
char* nextToken;
|
||||
long int rawPort = strtol(paramStart, &nextToken, 10);
|
||||
if (rawPort <= 0 || rawPort >= 65535) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip this entry if the transport isn't what we expect
|
||||
if (strncmp(nextToken, paramsSuffix, strlen(paramsSuffix)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This entry is a match
|
||||
*port = (uint16_t)rawPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
// No match for this media type and transport
|
||||
return false;
|
||||
}
|
||||
|
||||
static void parsePortConfigurations(PRTSP_MESSAGE response) {
|
||||
// Don't parse ports on very old GFE versions
|
||||
if (!APP_VERSION_AT_LEAST(7, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parseMediaEntry(response, "video", "RTP/AVP", &VideoPortNumber);
|
||||
parseMediaEntry(response, "audio", "RTP/AVP", &AudioPortNumber);
|
||||
|
||||
if (!parseMediaEntry(response, "application", "udp", &ControlPortNumber)) {
|
||||
if (!parseMediaEntry(response, "application", "udp_enc", &ControlPortNumber)) {
|
||||
parseMediaEntry(response, "application", "udp_ag", &ControlPortNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the Opus configuration from an RTSP DESCRIBE response
|
||||
static int parseOpusConfigurations(PRTSP_MESSAGE response) {
|
||||
HighQualitySurroundSupported = false;
|
||||
@ -644,7 +695,7 @@ int performRtspHandshake(void) {
|
||||
|
||||
// Initialize global state
|
||||
useEnet = (AppVersionQuad[0] >= 5) && (AppVersionQuad[0] <= 7) && (AppVersionQuad[2] < 404);
|
||||
sprintf(rtspTargetUrl, "rtsp%s://%s:48010", useEnet ? "ru" : "", urlAddr);
|
||||
sprintf(rtspTargetUrl, "rtsp%s://%s:%u", useEnet ? "ru" : "", urlAddr, RtspPortNumber);
|
||||
currentSeqNumber = 1;
|
||||
hasSessionId = false;
|
||||
controlStreamId = APP_VERSION_AT_LEAST(7, 1, 431) ? "streamid=control/13/0" : "streamid=control/1/0";
|
||||
@ -676,7 +727,7 @@ int performRtspHandshake(void) {
|
||||
ENetEvent event;
|
||||
|
||||
enet_address_set_address(&address, (struct sockaddr *)&RemoteAddr, RemoteAddrLen);
|
||||
enet_address_set_port(&address, 48010);
|
||||
enet_address_set_port(&address, RtspPortNumber);
|
||||
|
||||
// Create a client that can use 1 outgoing connection and 1 channel
|
||||
client = enet_host_create(RemoteAddr.ss_family, NULL, 1, 1, 0, 0);
|
||||
@ -695,7 +746,7 @@ int performRtspHandshake(void) {
|
||||
// Wait for the connect to complete
|
||||
if (serviceEnetHost(client, &event, RTSP_TIMEOUT_SEC * 1000) <= 0 ||
|
||||
event.type != ENET_EVENT_TYPE_CONNECT) {
|
||||
Limelog("RTSP: Failed to connect to UDP port 48010\n");
|
||||
Limelog("RTSP: Failed to connect to UDP port %u\n", RtspPortNumber);
|
||||
enet_peer_reset(peer);
|
||||
peer = NULL;
|
||||
enet_host_destroy(client);
|
||||
@ -773,6 +824,14 @@ int performRtspHandshake(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse audio, video, and control ports out of the RTSP DESCRIBE response.
|
||||
parsePortConfigurations(&response);
|
||||
|
||||
// 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.
|
||||
notifyAudioPortNegotiationComplete();
|
||||
|
||||
// Parse the Opus surround parameters out of the RTSP DESCRIBE response.
|
||||
ret = parseOpusConfigurations(&response);
|
||||
if (ret != 0) {
|
||||
|
@ -3,7 +3,6 @@
|
||||
#define FIRST_FRAME_MAX 1500
|
||||
#define FIRST_FRAME_TIMEOUT_SEC 10
|
||||
|
||||
#define RTP_PORT 47998
|
||||
#define FIRST_FRAME_PORT 47996
|
||||
|
||||
#define RTP_RECV_BUFFER (512 * 1024)
|
||||
@ -48,7 +47,7 @@ static void VideoPingThreadProc(void* context) {
|
||||
LC_SOCKADDR saddr;
|
||||
|
||||
memcpy(&saddr, &RemoteAddr, sizeof(saddr));
|
||||
SET_PORT(&saddr, RTP_PORT);
|
||||
SET_PORT(&saddr, VideoPortNumber);
|
||||
|
||||
while (!PltIsThreadInterrupted(&udpPingThread)) {
|
||||
// We do not check for errors here. Socket errors will be handled
|
||||
|
Loading…
x
Reference in New Issue
Block a user