Optimize MP4 forensic replay export (~12x faster, idle skip)#510
Open
jmvermeulen wants to merge 3 commits intoGoSecure:mainfrom
Open
Optimize MP4 forensic replay export (~12x faster, idle skip)#510jmvermeulen wants to merge 3 commits intoGoSecure:mainfrom
jmvermeulen wants to merge 3 commits intoGoSecure:mainfrom
Conversation
- Reduce FPS 30→10 for forensic captures (sufficient for review) - Batch multiple renders per frame window instead of encoding per-PDU - Advance PTS to skip idle gaps without encoding duplicate still frames - Switch to h264 ultrafast preset for faster encoding - Use faststart movflag for seekable output - Set GOP size to 5s for reliable timeline seeking
H264 requires even dimensions. Previously the full surface was scaled every frame to add 1px — profiling showed this cost ~6ms per frame (11% of encode time). Now pads the output surface by 1px via drawImage instead, avoiding the expensive rescale.
Detect idle periods by absence of FAST_PATH_INPUT PDUs (keyboard/mouse) instead of screen changes. Cursor blinks and screen refreshes no longer prevent idle detection. During idle, PTS is frozen with one frame encoded every 10s for state capture. True PDU gaps are also compressed.
Author
|
Tested at a 4 hour recording since this was almost impossible to convert without these fixes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--idle-skip Noption to compress periods without user input, producing shorter review videosProblem
Converting large
.pyrdpreplay files to MP4 was extremely slow. The bottleneck was encoding an H264 frame on every single PDU — even when multiple PDUs fell within the same frame window, and even during long idle gaps where the screen didn't change.Changes
Commit 1: Frame batching + PTS advance + ultrafast preset
onFinishRender()now sets adirtyflag instead of immediately encodingonPDUReceived()only encodes at 100ms frame boundaries whendirty=Trueultrafastfrag_keyframe+empty_moovtofaststartfor proper seekingCommit 2: Pad instead of scale for odd dimensions
QImage.scaled()every frame — profiling showed this cost ~6ms/frame (11% of encode time)drawImage, avoiding the expensive rescaleCommit 3: Idle skip (
--idle-skip N)--idle-skip NCLI option to compress periods of user inactivity longer than N secondsFAST_PATH_INPUTPDUs (keyboard/mouse), not screen changes — so cursor blinks and screen refreshes don't reset the idle timer--idle-skip 30is a good starting point for forensic reviewProfiling methodology
Benchmarked against clean main on a real multi-hour forensic capture (20K PDUs, wide-format resolution).
PDU type analysis revealed 96.3% GDI drawing orders, 0% bitmap updates — the bottleneck was not bitmap decompression but frame encoding.
cProfile breakdown (500 PDUs in the heaviest section):
writeFrameQImage.scaled/ padEnd-to-end throughput (2000 PDUs, vs clean main):
Frame encode statistics (full file):
Test plan
--idle-skipproduces identical output to previous behavior--idle-skipproduces shorter video, skipping input-idle periods