Raw 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

// 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].url

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.json

Raw 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-on
    • available — Recording is not started by default but can be started during the call
    • disabled — Raw recording is completely disabled for this call type or call
    • auto-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.
// 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):

User Permissions

The following permissions are checked when users interact with the recording API:

  • StartRecording required to start the recording
  • StopRecording required 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-ugly

Ubuntu/Debian:

sudo apt install ffmpeg gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly

Input 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 audio

Output 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=false

Options:

  • --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=false

Options:

  • --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 display

Options:

  • --media — Filter by media type: user (camera/mic), display (screenshare), or both (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 ./out

Output: 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 user123

Output files:

  • individual_*_audio_only_*.mkv — Audio-only per participant
  • individual_*_video_only_*.mkv — Video-only per participant
  • individual_*_audio_video_*.mkv — Combined A/V per participant
  • composite_*_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"
done

Create 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 ./mixed

Process 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 ./out

Generate 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