pyrdp icon indicating copy to clipboard operation
pyrdp copied to clipboard

fix(359): Support fractional fps.

Open alxbl opened this issue 4 years ago • 3 comments

This pull request fixes #359

I've done two things:

  • Default FPS is now 25. (PS: It looks like a recent refactor makes it impossible to configure the FPS for the MP4 converter)
  • Use fractional division and keep track of the drift. Whenever we drift by more than one frame, an extra frame is added.

I'm not sure if the drift was a real issue though since we only updated the previous timestamp whenever at least one frame was rendered. In other words, if the timestamp between 2 PDUs is smaller than the frame rate delta, we would just let the delta increase until we've gone past the next integer frame, then render the screen at the current PDU.

The only thing that might happen is that at low frame rates, some information is lost because the screen changes faster than our frame rate can observe. RDP doesn't really have a concept of framerate, updates are sent as they happen, so it's difficult to map it to a constant frame rate.

alxbl avatar Oct 24 '21 17:10 alxbl

The code looks fine. I'll run it on a large conversion job and check drift values to confirm we are fixing the right behavior I've seen.

obilodeau avatar Oct 25 '21 17:10 obilodeau

Based on early testing, I think this needs to be revisited. I already spoke to the submitter about this privately.

Highlight: Video duration is 10 seconds more for a 34 seconds video (if we trust the master's number). This is 33%+ longer. Does it make sense?

Below is some evidence collected on a small capture that I can share privately.

Little performance impact, most likely attributable to duration.

Original:

(venv) olivier@barachois:~/gosecure/research/projets/2021-10_pyrdp-video-precision$ time pyrdp-convert.py ../2021-08_pyrdp-1.1.0/rdp_replay_20210826_12-15-33_512_Stephen215343.pyrdp -f mp4 -o ./
[*] Converting '../2021-08_pyrdp-1.1.0/rdp_replay_20210826_12-15-33_512_Stephen215343.pyrdp' to MP4
100% (1465 of 1465) |##############################################| Elapsed Time: 0:00:42 Time:  0:00:42

[+] Succesfully wrote '/home/olivier/Documents/gosecure/research/projets/2021-10_pyrdp-video-precision/rdp_replay_20210826_12-15-33_512_Stephen215343.mp4'

real	0m46.372s
user	1m13.356s
sys	0m1.752s

with PR:

(venv) olivier@barachois:~/gosecure/research/projets/2021-10_pyrdp-video-precision$ time pyrdp-convert.py ../2021-08_pyrdp-1.1.0/rdp_replay_20210826_12-15-33_512_Stephen215343.pyrdp -f mp4 -o ./
[*] Converting '../2021-08_pyrdp-1.1.0/rdp_replay_20210826_12-15-33_512_Stephen215343.pyrdp' to MP4
100% (1465 of 1465) |##############################################| Elapsed Time: 0:00:46 Time:  0:00:46

[+] Succesfully wrote '/home/olivier/Documents/gosecure/research/projets/2021-10_pyrdp-video-precision/rdp_replay_20210826_12-15-33_512_Stephen215343.mp4'

real	0m49.827s
user	1m18.960s
sys	0m1.620s

larger file size (it's longer):

-rw-r--r-- 1 olivier olivier 5.5M Oct 27 12:00 small-fix-drift.mp4
-rw-r--r-- 1 olivier olivier 4.0M Oct 27 11:59 small-original.mp4

ffprobe on result with PR:

  Duration: 00:00:44.64, start: 0.080000, bitrate: 1021 kb/s
  Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x1324, 1019 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)

ffprobe on result with master:

  Duration: 00:00:34.30, start: 0.066667, bitrate: 958 kb/s
  Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x1324, 956 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)

Packet(frame?) count, original:

$ ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 small-original.mp4 
1029

with PR:

$ ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 small-fix-drift.mp4 
1116

obilodeau avatar Oct 27 '21 18:10 obilodeau

As discussed offline, I think we need to fully revisit how to calculate FPS/insert still frames.

As a starting point, we can know the expected wallclock duration of the recording based on the first PDU time and last PDU time. From there, it should be easy to calculate the total number of frames and make sure that they are dispersed evenly through the recording.

Then PDUs should be snapped to the nearest(?) frame. I'll probably abandon this or push significant changes when I have a chance.

alxbl avatar Oct 27 '21 20:10 alxbl