Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(rtp): improve timestamp accuracy #3513

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

andygrundman
Copy link
Contributor

Background

I am experimenting with the iOS AVSampleBufferAudioRenderer and AVSampleBufferRenderSynchronizer APIs, which include the ability to use RTP timestamps as a way to pace playback, provide a/v sync that won't drift, use variable-speed playback to cover for short glitches (such as Game Mode's drop in Bluetooth latency), proper spatial audio support for free, and more. It should also be nicely compatible with the AVSampleBufferDisplayLayer API currently used by the iOS video code. The biggest challenge with these APIs is their higher latency, but I'm hopeful it can get to a reasonable level. Worst case, it could be a good option when you are willing to trade a little latency for solid playback.

Description

  • Video: instead of using now() when the RTP packet is created, use the earlier packet->frame_timestamp that we're already collecting for host latency stats. This timestamp should be more accurate to when we captured the frame. I am not sure if all backends support this timestamp, so there is a fallback to the current method.
  • Audio: fix a bug where the RTP timestamps stop advancing when no audio is being sent. Audio now uses the same timer-based source for this field, but remains as milliseconds. Because of this bug, I am pretty sure no client is using these timestamps for anything.
  • Both use steady_clock and microseconds / 1000 to maintain precision. Video still uses 90khz time base per the spec, and audio is effectively at packet duration resolution of 5ms/10ms.

Known issue: I had to comment out one assert in Moonlight, because this patch changes one of the FEC timestamps. The FEC RFC says "The timestamp SHALL be set to a time corresponding to the repair packet's transmission time. Note that the timestamp value has no use in the actual FEC protection process and is usually useful for jitter calculations.", so I don't think this assert is correct anyway. Even though this shouldn't affect Moonlight release builds which don't use asserts or the extra FEC verification code, this probably needs to be resolved before this can be accepted, either by only sending correct timestamp packets to newer Moonlight versions, or fixing it in the FEC code. (Not to get too sidetracked but was the Opus FEC support ever tried? I sometimes wonder if the 40% FEC overhead on audio is too high for benefit.)

RtpAudioQueue.c:300
LC_ASSERT_VT(existingBlock->fecHeader.baseTimestamp == fecBlockBaseTs);

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Dependency update (updates to dependencies)
  • Documentation update (changes to documentation)
  • Repository update (changes to repository files, e.g. .github/...)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the in code docstring/documentation-blocks for new or existing methods/components

@andygrundman andygrundman force-pushed the andyg.fix-rtp-timestamps branch 2 times, most recently from 7230cb5 to 2710caa Compare January 4, 2025 14:04
@ReenigneArcher ReenigneArcher requested a review from cgutman January 4, 2025 14:45
@ReenigneArcher ReenigneArcher changed the title Improve the audio & video RTP timestamps fix(av): Improve the audio & video RTP timestamps Jan 4, 2025
@andygrundman
Copy link
Contributor Author

Oops, I changed the title in the commit, but forgot to do the PR. I guess it's still too long anyway:

fix(rtp): maintain audio timestamp continuity even during periods of silence. Measure video timestamps closer to the time of capture by reusing host latency's frame_timestamp.

@andygrundman andygrundman force-pushed the andyg.fix-rtp-timestamps branch from 2710caa to 15f6927 Compare January 4, 2025 22:26
@ReenigneArcher ReenigneArcher changed the title fix(av): Improve the audio & video RTP timestamps fix(rtp): improve timestamp accuracy Jan 5, 2025
@andygrundman andygrundman force-pushed the andyg.fix-rtp-timestamps branch from a6aa913 to 5c2a8e2 Compare January 7, 2025 06:22
@ReenigneArcher ReenigneArcher force-pushed the andyg.fix-rtp-timestamps branch from 5c2a8e2 to f8580af Compare January 8, 2025 03:13
Copy link

codecov bot commented Jan 8, 2025

Codecov Report

Attention: Patch coverage is 0% with 14 lines in your changes missing coverage. Please review.

Project coverage is 6.18%. Comparing base (9b9767b) to head (da59b33).

Files with missing lines Patch % Lines
src/stream.cpp 0.00% 14 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##           master   #3513      +/-   ##
=========================================
- Coverage    8.44%   6.18%   -2.27%     
=========================================
  Files          90      66      -24     
  Lines       16069   11989    -4080     
  Branches     7633    5584    -2049     
=========================================
- Hits         1357     741     -616     
+ Misses      14214   11072    -3142     
+ Partials      498     176     -322     
Flag Coverage Δ
Linux ?
Windows 6.18% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/stream.cpp 2.49% <0.00%> (-0.02%) ⬇️

... and 49 files with indirect coverage changes

@cgutman
Copy link
Collaborator

cgutman commented Jan 8, 2025

Because of this bug, I am pretty sure no client is using these timestamps for anything.

I believe that's accurate. The clients that I know of that try to pace audio do it via monitoring the amount of pending audio (either in moonlight-common-c's queue, in the OS audio stack, or both).

Known issue: I had to comment out one assert in Moonlight, because this patch changes one of the FEC timestamps. The FEC RFC says "The timestamp SHALL be set to a time corresponding to the repair packet's transmission time. Note that the timestamp value has no use in the actual FEC protection process and is usually useful for jitter calculations.", so I don't think this assert is correct anyway.

AFAIK, Nvidia did their own thing and didn't implement any known RTP FEC standard, so it's asserting what GFE and current Sunshine do.

IIUC, what is happening in your case is that you hit the (common) case where a data packet is received first and we synthesize an FEC header using this code:

        // This is a data packet, so we will need to synthesize an FEC header
        fecBlockPayloadType = packet->packetType;
        fecBlockBaseSeqNum = (packet->sequenceNumber / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS;
        fecBlockBaseTs = packet->timestamp - ((packet->sequenceNumber - fecBlockBaseSeqNum) * AudioPacketDuration);
        fecBlockSsrc = packet->ssrc;

Since you've adjusted the logic to no longer guarantee that there is AudioPacketDuration milliseconds between the timestamps in each packet, the synthesized FEC header won't always match the real thing that we will get later. If this an accurate description of what you saw going on, then I think removing the assert is fine.

Even though this shouldn't affect Moonlight release builds which don't use asserts or the extra FEC verification code, this probably needs to be resolved before this can be accepted, either by only sending correct timestamp packets to newer Moonlight versions, or fixing it in the FEC code.

Did you have some idea of how you would "fix it"? AFAICT, it seems basically intrinsic to this model where timestamps are real measured things and not just a glorified counter.

Not to get too sidetracked but was the Opus FEC support ever tried? I sometimes wonder if the 40% FEC overhead on audio is too high for benefit.

I know Nvidia didn't use it. Given how tiny of a fraction that audio data is of the total traffic, it probably made sense to use the existing Reed-Solomon FEC scheme they were already using for video. They didn't even bother with a variable FEC scheme for audio, unlike video FEC which can change based on client feedback.

// Audio timestamps are in milliseconds and should be AudioPacketDuration (5ms or 10ms) apart
auto timestamp = static_cast<std::uint32_t>(
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - session->audio.timestamp_epoch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's possible to get capture time from the OS too like we do with video frames. IAudioCaptureClient::GetBuffer() takes an optional pu64QPCPosition parameter that gives you the timestamp of capture.

* Video: instead of using now() when the RTP packet is created, use the earlier packet->frame_timestamp that we're already collecting for host latency stats. This timestamp should be more accurate to when we captured the frame. I am not sure if all backends support this timestamp, so there is a fallback to the current method.
* Audio: fix bug where the RTP timestamps stop advancing when no audio is being sent. Audio now uses the same timer-based source for this field, but remains as milliseconds.
* Both use steady_clock and microseconds / 1000 to maintain precision. Video still uses 90khz time base per the spec, and audio is effectively at packet duration resolution of 5ms/10ms.
@andygrundman andygrundman force-pushed the andyg.fix-rtp-timestamps branch from f8580af to da59b33 Compare January 8, 2025 08:20
Copy link

sonarqubecloud bot commented Jan 8, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants