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(359): Support fractional fps. #364

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions pyrdp/convert/MP4EventHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pyrdp.player.RenderingEventHandler import RenderingEventHandler

import logging
from math import floor

import av
import qimage2ndarray
Expand Down Expand Up @@ -38,7 +39,7 @@ def screen(self) -> QImage:

class MP4EventHandler(RenderingEventHandler):

def __init__(self, filename: str, fps=30, progress=None):
def __init__(self, filename: str, fps=25, progress=None):
"""
Construct an event handler that outputs to an Mp4 file.

Expand All @@ -57,16 +58,17 @@ def __init__(self, filename: str, fps=30, progress=None):
# we could probably batch the encoding of several frames and benefit from threads
# but trying this as-is lead to no gains
# (actually a degradation but that could be statistically irrelevant)
#self.stream.thread_count = 4
# self.stream.thread_count = 4
self.stream.pix_fmt = 'yuv420p'
self.progress = progress
self.scale = False
self.mouse = (0, 0)
self.fps = fps
self.delta = 1000 // fps # ms per frame
self.delta = 1000 / fps # ms per frame
self.log = logging.getLogger(__name__)
self.log.info('Begin MP4 export to %s: %d FPS', filename, fps)
self.timestamp = self.prevTimestamp = None
self.drift = 0 # Support non-integer frame rates.

super().__init__(MP4Image())

Expand All @@ -84,12 +86,17 @@ def onPDUReceived(self, pdu: PlayerPDU):
dt = self.delta
else:
dt = self.timestamp - self.prevTimestamp # ms
nframes = (dt // self.delta)
if nframes > 0:
for _ in range(nframes):

# Prevent drifting when fps doesn't perfectly divide a second.
nframes = (dt / self.delta) + self.drift
nwhole = floor(nframes)
self.drift = nframes - nwhole # update drift.

if nwhole > 0: # Only take whole frames.
for _ in range(nwhole):
self.writeFrame()
self.prevTimestamp = ts
self.log.debug('Rendered %d still frame(s)', nframes)
self.log.debug('Rendered %d still frame(s) Drift=%f', nwhole, self.drift)

def cleanup(self):
# Add one second worth of padding so that the video doesn't end too abruptly.
Expand Down