Merge pull request #14 from irtimmer/gen5

Add support for Generation 5 servers (GFE 2.10.2+)
This commit is contained in:
Cameron Gutman 2016-02-19 11:45:33 -05:00
commit 88edbd8dc1
6 changed files with 138 additions and 49 deletions

View File

@ -21,7 +21,8 @@ static PLT_THREAD lossStatsThread;
static PLT_THREAD invalidateRefFramesThread;
static PLT_EVENT invalidateRefFramesEvent;
static int lossCountSinceLastReport;
static long currentFrame;
static long lastGoodFrame;
static long lastSeenFrame;
static int stopping;
static int idrFrameRequired;
@ -49,6 +50,13 @@ static const short packetTypesGen4[] = {
0x060a, // Loss Stats
0x0611, // Frame Stats (unused)
};
static const short packetTypesGen5[] = {
0x0305, // Start A
0x0307, // Start B
0x0301, // Invalidate reference frames
0x0201, // Loss Stats
0x0204, // Frame Stats (unused)
};
static const char startAGen3[] = { 0 };
static const int startBGen3[] = { 0, 0, 0, 0xa };
@ -56,6 +64,9 @@ static const int startBGen3[] = { 0, 0, 0, 0xa };
static const char requestIdrFrameGen4[] = { 0, 0 };
static const char startBGen4[] = { 0 };
static const char startAGen5[] = { 0, 0 };
static const char startBGen5[] = { 0 };
static const short payloadLengthsGen3[] = {
sizeof(startAGen3), // Start A
sizeof(startBGen3), // Start B
@ -70,6 +81,13 @@ static const short payloadLengthsGen4[] = {
32, // Loss Stats
64, // Frame Stats
};
static const short payloadLengthsGen5[] = {
sizeof(startAGen5), // Start A
sizeof(startBGen5), // Start B
24, // Invalidate reference frames
32, // Loss Stats
80, // Frame Stats
};
static const char* preconstructedPayloadsGen3[] = {
startAGen3,
@ -79,6 +97,10 @@ static const char* preconstructedPayloadsGen4[] = {
requestIdrFrameGen4,
startBGen4
};
static const char* preconstructedPayloadsGen5[] = {
startAGen5,
startBGen5
};
static short* packetTypes;
static short* payloadLengths;
@ -97,14 +119,20 @@ int initializeControlStream(void) {
payloadLengths = (short*)payloadLengthsGen3;
preconstructedPayloads = (char**)preconstructedPayloadsGen3;
}
else {
else if (ServerMajorVersion == 4) {
packetTypes = (short*)packetTypesGen4;
payloadLengths = (short*)payloadLengthsGen4;
preconstructedPayloads = (char**)preconstructedPayloadsGen4;
}
else {
packetTypes = (short*)packetTypesGen5;
payloadLengths = (short*)payloadLengthsGen5;
preconstructedPayloads = (char**)preconstructedPayloadsGen5;
}
idrFrameRequired = 0;
currentFrame = 0;
lastGoodFrame = 0;
lastSeenFrame = 0;
lossCountSinceLastReport = 0;
return 0;
@ -173,8 +201,12 @@ void connectionDetectedFrameLoss(int startFrame, int endFrame) {
}
// When we receive a frame, update the number of our current frame
void connectionReceivedFrame(int frameIndex) {
currentFrame = frameIndex;
void connectionReceivedCompleteFrame(int frameIndex) {
lastGoodFrame = frameIndex;
}
void connectionSawFrame(int frameIndex) {
lastSeenFrame = frameIndex;
}
// When we lose packets, update our packet loss count
@ -270,7 +302,7 @@ static void lossStatsThreadFunc(void* context) {
BbPutInt(&byteBuffer, lossCountSinceLastReport);
BbPutInt(&byteBuffer, LOSS_REPORT_INTERVAL_MS);
BbPutInt(&byteBuffer, 1000);
BbPutLong(&byteBuffer, currentFrame);
BbPutLong(&byteBuffer, lastGoodFrame);
BbPutInt(&byteBuffer, 0);
BbPutInt(&byteBuffer, 0);
BbPutInt(&byteBuffer, 0x14);
@ -297,10 +329,17 @@ static void lossStatsThreadFunc(void* context) {
static void requestIdrFrame(void) {
long long payload[3];
if (ServerMajorVersion == 3) {
if (ServerMajorVersion != 4) {
// Form the payload
payload[0] = 0;
payload[1] = 0xFFFFF;
if (lastSeenFrame < 0x20) {
payload[0] = 0;
payload[1] = 0x20;
}
else {
payload[0] = lastSeenFrame - 0x20;
payload[1] = lastSeenFrame;
}
payload[2] = 0;
// Send the reference frame invalidation request and read the response
@ -454,4 +493,4 @@ int startControlStream(void) {
}
return 0;
}
}

View File

@ -326,7 +326,11 @@ int LiSendMouseMoveEvent(short deltaX, short deltaY) {
holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET);
holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE);
holder->packet.mouseMove.magic = htonl(MOUSE_MOVE_MAGIC);
holder->packet.mouseMove.magic = (MOUSE_MOVE_MAGIC);
// On Gen 5 servers, the header code is incremented by one
if (ServerMajorVersion >= 5) {
holder->packet.mouseMove.magic += 0x01000000;
}
holder->packet.mouseMove.deltaX = htons(deltaX);
holder->packet.mouseMove.deltaY = htons(deltaY);
@ -355,6 +359,9 @@ int LiSendMouseButtonEvent(char action, int button) {
holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET);
holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON);
holder->packet.mouseButton.action = action;
if (ServerMajorVersion >= 5) {
holder->packet.mouseButton.action++;
}
holder->packet.mouseButton.button = htonl(button);
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
@ -432,6 +439,10 @@ static int sendControllerEventInternal(short controllerNumber, short buttonFlags
holder->packetLength = sizeof(NV_MULTI_CONTROLLER_PACKET);
holder->packet.multiController.header.packetType = htonl(PACKET_TYPE_MULTI_CONTROLLER);
holder->packet.multiController.headerA = MC_HEADER_A;
// On Gen 5 servers, the header code is decremented by one
if (ServerMajorVersion >= 5) {
holder->packet.multiController.headerA--;
}
holder->packet.multiController.headerB = MC_HEADER_B;
holder->packet.multiController.controllerNumber = controllerNumber;
holder->packet.multiController.midA = MC_ACTIVE_CONTROLLER_FLAGS;
@ -488,6 +499,10 @@ int LiSendScrollEvent(signed char scrollClicks) {
holder->packetLength = sizeof(NV_SCROLL_PACKET);
holder->packet.scroll.header.packetType = htonl(PACKET_TYPE_SCROLL);
holder->packet.scroll.magicA = MAGIC_A;
// On Gen 5 servers, the header code is incremented by one
if (ServerMajorVersion >= 5) {
holder->packet.scroll.magicA++;
}
holder->packet.scroll.zero1 = 0;
holder->packet.scroll.zero2 = 0;
holder->packet.scroll.scrollAmt1 = htons(scrollClicks * 120);
@ -500,4 +515,4 @@ int LiSendScrollEvent(signed char scrollClicks) {
}
return err;
}
}

View File

@ -29,7 +29,8 @@ void destroyControlStream(void);
void requestIdrOnDemand(void);
void connectionSinkTooSlow(int startFrame, int endFrame);
void connectionDetectedFrameLoss(int startFrame, int endFrame);
void connectionReceivedFrame(int frameIndex);
void connectionReceivedCompleteFrame(int frameIndex);
void connectionSawFrame(int frameIndex);
void connectionLostPackets(int lastReceivedPacket, int nextReceivedPacket);
int performRtspHandshake(void);

View File

@ -299,9 +299,12 @@ int performRtspHandshake(void) {
if (ServerMajorVersion == 3) {
rtspClientVersion = 10;
}
else {
else if (ServerMajorVersion == 4) {
rtspClientVersion = 11;
}
else {
rtspClientVersion = 12;
}
{
RTSP_MESSAGE response;
@ -440,4 +443,4 @@ int performRtspHandshake(void) {
}
return 0;
}
}

View File

@ -139,8 +139,6 @@ static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
char payloadStr[92];
int err = 0;
unsigned char slicesPerFrame;
int audioChannelCount;
int audioChannelMask;
sprintf(payloadStr, "rtsp://%s:48010", addrStr);
err |= addAttributeString(head, "x-nv-general.serverAddress", payloadStr);
@ -156,32 +154,25 @@ static int addGen4Options(PSDP_OPTION* head, char* addrStr) {
sprintf(payloadStr, "%d", slicesPerFrame);
err |= addAttributeString(head, "x-nv-video[0].videoEncoderSlicesPerFrame", payloadStr);
if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) {
audioChannelCount = CHANNEL_COUNT_51_SURROUND;
audioChannelMask = CHANNEL_MASK_51_SURROUND;
}
else {
audioChannelCount = CHANNEL_COUNT_STEREO;
audioChannelMask = CHANNEL_MASK_STEREO;
}
return err;
}
sprintf(payloadStr, "%d", audioChannelCount);
err |= addAttributeString(head, "x-nv-audio.surround.numChannels", payloadStr);
sprintf(payloadStr, "%d", audioChannelMask);
err |= addAttributeString(head, "x-nv-audio.surround.channelMask", payloadStr);
if (audioChannelCount > 2) {
err |= addAttributeString(head, "x-nv-audio.surround.enable", "1");
}
else {
err |= addAttributeString(head, "x-nv-audio.surround.enable", "0");
}
static int addGen5Options(PSDP_OPTION* head) {
int err = 0;
// We want to use the legacy TCP connections for control and input rather than the new UDP stuff
err |= addAttributeString(head, "x-nv-general.useReliableUdp", "0");
err |= addAttributeString(head, "x-nv-ri.useControlChannel", "0");
err |= addAttributeString(head, "x-nv-vqos[0].enableQec", "0");
return err;
}
static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
PSDP_OPTION optionHead;
char payloadStr[92];
int audioChannelCount;
int audioChannelMask;
int err;
optionHead = NULL;
@ -200,20 +191,26 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
err |= addAttributeString(&optionHead, "x-nv-video[0].rateControlMode", "4");
if (StreamConfig.streamingRemotely) {
err |= addAttributeString(&optionHead, "x-nv-video[0].averageBitrate", "4");
err |= addAttributeString(&optionHead, "x-nv-video[0].peakBitrate", "4");
}
err |= addAttributeString(&optionHead, "x-nv-video[0].timeoutLengthMs", "7000");
err |= addAttributeString(&optionHead, "x-nv-video[0].framesWithInvalidRefThreshold", "0");
// We don't support dynamic bitrate scaling properly (it tends to bounce between min and max and never
// settle on the optimal bitrate if it's somewhere in the middle), so we'll just latch the bitrate
// to the requested value.
sprintf(payloadStr, "%d", StreamConfig.bitrate);
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrate", payloadStr);
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrate", payloadStr);
if (ServerMajorVersion >= 5) {
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrateKbps", payloadStr);
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrateKbps", payloadStr);
}
else {
if (StreamConfig.streamingRemotely) {
err |= addAttributeString(&optionHead, "x-nv-video[0].averageBitrate", "4");
err |= addAttributeString(&optionHead, "x-nv-video[0].peakBitrate", "4");
}
// We don't support dynamic bitrate scaling properly (it tends to bounce between min and max and never
// settle on the optimal bitrate if it's somewhere in the middle), so we'll just latch the bitrate
// to the requested value.
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.minimumBitrate", payloadStr);
err |= addAttributeString(&optionHead, "x-nv-vqos[0].bw.maximumBitrate", payloadStr);
}
// Using FEC turns padding on which makes us have to take the slow path
// in the depacketizer, not to mention exposing some ambiguous cases with
@ -235,9 +232,34 @@ static PSDP_OPTION getAttributesList(char*urlSafeAddr) {
if (ServerMajorVersion == 3) {
err |= addGen3Options(&optionHead, urlSafeAddr);
}
else {
else if (ServerMajorVersion == 4) {
err |= addGen4Options(&optionHead, urlSafeAddr);
}
else {
err |= addGen5Options(&optionHead);
}
if (ServerMajorVersion >= 4) {
if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) {
audioChannelCount = CHANNEL_COUNT_51_SURROUND;
audioChannelMask = CHANNEL_MASK_51_SURROUND;
}
else {
audioChannelCount = CHANNEL_COUNT_STEREO;
audioChannelMask = CHANNEL_MASK_STEREO;
}
sprintf(payloadStr, "%d", audioChannelCount);
err |= addAttributeString(&optionHead, "x-nv-audio.surround.numChannels", payloadStr);
sprintf(payloadStr, "%d", audioChannelMask);
err |= addAttributeString(&optionHead, "x-nv-audio.surround.channelMask", payloadStr);
if (audioChannelCount > 2) {
err |= addAttributeString(&optionHead, "x-nv-audio.surround.enable", "1");
}
else {
err |= addAttributeString(&optionHead, "x-nv-audio.surround.enable", "0");
}
}
if (err == 0) {
return optionHead;
@ -294,4 +316,4 @@ char* getSdpPayloadForStreamConfig(int rtspClientVersion, int* length) {
freeAttributeList(attributeList);
*length = offset;
return payload;
}
}

View File

@ -231,7 +231,7 @@ static void reassembleAvcFrame(int frameNumber) {
}
// Notify the control connection
connectionReceivedFrame(frameNumber);
connectionReceivedCompleteFrame(frameNumber);
// Clear frame drops
consecutiveFrameDrops = 0;
@ -392,6 +392,9 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
return;
}
// Notify the listener of the latest frame we've seen from the PC
connectionSawFrame(frameIndex);
// Look for a frame start before receiving a frame end
if (firstPacket && decodingFrame)
{
@ -469,6 +472,12 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length) {
}
lastPacketInStream = streamPacketIndex;
// If this is the first packet, skip the frame header (if one exists)
if (firstPacket && ServerMajorVersion >= 5) {
currentPos.offset += 8;
currentPos.length -= 8;
}
if (firstPacket &&
getSpecialSeq(&currentPos, &specialSeq) &&
isSeqFrameStart(&specialSeq) &&
@ -519,4 +528,4 @@ void queueRtpPacket(PRTP_PACKET rtpPacket, int length) {
}
processRtpPayload((PNV_VIDEO_PACKET)(((char*)rtpPacket) + dataOffset), length - dataOffset);
}
}