// Start raw recording
// Triggers webhook event: call.recording_started
await call.startRecording("raw");
// Stop raw recording
// Triggers webhook event: call.recording_stopped
// When processing completes: call.recording_ready (contains archive URL)
await call.stopRecording("raw");
// List all recordings for this call
const response = await call.listRecordings();
// Download using the URL from response.recordings or the call.recording_ready webhook payload
// response.recordings[0].urlRaw track
Raw recording (also known as unprocessed recording or packet-level recording) captures RTP packets exactly as received from participants, with no transcoding or processing. This is the most cost-effective recording option and gives you maximum flexibility for custom post-processing and encoding, though it requires additional tooling to generate playable media files.
Quickstart
How it works
When raw recording is active, the server captures each participant's RTP packets as-is and packages them into .rtpdump files (a standard format that can be inspected with Wireshark) alongside .sdp session description files and timing metadata. Everything is bundled into a single ZIP archive per call and uploaded to AWS S3 (Stream-managed by default, or your own bucket).
To convert raw recordings into playable media, use the Stream CLI tool. The CLI properly handles RTP injection with correct timing, DTX (discontinuous transmission), and packet loss recovery. Standard tools like FFmpeg cannot directly process these files since they expect RTP over UDP listeners, while the Stream CLI uses a TCP-based pipeline for reliable playback. GStreamer pipelines may also work for advanced users.
When to use raw recording
Advantages:
- Lowest cost—no real-time transcoding or rendering overhead
- Maximum flexibility—re-encode to any format, resolution, or codec later
- Preserves all original data including resolution/orientation changes and precise timing
- Ideal for compliance and archival—store everything today, process on-demand when needed
- Useful as redundancy backup in case other recording types fail
- Debug and analyze at packet level (inspect with Wireshark)
Limitations:
- Not immediately viewable—requires post-processing with Stream CLI to generate playable files
- More complex workflow compared to composite or individual recording
- Requires infrastructure to run post-processing pipelines
For ready-to-share output, consider composite recording. For per-participant tracks without manual post-processing, consider individual recording.
Use cases
- Maximum post-production flexibility: re-encode to any format, mix per your needs
- Cost-effective archival: store raw data today, generate outputs later
- Debugging and analysis: inspect tracks and timing at the packet/frame level
Output
Each call produces a single ZIP archive with raw media data and metadata. It becomes available for download when the call.recording_ready event is emitted.
Archive contents (example)
raw-recording-call-123456/
├── user_1/
│ ├── session_1/
│ │ ├── audio_1.rtpdump
│ │ ├── audio_1.sdp
│ │ ├── audio_2.rtpdump
│ │ ├── audio_2.sdp
│ │ ├── audio_3.rtpdump
│ │ ├── audio_3.sdp
│ │ ├── video_1.rtpdump
│ │ ├── video_1.sdp
│ │ ├── video_2.rtpdump
│ │ ├── video_2.sdp
│ │ └── timing_metadata.json
│ └── session_2/
│ ├── audio_1.rtpdump
│ ├── audio_1.sdp
│ └── timing_metadata.json
├── user_2/
│ └── session_1/
│ ├── audio_1.rtpdump
│ ├── audio_1.sdp
│ ├── video_1.rtpdump
│ ├── video_1.sdp
│ └── timing_metadata.json
└── user_3/
├── session_1/
│ ├── audio_1.rtpdump
│ ├── audio_1.sdp
│ └── timing_metadata.json
└── session_2/
├── audio_1.rtpdump
├── audio_1.sdp
└── timing_metadata.jsonRaw recording settings
These settings control whether raw recording is available or auto-starts, and what gets recorded. Configure them on a call type or override per-call via settings_override.
Available settings for raw_recording:
mode:disabled|available|auto-onavailable— Recording is not started by default but can be started during the calldisabled— Raw recording is completely disabled for this call type or callauto-on— Raw recording starts automatically when the call begins, including backstage
audio_only:true|false(default:false)- When
true, only audio tracks are recorded—no video packets are captured. This reduces archive size and is useful for voice-only calls, podcasts, or when video is not needed. For usage examples, see Audio-only recording.
- When
// Per-call override
await call.update({
settings_override: {
raw_recording: {
mode: "available",
},
},
});
// Default for a call type
await client.video.updateCallType({
name: "<call type name>",
settings: {
raw_recording: {
mode: "auto-on",
},
},
});Events
These events are sent to users connected to the call and your webhook/SQS, each event contains a new recording_type field that refers to the type of recording (raw in this case):
call.recording_startedwhen the recording has startedcall.recording_stoppedwhen the recording has stoppedcall.recording_readywhen the recording is available for downloadcall.recording_failedwhen recording fails for any reason
User Permissions
The following permissions are checked when users interact with the recording API:
StartRecordingrequired to start the recordingStopRecordingrequired to stop the recording
External storage
By default, raw recording archives are stored in Stream-managed S3 and retained according to your account policies. If your call type is configured to use a different storage, archives will be uploaded there.
Stream CLI for raw recordings
The Stream CLI provides post-processing tools to extract, process, and combine audio/video tracks from raw recording archives. It handles RTP packet injection with correct timing, DTX (discontinuous transmission) correction, and gap filling.
Installation
Install the Stream CLI following the instructions at github.com/GetStream/stream-cli.
The CLI also requires FFmpeg and GStreamer as dependencies:
macOS:
brew install ffmpeg gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-uglyUbuntu/Debian:
sudo apt install ffmpeg gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-uglyInput options
The CLI accepts three input sources (mutually exclusive):
--input-file <path>— Local tar.gz archive--input-dir <path>— Extracted recording directory--input-s3 <url>— S3 URL (s3://bucket/path) or presigned HTTPS URL
S3 downloads are cached locally to avoid re-downloading on subsequent commands.
Commands
list-tracks — Discover recording contents
Explore what's in a raw recording archive, with automatic screenshare detection.
# List all tracks in table format
stream-cli video raw-recording list-tracks --input-file recording.tar.gz
# Get JSON output for programmatic use
stream-cli video raw-recording list-tracks --input-file recording.tar.gz --format json
# Get user IDs only (useful for scripting)
stream-cli video raw-recording list-tracks --input-file recording.tar.gz --format users
# Filter by track type
stream-cli video raw-recording list-tracks --input-file recording.tar.gz --track-type audioOutput formats: table (default), json, users, sessions, tracks
extract-audio — Extract audio tracks
Convert audio tracks to playable MKV format with gap filling and DTX correction.
# Extract audio for all users
stream-cli video raw-recording extract-audio --input-file recording.tar.gz --output ./out
# Extract audio for specific user
stream-cli video raw-recording extract-audio --input-file recording.tar.gz --output ./out --user-id user123
# Extract a specific track
stream-cli video raw-recording extract-audio --input-file recording.tar.gz --output ./out --track-id track789
# Disable gap filling (gaps won't be filled with silence)
stream-cli video raw-recording extract-audio --input-file recording.tar.gz --output ./out --fill-gaps=falseOptions:
--user-id,--session-id,--track-id— Filter output (mutually exclusive)--fill-gaps— Fill temporal gaps with silence (default: true)--fix-dtx— Fix DTX audio shrink issues (default: true)
extract-video — Extract video tracks
Convert video tracks to playable MKV format with gap filling.
# Extract video for all users
stream-cli video raw-recording extract-video --input-file recording.tar.gz --output ./out
# Extract video for specific user
stream-cli video raw-recording extract-video --input-file recording.tar.gz --output ./out --user-id user123
# Disable gap filling (gaps won't be filled with black frames)
stream-cli video raw-recording extract-video --input-file recording.tar.gz --output ./out --fill-gaps=falseOptions:
--user-id,--session-id,--track-id— Filter output (mutually exclusive)--fill-gaps— Fill temporal gaps with black frames (default: true)
mux-av — Combine audio and video
Synchronize and combine audio and video tracks into single files.
# Mux all tracks
stream-cli video raw-recording mux-av --input-file recording.tar.gz --output ./out
# Mux tracks for specific user
stream-cli video raw-recording mux-av --input-file recording.tar.gz --output ./out --user-id user123
# Mux only camera/microphone tracks (exclude screenshare)
stream-cli video raw-recording mux-av --input-file recording.tar.gz --output ./out --media user
# Mux only screenshare tracks
stream-cli video raw-recording mux-av --input-file recording.tar.gz --output ./out --media displayOptions:
--media— Filter by media type:user(camera/mic),display(screenshare), orboth(default)
mix-audio — Mix multiple audio tracks
Merge audio from multiple participants into a single synchronized MP3 file.
# Mix all audio tracks from all users
stream-cli video raw-recording mix-audio --input-file recording.tar.gz --output ./outOutput: composite_{callType}_{callId}_audio_{timestamp}.mp3
process-all — Complete workflow
Execute audio extraction, video extraction, and muxing in a single command.
# Process all tracks
stream-cli video raw-recording process-all --input-file recording.tar.gz --output ./out
# Process tracks for specific user only
stream-cli video raw-recording process-all --input-file recording.tar.gz --output ./out --user-id user123Output files:
individual_*_audio_only_*.mkv— Audio-only per participantindividual_*_video_only_*.mkv— Video-only per participantindividual_*_audio_video_*.mkv— Combined A/V per participantcomposite_*_audio_*.mp3— Mixed audio from all participants
Workflow examples
Extract audio for each participant separately
# 1. Discover participants
stream-cli video raw-recording list-tracks --input-file call.tar.gz --format users
# 2. Extract each participant's audio
for user in $(stream-cli video raw-recording list-tracks --input-file call.tar.gz --format users); do
echo "Extracting audio for user: $user"
stream-cli video raw-recording extract-audio --input-file call.tar.gz --output ./extracted --user-id "$user"
doneCreate a mixed audio file (podcast-style)
# Mix all participants into single audio file
stream-cli video raw-recording mix-audio --input-file conference.tar.gz --output ./mixedProcess directly from S3
# Using S3 URL (requires AWS credentials)
stream-cli video raw-recording process-all --input-s3 s3://mybucket/recordings/call.tar.gz --output ./out
# Using presigned URL (no AWS credentials needed)
stream-cli video raw-recording process-all --input-s3 "https://mybucket.s3.amazonaws.com/call.tar.gz?X-Amz-..." --output ./outGenerate all outputs at once
stream-cli video raw-recording process-all --input-file recording.tar.gz --output ./complete
# Results:
# ./complete/individual_*_audio_only_*.mkv
# ./complete/individual_*_video_only_*.mkv
# ./complete/individual_*_audio_video_*.mkv
# ./complete/composite_*_audio_*.mp3