mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-07-21 06:10:31 +00:00
Improve support for high-resolution stats
* This patch adds a new microsecond-resolution function call, LiGetMicroseconds(), to complement the existing LiGetMillis(). Many variables used by stats have been updated to work at this higher resolution and now provide better results when displaying e.g. sub-millisecond frametime stats. To try and avoid confusion, variables that now contain microseconds have been renamed with a suffix of 'Us', and those ending in 'Ms' contain milliseconds. I originally experimented with nanoseconds but it felt like overkill for our needs. Public API in Limelight.h: uint64_t LiGetMicroseconds(void); uint64_t LiGetMillis(void); const RTP_AUDIO_STATS* LiGetRTPAudioStats(void); // provides access to RTP data for the overlay stats const RTP_VIDEO_STATS* LiGetRTPVideoStats(void); Note: Users of this library may need to make changes. If using LiGetMillis() to track the duration of something that is shown to the user, consider switching to LiGetMicroseconds(). Remember to divide by 1000 at time of display to show in milliseconds.
This commit is contained in:
committed by
Cameron Gutman
parent
5f2280183c
commit
82ee2d6590
+25
-22
@@ -91,7 +91,7 @@ static void removeEntryFromList(PRTPV_QUEUE_LIST list, PRTPV_QUEUE_ENTRY entry)
|
||||
|
||||
static void reportFinalFrameFecStatus(PRTP_VIDEO_QUEUE queue) {
|
||||
SS_FRAME_FEC_STATUS fecStatus;
|
||||
|
||||
|
||||
fecStatus.frameIndex = BE32(queue->currentFrameNumber);
|
||||
fecStatus.highestReceivedSequenceNumber = BE16(queue->receivedHighestSequenceNumber);
|
||||
fecStatus.nextContiguousSequenceNumber = BE16(queue->nextContiguousSequenceNumber);
|
||||
@@ -103,7 +103,7 @@ static void reportFinalFrameFecStatus(PRTP_VIDEO_QUEUE queue) {
|
||||
fecStatus.fecPercentage = (uint8_t)queue->fecPercentage;
|
||||
fecStatus.multiFecBlockIndex = (uint8_t)queue->multiFecCurrentBlockNumber;
|
||||
fecStatus.multiFecBlockCount = (uint8_t)(queue->multiFecLastBlockNumber + 1);
|
||||
|
||||
|
||||
connectionSendFrameFecStatus(&fecStatus);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ static void reportFinalFrameFecStatus(PRTP_VIDEO_QUEUE queue) {
|
||||
static bool queuePacket(PRTP_VIDEO_QUEUE queue, PRTPV_QUEUE_ENTRY newEntry, PRTP_PACKET packet, int length, bool isParity, bool isFecRecovery) {
|
||||
PRTPV_QUEUE_ENTRY entry;
|
||||
bool outOfSequence;
|
||||
|
||||
|
||||
LC_ASSERT(!(isFecRecovery && isParity));
|
||||
LC_ASSERT(!isBefore16(packet->sequenceNumber, queue->nextContiguousSequenceNumber));
|
||||
|
||||
@@ -195,7 +195,7 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
int ret;
|
||||
|
||||
LC_ASSERT(totalPackets == U16(queue->bufferHighestSequenceNumber - queue->bufferLowestSequenceNumber) + 1U);
|
||||
|
||||
|
||||
#ifdef FEC_VALIDATION_MODE
|
||||
// We'll need an extra packet to run in FEC validation mode, because we will
|
||||
// be "dropping" one below and recovering it using parity. However, some frames
|
||||
@@ -263,9 +263,9 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
ret = -2;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
rs = reed_solomon_new(queue->bufferDataPackets, queue->bufferParityPackets);
|
||||
|
||||
|
||||
// This could happen in an OOM condition, but it could also mean the FEC data
|
||||
// that we fed to reed_solomon_new() is bogus, so we'll assert to get a better look.
|
||||
LC_ASSERT(rs != NULL);
|
||||
@@ -273,9 +273,9 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
ret = -3;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
memset(marks, 1, sizeof(char) * (totalPackets));
|
||||
|
||||
|
||||
int receiveSize = StreamConfig.packetSize + MAX_RTP_HEADER_SIZE;
|
||||
int packetBufferSize = receiveSize + sizeof(RTPV_QUEUE_ENTRY);
|
||||
|
||||
@@ -307,7 +307,7 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
|
||||
packets[index] = (unsigned char*) entry->packet;
|
||||
marks[index] = 0;
|
||||
|
||||
|
||||
//Set padding to zero
|
||||
if (entry->length < receiveSize) {
|
||||
memset(&packets[index][entry->length], 0, receiveSize - entry->length);
|
||||
@@ -326,9 +326,9 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ret = reed_solomon_reconstruct(rs, packets, marks, totalPackets, receiveSize);
|
||||
|
||||
|
||||
// We should always provide enough parity to recover the missing data successfully.
|
||||
// If this fails, something is probably wrong with our FEC state.
|
||||
LC_ASSERT(ret == 0);
|
||||
@@ -339,7 +339,7 @@ static int reconstructFrame(PRTP_VIDEO_QUEUE queue) {
|
||||
queue->bufferDataPackets - queue->receivedDataPackets,
|
||||
queue->currentFrameNumber);
|
||||
#endif
|
||||
|
||||
|
||||
// Report the final FEC status if we needed to perform a recovery
|
||||
reportFinalFrameFecStatus(queue);
|
||||
}
|
||||
@@ -355,7 +355,7 @@ cleanup_packets:
|
||||
rtpPacket->header = queue->pendingFecBlockList.head->packet->header;
|
||||
rtpPacket->timestamp = queue->pendingFecBlockList.head->packet->timestamp;
|
||||
rtpPacket->ssrc = queue->pendingFecBlockList.head->packet->ssrc;
|
||||
|
||||
|
||||
int dataOffset = sizeof(*rtpPacket);
|
||||
if (rtpPacket->header & FLAG_EXTENSION) {
|
||||
dataOffset += 4; // 2 additional fields
|
||||
@@ -457,7 +457,7 @@ cleanup:
|
||||
|
||||
if (marks != NULL)
|
||||
free(marks);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -497,8 +497,8 @@ static void stageCompleteFecBlock(PRTP_VIDEO_QUEUE queue) {
|
||||
// and use the first packet's receive time for all packets. This ends up
|
||||
// actually being better for the measurements that the depacketizer does,
|
||||
// since it properly handles out of order packets.
|
||||
LC_ASSERT(queue->bufferFirstRecvTimeMs != 0);
|
||||
entry->receiveTimeMs = queue->bufferFirstRecvTimeMs;
|
||||
LC_ASSERT(queue->bufferFirstRecvTimeUs != 0);
|
||||
entry->receiveTimeUs = queue->bufferFirstRecvTimeUs;
|
||||
|
||||
// Move this packet to the completed FEC block list
|
||||
insertEntryIntoList(&queue->completedFecBlockList, entry);
|
||||
@@ -631,7 +631,7 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
|
||||
queue->bufferDataPackets);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We must either start on the current FEC block number for the current frame,
|
||||
// or block 0 of a new frame.
|
||||
uint8_t expectedFecBlockNumber = (queue->currentFrameNumber == nvPacket->frameIndex ? queue->multiFecCurrentBlockNumber : 0);
|
||||
@@ -689,8 +689,8 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
|
||||
// Tell the control stream logic about this frame, even if we don't end up
|
||||
// being able to reconstruct a full frame from it.
|
||||
connectionSawFrame(queue->currentFrameNumber);
|
||||
|
||||
queue->bufferFirstRecvTimeMs = PltGetMillis();
|
||||
|
||||
queue->bufferFirstRecvTimeUs = PltGetMicroseconds();
|
||||
queue->bufferLowestSequenceNumber = U16(packet->sequenceNumber - fecIndex);
|
||||
queue->nextContiguousSequenceNumber = queue->bufferLowestSequenceNumber;
|
||||
queue->receivedDataPackets = 0;
|
||||
@@ -706,6 +706,9 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
|
||||
queue->bufferHighestSequenceNumber = U16(queue->bufferFirstParitySequenceNumber + queue->bufferParityPackets - 1);
|
||||
queue->multiFecCurrentBlockNumber = fecCurrentBlockNumber;
|
||||
queue->multiFecLastBlockNumber = (nvPacket->multiFecBlocks >> 6) & 0x3;
|
||||
|
||||
queue->stats.packetCountVideo += queue->bufferDataPackets;
|
||||
queue->stats.packetCountFec += queue->bufferParityPackets;
|
||||
}
|
||||
|
||||
// Reject packets above our FEC queue valid sequence number range
|
||||
@@ -762,18 +765,18 @@ int RtpvAddPacket(PRTP_VIDEO_QUEUE queue, PRTP_PACKET packet, int length, PRTPV_
|
||||
queue->receivedParityPackets++;
|
||||
LC_ASSERT(queue->receivedParityPackets <= queue->bufferParityPackets);
|
||||
}
|
||||
|
||||
|
||||
// Try to submit this frame. If we haven't received enough packets,
|
||||
// this will fail and we'll keep waiting.
|
||||
if (reconstructFrame(queue) == 0) {
|
||||
// Stage the complete FEC block for use once reassembly is complete
|
||||
stageCompleteFecBlock(queue);
|
||||
|
||||
|
||||
// stageCompleteFecBlock() should have consumed all pending FEC data
|
||||
LC_ASSERT(queue->pendingFecBlockList.head == NULL);
|
||||
LC_ASSERT(queue->pendingFecBlockList.tail == NULL);
|
||||
LC_ASSERT(queue->pendingFecBlockList.count == 0);
|
||||
|
||||
|
||||
// If we're not yet at the last FEC block for this frame, move on to the next block.
|
||||
// Otherwise, the frame is complete and we can move on to the next frame.
|
||||
if (queue->multiFecCurrentBlockNumber < queue->multiFecLastBlockNumber) {
|
||||
|
||||
Reference in New Issue
Block a user