Add backwards compatibility for GFE 2.1.x

This commit is contained in:
Cameron Gutman 2015-02-01 20:16:08 -05:00
parent 7883ce67cd
commit 0fa1a02e0a
7 changed files with 197 additions and 43 deletions

View File

@ -8,6 +8,8 @@ static CONNECTION_LISTENER_CALLBACKS originalCallbacks;
// This is used for debug prints so it's not declared static
PLATFORM_CALLBACKS platformCallbacks;
int serverMajorVersion;
static int alreadyTerminated;
/* Connection stages */
@ -148,8 +150,10 @@ void LiCompleteThreadStart(void)
/* Starts the connection to the streaming machine */
int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, PPLATFORM_CALLBACKS plCallbacks,
void* renderContext, int drFlags) {
void* renderContext, int drFlags, int _serverMajorVersion) {
int err;
serverMajorVersion = _serverMajorVersion;
memcpy(&originalCallbacks, clCallbacks, sizeof(originalCallbacks));
memcpy(&platformCallbacks, plCallbacks, sizeof(platformCallbacks));

View File

@ -20,22 +20,59 @@ static PCONNECTION_LISTENER_CALLBACKS listenerCallbacks;
static int lossCountSinceLastReport = 0;
static long currentFrame = 0;
#define PTYPE_START_STREAM_A 0x0606
#define PPAYLEN_START_STREAM_A 2
static const char PPAYLOAD_START_STREAM_A[PPAYLEN_START_STREAM_A] = { 0, 0 };
#define IDX_START_A 0
#define IDX_START_B 1
#define IDX_RESYNC 2
#define IDX_LOSS_STATS 3
#define PTYPE_START_STREAM_B 0x0609
#define PPAYLEN_START_STREAM_B 1
static const char PPAYLOAD_START_STREAM_B[PPAYLEN_START_STREAM_B] = { 0 };
static const short packetTypesGen3[] = {
0x140b, // Start A
0x1410, // Start B
0x1404, // Resync
0x140c, // Loss Stats
0x1417, // Frame Stats (unused)
};
static const short packetTypesGen4[] = {
0x0606, // Start A
0x0609, // Start B
0x0604, // Resync
0x060a, // Loss Stats
0x0611, // Frame Stats (unused)
};
#define PTYPE_RESYNC 0x0604
#define PPAYLEN_RESYNC 24
static const char startAGen3[] = {0};
static const int startBGen3[] = {0, 0, 0, 0xa};
#define PTYPE_LOSS_STATS 0x060a
#define PPAYLEN_LOSS_STATS 32
static const char startAGen4[] = {0, 0};
static const char startBGen4[] = {0};
#define PTYPE_FRAME_STATS 0x0611
#define PPAYLEN_FRAME_STATS 64
static const short payloadLengthsGen3[] = {
sizeof(startAGen3), // Start A
sizeof(startBGen3), // Start B
24, // Resync
32, // Loss Stats
64, // Frame Stats
};
static const short payloadLengthsGen4[] = {
sizeof(startAGen4), // Start A
sizeof(startBGen4), // Start B
24, // Resync
32, // Loss Stats
64, // Frame Stats
};
static const char* preconstructedPayloadsGen3[] = {
startAGen3,
(char*)startBGen3
};
static const char* preconstructedPayloadsGen4[] = {
startAGen4,
startBGen4
};
static short *packetTypes;
static short *payloadLengths;
static char **preconstructedPayloads;
#define LOSS_REPORT_INTERVAL_MS 50
@ -47,6 +84,17 @@ int initializeControlStream(IP_ADDRESS addr, PSTREAM_CONFIGURATION streamConfigP
host = addr;
listenerCallbacks = clCallbacks;
if (serverMajorVersion == 3) {
packetTypes = (short*)packetTypesGen3;
payloadLengths = (short*)payloadLengthsGen3;
preconstructedPayloads = (char**)preconstructedPayloadsGen3;
}
else {
packetTypes = (short*)packetTypesGen4;
payloadLengths = (short*)payloadLengthsGen4;
preconstructedPayloads = (char**)preconstructedPayloadsGen4;
}
return 0;
}
@ -156,12 +204,12 @@ static int sendMessageAndDiscardReply(short ptype, short paylen, const void* pay
}
static void lossStatsThreadFunc(void* context) {
char lossStatsPayload[PPAYLEN_LOSS_STATS];
char lossStatsPayload[payloadLengths[IDX_LOSS_STATS]];
BYTE_BUFFER byteBuffer;
while (!PltIsThreadInterrupted(&lossStatsThread)) {
// Construct the payload
BbInitializeWrappedBuffer(&byteBuffer, lossStatsPayload, 0, PPAYLEN_LOSS_STATS, BYTE_ORDER_LITTLE);
BbInitializeWrappedBuffer(&byteBuffer, lossStatsPayload, 0, payloadLengths[IDX_LOSS_STATS], BYTE_ORDER_LITTLE);
BbPutInt(&byteBuffer, lossCountSinceLastReport);
BbPutInt(&byteBuffer, LOSS_REPORT_INTERVAL_MS);
BbPutInt(&byteBuffer, 1000);
@ -171,8 +219,8 @@ static void lossStatsThreadFunc(void* context) {
BbPutInt(&byteBuffer, 0x14);
// Send the message (and don't expect a response)
if (!sendMessageAndForget(PTYPE_LOSS_STATS,
PPAYLEN_LOSS_STATS, lossStatsPayload)) {
if (!sendMessageAndForget(packetTypes[IDX_LOSS_STATS],
payloadLengths[IDX_LOSS_STATS], lossStatsPayload)) {
Limelog("Loss stats thread terminating #1\n");
listenerCallbacks->connectionTerminated(LastSocketError());
return;
@ -202,7 +250,7 @@ static void resyncThreadFunc(void* context) {
PltClearEvent(&resyncEvent);
// Send the resync request and read the response
if (!sendMessageAndDiscardReply(PTYPE_RESYNC, PPAYLEN_RESYNC, payload)) {
if (!sendMessageAndDiscardReply(packetTypes[IDX_RESYNC], payloadLengths[IDX_RESYNC], payload)) {
Limelog("Resync thread terminating #1\n");
listenerCallbacks->connectionTerminated(LastSocketError());
return;
@ -242,16 +290,16 @@ int startControlStream(void) {
enableNoDelay(ctlSock);
// Send START A
if (!sendMessageAndDiscardReply(PTYPE_START_STREAM_A,
PPAYLEN_START_STREAM_A,
PPAYLOAD_START_STREAM_A)) {
if (!sendMessageAndDiscardReply(packetTypes[IDX_START_A],
payloadLengths[IDX_START_A],
preconstructedPayloads[IDX_START_A])) {
return LastSocketError();
}
// Send START B
if (!sendMessageAndDiscardReply(PTYPE_START_STREAM_B,
PPAYLEN_START_STREAM_B,
PPAYLOAD_START_STREAM_B)) {
if (!sendMessageAndDiscardReply(packetTypes[IDX_START_B],
payloadLengths[IDX_START_B],
preconstructedPayloads[IDX_START_B])) {
return LastSocketError();
}

View File

@ -6,11 +6,10 @@
#include "PlatformThreads.h"
#include "Video.h"
/* GFE 2.2.2+ RTSP/SDP version code */
#define RTSP_CLIENT_VERSION 11
#define RTSP_CLIENT_VERSION_S "11"
extern int serverMajorVersion;
char* getSdpPayloadForStreamConfig(PSTREAM_CONFIGURATION streamConfig, struct in_addr targetAddress, int *length);
char* getSdpPayloadForStreamConfig(PSTREAM_CONFIGURATION streamConfig, struct in_addr targetAddress,
int rtspClientVersion, int *length);
int initializeControlStream(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks);
int startControlStream(void);

View File

@ -103,7 +103,7 @@ typedef struct _PLATFORM_CALLBACKS {
int LiStartConnection(IP_ADDRESS host, PSTREAM_CONFIGURATION streamConfig, PCONNECTION_LISTENER_CALLBACKS clCallbacks,
PDECODER_RENDERER_CALLBACKS drCallbacks, PAUDIO_RENDERER_CALLBACKS arCallbacks, PPLATFORM_CALLBACKS plCallbacks,
void* renderContext, int drFlags);
void* renderContext, int drFlags, int _serverMajorVersion);
void LiStopConnection(void);
const char* LiGetStageName(int stage);

View File

@ -10,6 +10,7 @@ static char rtspTargetUrl[256];
static char sessionIdString[16];
static int hasSessionId;
static char responseBuffer[RTSP_MAX_RESP_SIZE];
static int rtspClientVersion;
/* Create RTSP Option */
static POPTION_ITEM createOptionItem(char* option, char* content)
@ -60,14 +61,16 @@ static int addOption(PRTSP_MESSAGE msg, char* option, char* content)
static int initializeRtspRequest(PRTSP_MESSAGE msg, char* command, char* target)
{
char sequenceNumberStr[16];
char clientVersionStr[16];
// FIXME: Hacked CSeq attribute due to RTSP parser bug
createRtspRequest(msg, NULL, 0, command, target, "RTSP/1.0",
0, NULL, NULL, 0);
sprintf(sequenceNumberStr, "%d", currentSeqNumber++);
sprintf(clientVersionStr, "%d", rtspClientVersion);
if (!addOption(msg, "CSeq", sequenceNumberStr) ||
!addOption(msg, "X-GS-ClientVersion", RTSP_CLIENT_VERSION_S)) {
!addOption(msg, "X-GS-ClientVersion", clientVersionStr)) {
freeMessage(msg);
return 0;
}
@ -247,7 +250,8 @@ static int sendVideoAnnounce(PRTSP_MESSAGE response, PSTREAM_CONFIGURATION strea
}
memcpy(&sdpAddr, &remoteAddr, sizeof(remoteAddr));
request.payload = getSdpPayloadForStreamConfig(streamConfig, sdpAddr, &payloadLength);
request.payload = getSdpPayloadForStreamConfig(streamConfig, sdpAddr,
rtspClientVersion, &payloadLength);
if (request.payload == NULL) {
goto FreeMessage;
}
@ -278,6 +282,13 @@ int performRtspHandshake(IP_ADDRESS addr, PSTREAM_CONFIGURATION streamConfigPtr)
sprintf(rtspTargetUrl, "rtsp://%s", inet_ntoa(inaddr));
currentSeqNumber = 1;
hasSessionId = 0;
if (serverMajorVersion == 3) {
rtspClientVersion = 10;
}
else {
rtspClientVersion = 11;
}
{
RTSP_MESSAGE response;

View File

@ -88,6 +88,62 @@ static int addAttributeString(PSDP_OPTION *head, char* name, const char* payload
return addAttributeBinary(head, name, payload, (int)strlen(payload));
}
static int addGen3Options(PSDP_OPTION *head, char* addrStr) {
int payloadInt;
int err = 0;
err |= addAttributeString(head, "x-nv-general.serverAddress", addrStr);
payloadInt = htonl(0x42774141);
err |= addAttributeBinary(head,
"x-nv-general.featureFlags", &payloadInt, sizeof(payloadInt));
payloadInt = htonl(0x41514141);
err |= addAttributeBinary(head,
"x-nv-video[0].transferProtocol", &payloadInt, sizeof(payloadInt));
err |= addAttributeBinary(head,
"x-nv-video[1].transferProtocol", &payloadInt, sizeof(payloadInt));
err |= addAttributeBinary(head,
"x-nv-video[2].transferProtocol", &payloadInt, sizeof(payloadInt));
err |= addAttributeBinary(head,
"x-nv-video[3].transferProtocol", &payloadInt, sizeof(payloadInt));
payloadInt = htonl(0x42414141);
err |= addAttributeBinary(head,
"x-nv-video[0].rateControlMode", &payloadInt, sizeof(payloadInt));
payloadInt = htonl(0x42514141);
err |= addAttributeBinary(head,
"x-nv-video[1].rateControlMode", &payloadInt, sizeof(payloadInt));
err |= addAttributeBinary(head,
"x-nv-video[2].rateControlMode", &payloadInt, sizeof(payloadInt));
err |= addAttributeBinary(head,
"x-nv-video[3].rateControlMode", &payloadInt, sizeof(payloadInt));
err |= addAttributeString(head, "x-nv-vqos[0].bw.flags", "14083");
err |= addAttributeString(head, "x-nv-vqos[0].videoQosMaxConsecutiveDrops", "0");
err |= addAttributeString(head, "x-nv-vqos[1].videoQosMaxConsecutiveDrops", "0");
err |= addAttributeString(head, "x-nv-vqos[2].videoQosMaxConsecutiveDrops", "0");
err |= addAttributeString(head, "x-nv-vqos[3].videoQosMaxConsecutiveDrops", "0");
return err;
}
static int addGen4Options(PSDP_OPTION *head, char* addrStr) {
char payloadStr[92];
int err = 0;
sprintf(payloadStr, "rtsp://%s:48010", addrStr);
err |= addAttributeString(head, "x-nv-general.serverAddress", payloadStr);
err |= addAttributeString(head, "x-nv-video[0].rateControlMode", "4");
err |= addAttributeString(head, "x-nv-vqos[0].bw.flags", "51");
return err;
}
static PSDP_OPTION getAttributesList(PSTREAM_CONFIGURATION streamConfig, struct in_addr targetAddress) {
PSDP_OPTION optionHead;
char payloadStr[92];
@ -95,9 +151,6 @@ static PSDP_OPTION getAttributesList(PSTREAM_CONFIGURATION streamConfig, struct
optionHead = NULL;
err = 0;
sprintf(payloadStr, "rtsp://%s:48010", inet_ntoa(targetAddress));
err |= addAttributeString(&optionHead, "x-nv-general.serverAddress", payloadStr);
sprintf(payloadStr, "%d", streamConfig->width);
err |= addAttributeString(&optionHead, "x-nv-video[0].clientViewportWd", payloadStr);
@ -121,9 +174,6 @@ static PSDP_OPTION getAttributesList(PSTREAM_CONFIGURATION streamConfig, struct
err |= addAttributeString(&optionHead, "x-nv-video[0].timeoutLengthMs", "7000");
err |= addAttributeString(&optionHead, "x-nv-video[0].framesWithInvalidRefThreshold", "0");
// This flags value will mean that resolution won't change as bitrate falls
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.flags", "51");
// Lock the bitrate since we're not scaling resolution so the picture doesn't get too bad
if (streamConfig->height >= 1080 && streamConfig->fps >= 60) {
if (streamConfig->bitrate < 10000) {
@ -167,6 +217,13 @@ static PSDP_OPTION getAttributesList(PSTREAM_CONFIGURATION streamConfig, struct
// FIXME: Remote optimizations
err |= addAttributeString(&optionHead, "x-nv-vqos[0].qosTrafficType", "5");
err |= addAttributeString(&optionHead, "x-nv-aqos.qosTrafficType", "4");
if (serverMajorVersion == 3) {
err |= addGen3Options(&optionHead, inet_ntoa(targetAddress));
}
else {
err |= addGen4Options(&optionHead, inet_ntoa(targetAddress));
}
if (err == 0) {
return optionHead;
@ -177,22 +234,24 @@ static PSDP_OPTION getAttributesList(PSTREAM_CONFIGURATION streamConfig, struct
}
/* Populate the SDP header with required information */
static int fillSdpHeader(char* buffer, struct in_addr targetAddress) {
static int fillSdpHeader(char* buffer, struct in_addr targetAddress, int rtspClientVersion) {
return sprintf(buffer,
"v=0\r\n"
"o=android 0 "RTSP_CLIENT_VERSION_S" IN IPv4 %s\r\n"
"s=NVIDIA Streaming Client\r\n", inet_ntoa(targetAddress));
"o=android 0 %d IN IPv4 %s\r\n"
"s=NVIDIA Streaming Client\r\n", rtspClientVersion, inet_ntoa(targetAddress));
}
/* Populate the SDP tail with required information */
static int fillSdpTail(char* buffer) {
return sprintf(buffer,
"t=0 0\r\n"
"m=video 47998 \r\n");
"m=video %d \r\n",
serverMajorVersion < 4 ? 47996 : 47998);
}
/* Get the SDP attributes for the stream config */
char* getSdpPayloadForStreamConfig(PSTREAM_CONFIGURATION streamConfig, struct in_addr targetAddress, int *length) {
char* getSdpPayloadForStreamConfig(PSTREAM_CONFIGURATION streamConfig, struct in_addr targetAddress,
int rtspClientVersion, int *length) {
PSDP_OPTION attributeList;
int offset;
char* payload;
@ -209,7 +268,7 @@ char* getSdpPayloadForStreamConfig(PSTREAM_CONFIGURATION streamConfig, struct in
return NULL;
}
offset = fillSdpHeader(payload, targetAddress);
offset = fillSdpHeader(payload, targetAddress, rtspClientVersion);
offset += fillSerializedAttributeList(&payload[offset], attributeList);
offset += fillSdpTail(&payload[offset]);

View File

@ -6,6 +6,7 @@
#define FIRST_FRAME_MAX 1500
#define RTP_PORT 47998
#define FIRST_FRAME_PORT 47996
static DECODER_RENDERER_CALLBACKS callbacks;
static STREAM_CONFIGURATION configuration;
@ -13,6 +14,7 @@ static IP_ADDRESS remoteHost;
static PCONNECTION_LISTENER_CALLBACKS listenerCallbacks;
static SOCKET rtpSocket = INVALID_SOCKET;
static SOCKET firstFrameSocket = INVALID_SOCKET;
static PLT_THREAD udpPingThread;
static PLT_THREAD receiveThread;
@ -108,6 +110,17 @@ static void DecoderThreadProc(void* context) {
}
}
/* Read the first frame of the video stream */
int readFirstFrame(void) {
// All that matters is that we close this socket.
// This starts the flow of video on Gen 3 servers.
closesocket(firstFrameSocket);
firstFrameSocket = INVALID_SOCKET;
return 0;
}
/* Terminate the video stream */
void stopVideoStream(void) {
callbacks.stop();
@ -116,6 +129,10 @@ void stopVideoStream(void) {
PltInterruptThread(&receiveThread);
PltInterruptThread(&decoderThread);
if (firstFrameSocket != INVALID_SOCKET) {
closesocket(firstFrameSocket);
firstFrameSocket = INVALID_SOCKET;
}
if (rtpSocket != INVALID_SOCKET) {
closesocket(rtpSocket);
rtpSocket = INVALID_SOCKET;
@ -156,12 +173,28 @@ int startVideoStream(void* rendererContext, int drFlags) {
return err;
}
if (serverMajorVersion == 3) {
// Connect this socket to open port 47998 for our ping thread
firstFrameSocket = connectTcpSocket(remoteHost, FIRST_FRAME_PORT);
if (firstFrameSocket == INVALID_SOCKET) {
return LastSocketError();
}
}
// Start pinging before reading the first frame so GFE knows where
// to send UDP data
err = PltCreateThread(UdpPingThreadProc, NULL, &udpPingThread);
if (err != 0) {
return err;
}
if (serverMajorVersion == 3) {
// Read the first frame to start the flow of video
err = readFirstFrame();
if (err != 0) {
return err;
}
}
return 0;
}