mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-17 17:05:50 +00:00
Merge pull request #14 from irtimmer/gen5
Add support for Generation 5 servers (GFE 2.10.2+)
This commit is contained in:
commit
88edbd8dc1
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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(¤tPos, &specialSeq) &&
|
||||
isSeqFrameStart(&specialSeq) &&
|
||||
@ -519,4 +528,4 @@ void queueRtpPacket(PRTP_PACKET rtpPacket, int length) {
|
||||
}
|
||||
|
||||
processRtpPayload((PNV_VIDEO_PACKET)(((char*)rtpPacket) + dataOffset), length - dataOffset);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user