Skip to content

Add recording#193

Merged
yondonfu merged 4 commits intomainfrom
rafal/recording
Jan 20, 2026
Merged

Add recording#193
yondonfu merged 4 commits intomainfrom
rafal/recording

Conversation

@leszko
Copy link
Copy Markdown
Collaborator

@leszko leszko commented Nov 27, 2025

Env variables

RECORDING_ENABLED

  • Default: true
  • Description: Enable/disable recording. When false, recording is disabled.

RECORDING_MAX_LENGTH

  • Default: 1h
  • Description: Maximum total recording length (sum of all segments). Recording stops when reached.
  • Format: Supports 1h, 30m, 120s, or plain seconds (e.g., 3600)

RECORDING_STARTUP_CLEANUP_ENABLED

  • Default: true
  • Description: Enable/disable cleanup of recording files

Assumptions / Comments

  • The recording stream is sourced from the same place we send frames to the frontend.
    • This means that if a frame is dropped, it will also be missing from the recording (sorry, Ryan).
    • The main benefit is that we can piggy-back on our existing PTS calculation mechanism, and aiortc.contrib.media.MediaRecorder.
  • Temporary recordings are stored in the OS temp directory.
  • Both stream pause/resume and exporting a recording cause the backend to segment/clip the stream. The final recording is a concatenation of these segments when downloaded. This is mainly due to how pause handling works and the behavior of MediaRecorder, which needs to finalize the stream before exporting.
  • The pause/resume feature adds significant complexity to recording logic. On the backend we keep sending the same frame while paused, but we don't want repeated identical frames in the final recording.
    Recording input may be tricky because of the pause/resume (we don't pause input stream!)
  • We use ffmpeg (as Python library) for mp4 segments concatenation; I experimented with using AV library, but it happened to be surprisingly difficult (a lot of corner cases, etc.), so I ended up with ffmpeg. AV is still an option, but required more work and maintenance. One other option is to use the moviepy library
  • MAX_FPS is decreased to 30 because 60 FPS won't work with MediaRecorder, because MediaRecorder calculates PTS/DTS on its own and if we start delivering frames at 60FPS, it fails because PTS/DTS are duplicated

Demo with comments

  1. Paused video is visible in the recording
  2. When the recording is downloaded the second time, then it has the length of the whole video, but the Start Time is from the last recording time. So depending on the player you may see a blank screen at the beginning (you see in Quick Time, but VLC or ffplay respects the Start Time and starts from where the video started). e.g.:
  • Play stream, after 00:10:00, you download the recording; stream keeps playing
  • After another 00:10:00, you click download again,
  • The results stream has Duration 00:20:00 and Start Time 00:10:00
  • QuickTime does not respect Start Time, so you'll see first 00:10:00 blank black screen
demo_recording.mp4

fix #56

@leszko leszko force-pushed the rafal/recording branch 4 times, most recently from 468393a to d953d15 Compare November 28, 2025 14:02
@leszko leszko marked this pull request as ready for review November 28, 2025 14:25
@leszko leszko requested a review from yondonfu November 28, 2025 14:59
Comment thread pyproject.toml Outdated
Comment thread src/scope/server/app.py Outdated
Comment thread src/scope/server/webrtc.py Outdated
Comment thread src/scope/server/recording.py Outdated
@leszko leszko force-pushed the rafal/recording branch 8 times, most recently from 4134dca to 648ee01 Compare January 20, 2026 14:35
Signed-off-by: Rafal Leszko <rafal@livepeer.org>
Signed-off-by: Rafal Leszko <rafal@livepeer.org>
@leszko leszko requested a review from yondonfu January 20, 2026 16:39
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
Signed-off-by: Yondon Fu <yondon.fu@gmail.com>
@yondonfu
Copy link
Copy Markdown
Contributor

fb9ff00 fixes the black screen issue at the start of all recordings after the first one in a single session by normalizing timestamps so that they start from 0. It also switches MAX_FPS back to 60 to avoid affecting other streaming logic and instead introduces a RECORDING_MAX_FPS value set to 30 (which is what MediaRecorder uses as a hardcoded value) and when recording drops frames if they come faster than this value.

40e455f adjusts spacing for buttons in ExportDialog which felt very cramped previously.

Copy link
Copy Markdown
Contributor

@yondonfu yondonfu left a comment

Choose a reason for hiding this comment

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

LGTM

@yondonfu yondonfu merged commit 1f04268 into main Jan 20, 2026
5 checks passed
@yondonfu yondonfu deleted the rafal/recording branch January 20, 2026 17:11
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.

Add support for recording

2 participants