Supercede RFI protocol with LTR frames #120
Labels
No labels
bug
dependencies
duplicate
enhancement
hacktoberfest-accepted
invalid
question
submodules
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-moonlight-stream/moonlight-common-c#120
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Presently Reference Frame Invalidation protocol (RFI for short) works by invalidating short-term reference frames, while DPB works as FIFO. LTR or long-term reference frames allow to manually "pin" reference frames in DPB, which exempts them from being pushed out by short-term reference frames. This can give us a number of important properties:
Now comes the "but", to do this efficiently the protocol needs to be extended. Servers need to flag LTR frames and clients need to send ACKs for them. So the exchange could look like this:
That's about it. I haven't written any code for this yet, so any thoughts or opinions are welcome.
I've just tested this on VPL encoder (not ffmpeg) and it works as expected. 3-frame DPB and generating LTR frame speculatively every 100ms gives ~200ms of no-IDR recovery. With ACKs in the protocol the no-IDR duration will become virtually infinite and with much shorter temporal distance to LTR recovery frame.
I really like the idea of using LTR frames. I don't think it would be difficult to integrate on-demand frame ACKs into the control stream. Maybe the host could indicate that it wants an ACK by setting a flag in the NV_VIDEO_PACKET struct? It seems like either
flagsorreservedfields would work.Edit: I think
flagswon't work because the VideoDepacketizer and RtpVideoQueue is strict about not having additional flags set there. The historical reason is some unusual FEC recovery failures from GFE where a completely corrupt frame would come out of FEC recovery. But it doesn't look like anything touchesreservedat all, so we could repurpose that as a second flags field.Yeah, I will look into this, thanks.
Oh, and another thing. I'm not sure that relying on UDP packet checksums for frame validation is enough: FEC validation is not being enabled by default and 16-bit checksum makes collisions improbable, but possible. So another, frame level checksum makes sense to me. With some blazing fast algorithm like XXH32. There's only 2 unused bytes left in short frame header, but thankfully we can still utilize long frame header. Sunshine uses version7.1.431.-1, which gives us 24-byte long header to work with. But this doesn't block LTR RFI and can be done independently.Actually scratch this. CRC-32 in lower level transports should catch complex collisions, and UDP checksum is enough for bit-flips in the routing hardware. Less work for us, yay.
Something like this maybe https://github.com/moonlight-stream/moonlight-common-c/pull/122
LTR ACKs share the queue with RFI
Ready for review.
Also this type of RFI can allow multiple clients to share one encoder instance while retaining independent error recovery. And it won't require another protocol extension, or for clients to do anything extra. Whether it's relevant for us is up for debate, but the possibility is there 😅.