mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-18 01:15:46 +00:00
Implement RTSP over ENet for GFE 2.11
This commit is contained in:
parent
c44cdffdd5
commit
31ce0f5c87
@ -8,6 +8,7 @@ static PLT_THREAD terminationCallbackThread;
|
|||||||
static long terminationCallbackErrorCode;
|
static long terminationCallbackErrorCode;
|
||||||
|
|
||||||
// Common globals
|
// Common globals
|
||||||
|
char* RemoteAddrString;
|
||||||
struct sockaddr_storage RemoteAddr;
|
struct sockaddr_storage RemoteAddr;
|
||||||
SOCKADDR_LEN RemoteAddrLen;
|
SOCKADDR_LEN RemoteAddrLen;
|
||||||
int ServerMajorVersion;
|
int ServerMajorVersion;
|
||||||
@ -108,6 +109,11 @@ void LiStopConnection(void) {
|
|||||||
Limelog("done\n");
|
Limelog("done\n");
|
||||||
}
|
}
|
||||||
LC_ASSERT(stage == STAGE_NONE);
|
LC_ASSERT(stage == STAGE_NONE);
|
||||||
|
|
||||||
|
if (RemoteAddrString != NULL) {
|
||||||
|
free(RemoteAddrString);
|
||||||
|
RemoteAddrString = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void terminationCallbackThreadFunc(void* context)
|
static void terminationCallbackThreadFunc(void* context)
|
||||||
@ -181,6 +187,7 @@ int LiStartConnection(const char* host, PSTREAM_CONFIGURATION streamConfig, PCON
|
|||||||
NegotiatedVideoFormat = 0;
|
NegotiatedVideoFormat = 0;
|
||||||
ServerMajorVersion = _serverMajorVersion;
|
ServerMajorVersion = _serverMajorVersion;
|
||||||
memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));
|
memcpy(&StreamConfig, streamConfig, sizeof(StreamConfig));
|
||||||
|
RemoteAddrString = strdup(host);
|
||||||
|
|
||||||
// Replace missing callbacks with placeholders
|
// Replace missing callbacks with placeholders
|
||||||
fixupMissingCallbacks(&drCallbacks, &arCallbacks, &clCallbacks);
|
fixupMissingCallbacks(&drCallbacks, &arCallbacks, &clCallbacks);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "Video.h"
|
#include "Video.h"
|
||||||
|
|
||||||
// Common globals
|
// Common globals
|
||||||
|
extern char* RemoteAddrString;
|
||||||
extern struct sockaddr_storage RemoteAddr;
|
extern struct sockaddr_storage RemoteAddr;
|
||||||
extern SOCKADDR_LEN RemoteAddrLen;
|
extern SOCKADDR_LEN RemoteAddrLen;
|
||||||
extern int ServerMajorVersion;
|
extern int ServerMajorVersion;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "PlatformThreads.h"
|
#include "PlatformThreads.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
|
#include <enet/enet.h>
|
||||||
|
|
||||||
int initializePlatformSockets(void);
|
int initializePlatformSockets(void);
|
||||||
void cleanupPlatformSockets(void);
|
void cleanupPlatformSockets(void);
|
||||||
|
|
||||||
@ -233,6 +235,11 @@ int initializePlatform(void) {
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = initializePlatformSockets();
|
err = initializePlatformSockets();
|
||||||
|
if (err != 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enet_initialize();
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -242,6 +249,8 @@ int initializePlatform(void) {
|
|||||||
|
|
||||||
void cleanupPlatform(void) {
|
void cleanupPlatform(void) {
|
||||||
cleanupPlatformSockets();
|
cleanupPlatformSockets();
|
||||||
|
|
||||||
|
enet_deinitialize();
|
||||||
|
|
||||||
LC_ASSERT(running_threads == 0);
|
LC_ASSERT(running_threads == 0);
|
||||||
}
|
}
|
@ -1,10 +1,11 @@
|
|||||||
#include "Limelight-internal.h"
|
#include "Limelight-internal.h"
|
||||||
#include "Rtsp.h"
|
#include "Rtsp.h"
|
||||||
|
|
||||||
|
#include <enet/enet.h>
|
||||||
|
|
||||||
#define RTSP_MAX_RESP_SIZE 32768
|
#define RTSP_MAX_RESP_SIZE 32768
|
||||||
#define RTSP_TIMEOUT_SEC 10
|
#define RTSP_TIMEOUT_SEC 10
|
||||||
|
|
||||||
static SOCKET sock = INVALID_SOCKET;
|
|
||||||
static int currentSeqNumber;
|
static int currentSeqNumber;
|
||||||
static char rtspTargetUrl[256];
|
static char rtspTargetUrl[256];
|
||||||
static char sessionIdString[16];
|
static char sessionIdString[16];
|
||||||
@ -12,6 +13,10 @@ static int hasSessionId;
|
|||||||
static char responseBuffer[RTSP_MAX_RESP_SIZE];
|
static char responseBuffer[RTSP_MAX_RESP_SIZE];
|
||||||
static int rtspClientVersion;
|
static int rtspClientVersion;
|
||||||
|
|
||||||
|
static SOCKET sock = INVALID_SOCKET;
|
||||||
|
static ENetHost* client;
|
||||||
|
static ENetPeer* peer;
|
||||||
|
|
||||||
// Create RTSP Option
|
// Create RTSP Option
|
||||||
static POPTION_ITEM createOptionItem(char* option, char* content)
|
static POPTION_ITEM createOptionItem(char* option, char* content)
|
||||||
{
|
{
|
||||||
@ -78,8 +83,120 @@ static int initializeRtspRequest(PRTSP_MESSAGE msg, char* command, char* target)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send RTSP message and get response
|
// Send RTSP message and get response over ENet
|
||||||
static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int* error) {
|
static int transactRtspMessageEnet(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int expectingPayload, int* error) {
|
||||||
|
ENetEvent event;
|
||||||
|
char* serializedMessage;
|
||||||
|
int messageLen;
|
||||||
|
int offset;
|
||||||
|
ENetPacket* packet;
|
||||||
|
char* payload;
|
||||||
|
int payloadLength;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// We're going to handle the payload separately, so temporarily set the payload to NULL
|
||||||
|
payload = request->payload;
|
||||||
|
payloadLength = request->payloadLength;
|
||||||
|
request->payload = NULL;
|
||||||
|
request->payloadLength = 0;
|
||||||
|
|
||||||
|
// Serialize the RTSP message into a message buffer
|
||||||
|
serializedMessage = serializeRtspMessage(request, &messageLen);
|
||||||
|
if (serializedMessage == NULL) {
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the reliable packet that describes our outgoing message
|
||||||
|
packet = enet_packet_create(serializedMessage, messageLen, ENET_PACKET_FLAG_RELIABLE);
|
||||||
|
if (packet == NULL) {
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the message
|
||||||
|
enet_peer_send(peer, 0, packet);
|
||||||
|
enet_host_flush(client);
|
||||||
|
|
||||||
|
// If we have a payload to send, we'll need to send that separately
|
||||||
|
if (payload != NULL) {
|
||||||
|
packet = enet_packet_create(payload, payloadLength, ENET_PACKET_FLAG_RELIABLE);
|
||||||
|
if (packet == NULL) {
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the payload
|
||||||
|
enet_peer_send(peer, 0, packet);
|
||||||
|
enet_host_flush(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a reply
|
||||||
|
if (enet_host_service(client, &event, RTSP_TIMEOUT_SEC * 1000) <= 0 ||
|
||||||
|
event.type != ENET_EVENT_TYPE_RECEIVE) {
|
||||||
|
Limelog("Failed to receive RTSP reply\n");
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.packet->dataLength > RTSP_MAX_RESP_SIZE) {
|
||||||
|
Limelog("RTSP message too long\n");
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data out and destroy the packet
|
||||||
|
memcpy(responseBuffer, event.packet->data, event.packet->dataLength);
|
||||||
|
offset = event.packet->dataLength;
|
||||||
|
enet_packet_destroy(event.packet);
|
||||||
|
|
||||||
|
// Wait for the payload if we're expecting some
|
||||||
|
if (expectingPayload) {
|
||||||
|
// Only wait 1 second since the packets should be here immediately
|
||||||
|
// after the header.
|
||||||
|
if (enet_host_service(client, &event, 1000) <= 0 ||
|
||||||
|
event.type != ENET_EVENT_TYPE_RECEIVE) {
|
||||||
|
Limelog("Failed to receive RTSP reply payload\n");
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.packet->dataLength + offset > RTSP_MAX_RESP_SIZE) {
|
||||||
|
Limelog("RTSP message payload too long\n");
|
||||||
|
ret = 0;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the payload out to the end of the response buffer and destroy the packet
|
||||||
|
memcpy(&responseBuffer[offset], event.packet->data, event.packet->dataLength);
|
||||||
|
offset += event.packet->dataLength;
|
||||||
|
enet_packet_destroy(event.packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseRtspMessage(response, responseBuffer, offset) == RTSP_ERROR_SUCCESS) {
|
||||||
|
// Successfully parsed response
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Limelog("Failed to parse RTSP response\n");
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
// Swap back the payload pointer to avoid leaking memory later
|
||||||
|
request->payload = payload;
|
||||||
|
request->payloadLength = payloadLength;
|
||||||
|
|
||||||
|
// Free the serialized buffer
|
||||||
|
if (serializedMessage != NULL) {
|
||||||
|
free(serializedMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send RTSP message and get response over TCP
|
||||||
|
static int transactRtspMessageTcp(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int expectingPayload, int* error) {
|
||||||
SOCK_RET err;
|
SOCK_RET err;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int offset;
|
int offset;
|
||||||
@ -146,12 +263,32 @@ Exit:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int expectingPayload, int* error) {
|
||||||
|
// Gen 5+ does RTSP over ENet not TCP
|
||||||
|
if (ServerMajorVersion >= 5) {
|
||||||
|
return transactRtspMessageEnet(request, response, expectingPayload, error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return transactRtspMessageTcp(request, response, expectingPayload, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Terminate the RTSP Handshake process by shutting down the socket.
|
// Terminate the RTSP Handshake process by shutting down the socket.
|
||||||
// The thread waiting on RTSP will close the socket.
|
// The thread waiting on RTSP will close the socket.
|
||||||
void terminateRtspHandshake(void) {
|
void terminateRtspHandshake(void) {
|
||||||
if (sock != INVALID_SOCKET) {
|
if (sock != INVALID_SOCKET) {
|
||||||
shutdownTcpSocket(sock);
|
shutdownTcpSocket(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (peer != NULL) {
|
||||||
|
enet_peer_reset(peer);
|
||||||
|
peer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client != NULL) {
|
||||||
|
enet_host_destroy(client);
|
||||||
|
client = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send RTSP OPTIONS request
|
// Send RTSP OPTIONS request
|
||||||
@ -163,7 +300,7 @@ static int requestOptions(PRTSP_MESSAGE response, int* error) {
|
|||||||
|
|
||||||
ret = initializeRtspRequest(&request, "OPTIONS", rtspTargetUrl);
|
ret = initializeRtspRequest(&request, "OPTIONS", rtspTargetUrl);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
ret = transactRtspMessage(&request, response, error);
|
ret = transactRtspMessage(&request, response, 0, error);
|
||||||
freeMessage(&request);
|
freeMessage(&request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +320,7 @@ static int requestDescribe(PRTSP_MESSAGE response, int* error) {
|
|||||||
"application/sdp") &&
|
"application/sdp") &&
|
||||||
addOption(&request, "If-Modified-Since",
|
addOption(&request, "If-Modified-Since",
|
||||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||||
ret = transactRtspMessage(&request, response, error);
|
ret = transactRtspMessage(&request, response, 1, error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -213,7 +350,7 @@ static int setupStream(PRTSP_MESSAGE response, char* target, int* error) {
|
|||||||
if (addOption(&request, "Transport", " ") &&
|
if (addOption(&request, "Transport", " ") &&
|
||||||
addOption(&request, "If-Modified-Since",
|
addOption(&request, "If-Modified-Since",
|
||||||
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
"Thu, 01 Jan 1970 00:00:00 GMT")) {
|
||||||
ret = transactRtspMessage(&request, response, error);
|
ret = transactRtspMessage(&request, response, 0, error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -236,7 +373,7 @@ static int playStream(PRTSP_MESSAGE response, char* target, int* error) {
|
|||||||
ret = initializeRtspRequest(&request, "PLAY", target);
|
ret = initializeRtspRequest(&request, "PLAY", target);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
if (addOption(&request, "Session", sessionIdString)) {
|
if (addOption(&request, "Session", sessionIdString)) {
|
||||||
ret = transactRtspMessage(&request, response, error);
|
ret = transactRtspMessage(&request, response, 0, error);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -277,7 +414,7 @@ static int sendVideoAnnounce(PRTSP_MESSAGE response, int* error) {
|
|||||||
goto FreeMessage;
|
goto FreeMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = transactRtspMessage(&request, response, error);
|
ret = transactRtspMessage(&request, response, 0, error);
|
||||||
|
|
||||||
FreeMessage:
|
FreeMessage:
|
||||||
freeMessage(&request);
|
freeMessage(&request);
|
||||||
@ -305,6 +442,37 @@ int performRtspHandshake(void) {
|
|||||||
else {
|
else {
|
||||||
rtspClientVersion = 12;
|
rtspClientVersion = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gen 5 servers use ENet to do the RTSP handshake
|
||||||
|
if (ServerMajorVersion >= 5) {
|
||||||
|
ENetAddress address;
|
||||||
|
ENetEvent event;
|
||||||
|
|
||||||
|
// Create a client that can use 1 outgoing connection and 1 channel
|
||||||
|
client = enet_host_create(NULL, 1, 1, 0, 0);
|
||||||
|
if (client == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enet_address_set_host(&address, RemoteAddrString);
|
||||||
|
address.port = 48010;
|
||||||
|
|
||||||
|
// Connect to the host
|
||||||
|
peer = enet_host_connect(client, &address, 1, 0);
|
||||||
|
if (peer == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the connect to complete
|
||||||
|
if (enet_host_service(client, &event, RTSP_TIMEOUT_SEC * 1000) <= 0 ||
|
||||||
|
event.type != ENET_EVENT_TYPE_CONNECT) {
|
||||||
|
Limelog("RTSP: Failed to connect to UDP port 48010\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the connect verify ACK is sent immediately
|
||||||
|
enet_host_flush(client);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
RTSP_MESSAGE response;
|
RTSP_MESSAGE response;
|
||||||
@ -454,6 +622,19 @@ int performRtspHandshake(void) {
|
|||||||
|
|
||||||
freeMessage(&response);
|
freeMessage(&response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cleanup the ENet stuff
|
||||||
|
if (ServerMajorVersion >= 5) {
|
||||||
|
if (peer != NULL) {
|
||||||
|
enet_peer_reset(peer);
|
||||||
|
peer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client != NULL) {
|
||||||
|
enet_host_destroy(client);
|
||||||
|
client = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,13 @@ int parseRtspMessage(PRTSP_MESSAGE msg, char* rtspMessage, int length) {
|
|||||||
endCheck = &token[0] + strlen(token) + 1;
|
endCheck = &token[0] + strlen(token) + 1;
|
||||||
|
|
||||||
// See if we've hit the end of the message. The first \r is missing because it's been tokenized
|
// See if we've hit the end of the message. The first \r is missing because it's been tokenized
|
||||||
if (startsWith(endCheck, "\n\r\n")) {
|
if (startsWith(endCheck, "\n") && endCheck[1] == '\0') {
|
||||||
|
// RTSP over ENet doesn't always have the second CRLF for some reason
|
||||||
|
messageEnded = 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (startsWith(endCheck, "\n\r\n")) {
|
||||||
// We've encountered the end of the message - mark it thus
|
// We've encountered the end of the message - mark it thus
|
||||||
messageEnded = 1;
|
messageEnded = 1;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user