From 5a97188197177142edffd9c4a90863fbe700bece Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 17 Aug 2014 19:09:06 -0700 Subject: [PATCH] Initial RTSP implementation for GFE 2.1.1 --- limelight-common/RtspConnection.c | 348 ++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 12 deletions(-) diff --git a/limelight-common/RtspConnection.c b/limelight-common/RtspConnection.c index 74b7993..ef06a41 100644 --- a/limelight-common/RtspConnection.c +++ b/limelight-common/RtspConnection.c @@ -1,26 +1,94 @@ #include "Limelight-internal.h" +#include "Rtsp.h" static SOCKET sock = INVALID_SOCKET; +static IP_ADDRESS remoteAddr; +static int currentSeqNumber = 1; +static char* rtspTargetUrl; +static char sessionIdString[16]; + +// GFE 2.1.1 +#define RTSP_CLIENT_VERSION 10 +#define RTSP_CLIENT_VERSION_S "10" #define RTSP_MAX_RESP_SIZE 1024 -// FIXME defs -static void* transactRtspMessage(IP_ADDRESS addr, void* message) { - int err; +static POPTION_ITEM createOptionItem(char* option, char* content) +{ + POPTION_ITEM item = malloc(sizeof(*item)); + if (item == NULL) { + return NULL; + } + + item->option = malloc(strlen(option) + 1); + if (item->option == NULL) { + free(item); + return NULL; + } + + strcpy(item->option, option); + + item->content = malloc(strlen(content) + 1); + if (item->content == NULL) { + free(item->option); + free(item); + return NULL; + } + + strcpy(item->content, content); + + item->next = NULL; + item->flags = FLAG_ALLOCATED_OPTION_FIELDS; + + return item; +} + +static int addOption(PRTSP_MESSAGE msg, char* option, char* content) +{ + POPTION_ITEM item = createOptionItem(option, content); + if (item == NULL) { + return 0; + } + + insertOption(&msg->options, item); + msg->flags |= FLAG_ALLOCATED_OPTION_ITEMS; + + return 1; +} + +static int initializeRtspRequest(PRTSP_MESSAGE msg, char* command, char* target) +{ + createRtspRequest(msg, NULL, 0, command, target, "RTSP/1.0", + currentSeqNumber++, NULL, NULL); + + if (!addOption(msg, "X-GS-ClientVersion", RTSP_CLIENT_VERSION_S)) { + freeMessage(msg); + return 0; + } + + return 1; +} + +/* Returns 1 on success, 0 otherwise */ +static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response) { + int err, ret = 0; char responseBuffer[RTSP_MAX_RESP_SIZE]; int offset; - void* responseMsg = NULL; - char* serializedMessage; + PRTSP_MESSAGE responseMsg = NULL; + char* serializedMessage = NULL; int messageLen; - sock = connectTcpSocket(addr, 48010); + sock = connectTcpSocket(remoteAddr, 48010); if (sock == INVALID_SOCKET) { - return NULL; + return ret; } enableNoDelay(sock); - serializedMessage = NULL; // FIXME - messageLen = 0; // FIXME + serializedMessage = serializeRtspMessage(request, &messageLen); + if (serializedMessage == NULL) { + closesocket(sock); + return ret; + } // Send our message err = send(sock, serializedMessage, messageLen, 0); @@ -40,17 +108,27 @@ static void* transactRtspMessage(IP_ADDRESS addr, void* message) { // Warn if the RTSP message is too big if (offset == RTSP_MAX_RESP_SIZE) { - Limelog("RTSP message too long"); + Limelog("RTSP message too long\n"); goto Exit; } } - responseMsg = NULL; // FIXME + if (parseRtspMessage(response, responseBuffer) == RTSP_ERROR_SUCCESS) { + // Successfully parsed response + ret = 1; + } + else { + Limelog("Failed to parse RTSP response\n"); + } Exit: + if (serializedMessage != NULL) { + free(serializedMessage); + } + closesocket(sock); sock = INVALID_SOCKET; - return responseMsg; + return ret; } void terminateRtspHandshake(void) { @@ -60,6 +138,252 @@ void terminateRtspHandshake(void) { } } +static int requestOptions(PRTSP_MESSAGE response) { + RTSP_MESSAGE request; + int ret; + + ret = initializeRtspRequest(&request, "OPTIONS", rtspTargetUrl); + if (ret != 0) { + ret = transactRtspMessage(&request, response); + freeMessage(&request); + } + + return ret; +} + +static int requestDescribe(PRTSP_MESSAGE response) { + RTSP_MESSAGE request; + int ret; + + ret = initializeRtspRequest(&request, "DESCRIBE", rtspTargetUrl); + if (ret != 0) { + if (addOption(&request, "Accept", + "application/sdp") && + addOption(&request, "If-Modified-Since", + "Thu, 01 Jan 1970 00:00:00 GMT")) { + ret = transactRtspMessage(&request, response); + } + else { + ret = 0; + } + freeMessage(&request); + } + + return ret; +} + +static int setupStream(PRTSP_MESSAGE response, char* target) { + RTSP_MESSAGE request; + int ret; + + ret = initializeRtspRequest(&request, "SETUP", target); + if (ret != 0) { + if (sessionIdString[0] != 0) { + if (!addOption(&request, "Session", sessionIdString)) { + ret = 0; + goto FreeMessage; + } + } + + if (addOption(&request, "Transport", " ") && + addOption(&request, "If-Modified-Since", + "Thu, 01 Jan 1970 00:00:00 GMT")) { + ret = transactRtspMessage(&request, response); + } + else { + ret = 0; + } + + FreeMessage: + freeMessage(&request); + } + + return ret; +} + +static int playStream(PRTSP_MESSAGE response, char* target) { + RTSP_MESSAGE request; + int ret; + + ret = initializeRtspRequest(&request, "PLAY", target); + if (ret != 0) { + if (addOption(&request, "Session", sessionIdString)) { + ret = transactRtspMessage(&request, response); + } + else { + ret = 0; + } + freeMessage(&request); + } + + return ret; +} + +static int sendVideoAnnounce(PRTSP_MESSAGE response, PSTREAM_CONFIGURATION streamConfig) { + RTSP_MESSAGE request; + int ret; + int payloadLength; + char payloadLengthStr[16]; + struct in_addr sdpAddr; + + ret = initializeRtspRequest(&request, "ANNOUNCE", "streamid=video"); + if (ret != 0) { + ret = 0; + + if (!addOption(&request, "Session", sessionIdString) || + !addOption(&request, "Content-type", "application/sdp")) { + goto FreeMessage; + } + + sdpAddr.S_un.S_addr = remoteAddr; + request.payload = getSdpPayloadForStreamConfig(streamConfig, sdpAddr, &payloadLength); + if (request.payload == NULL) { + goto FreeMessage; + } + request.flags |= FLAG_ALLOCATED_PAYLOAD; + + sprintf(payloadLengthStr, "%d", payloadLength); + if (!addOption(&request, "Content-length", payloadLengthStr)) { + goto FreeMessage; + } + + ret = transactRtspMessage(&request, response); + + FreeMessage: + freeMessage(&request); + } + + return ret; +} + int performRtspHandshake(IP_ADDRESS addr, PSTREAM_CONFIGURATION streamConfigPtr) { + remoteAddr = addr; + + { + RTSP_MESSAGE response; + + if (!requestOptions(&response)) { + Limelog("RTSP OPTIONS request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP OPTIONS request failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + + if (!requestDescribe(&response)) { + Limelog("RTSP DESCRIBE request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP DESCRIBE request failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + char* sessionId; + + if (!setupStream(&response, "streamid=audio")) { + Limelog("RTSP SETUP streamid=audio request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP SETUP streamid=audio request failed: %d\n", + response.message.response.statusCode); + return -1; + } + + sessionId = getOptionContent(response.options, "Session"); + if (sessionId == NULL) { + Limelog("RTSP SETUP streamid=audio is missing session attribute"); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + + if (!setupStream(&response, "streamid=video")) { + Limelog("RTSP SETUP streamid=video request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP SETUP streamid=video request failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + + if (!sendVideoAnnounce(&response, streamConfigPtr)) { + Limelog("RTSP ANNOUNCE request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP ANNOUNCE request failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + + if (!playStream(&response, "streamid=video")) { + Limelog("RTSP PLAY streamid=video request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP PLAY streamid=video failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + + { + RTSP_MESSAGE response; + + if (!playStream(&response, "streamid=audio")) { + Limelog("RTSP PLAY streamid=audio request failed\n"); + return -1; + } + + if (response.message.response.statusCode != 200) { + Limelog("RTSP PLAY streamid=audio failed: %d\n", + response.message.response.statusCode); + return -1; + } + + freeMessage(&response); + } + return 0; } \ No newline at end of file