# 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

<tabs groupId="examples">

<tabs-item value="js" label="JavaScript">

```js
// 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
```

</tabs-item>

<tabs-item value="py" label="Python">

```py
# Start raw recording
# Triggers webhook event: call.recording_started
call.start_recording("raw")

# Stop raw recording
# Triggers webhook event: call.recording_stopped
# When processing completes: call.recording_ready (contains archive URL)
call.stop_recording("raw")

# List all recordings for this call
response = call.list_recordings()

# Download using the URL from response.data.recordings or the call.recording_ready webhook payload
# response.data.recordings[0].url
```

</tabs-item>

<tabs-item value="go" label="Golang">

```go
// Start raw recording
// Triggers webhook event: call.recording_started
call.StartRecording(ctx, "raw", &getstream.StartRecordingRequest{})

// Stop raw recording
// Triggers webhook event: call.recording_stopped
// When processing completes: call.recording_ready (contains archive URL)
call.StopRecording(ctx, "raw", &getstream.StopRecordingRequest{})

// List all recordings for this call
response, err := call.ListRecordings(ctx, &getstream.ListRecordingsRequest{})

// Download using the URL from response.Data.Recordings or the call.recording_ready webhook payload
// response.Data.Recordings[0].Url
```

</tabs-item>

<tabs-item value="curl" label="cURL">

```bash
# Start raw recording
# Triggers webhook event: call.recording_started
curl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings/raw/start?api_key=${API_KEY}" \
    -H "Authorization: ${TOKEN}" \
    -H "stream-auth-type: jwt"

# Stop raw recording
# Triggers webhook event: call.recording_stopped
# When processing completes: call.recording_ready (contains archive URL)
curl -X POST "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings/raw/stop?api_key=${API_KEY}" \
    -H "Authorization: ${TOKEN}" \
    -H "stream-auth-type: jwt"

# List all recordings for this call
# Download using the URL from response.recordings or the call.recording_ready webhook payload
curl "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}/recordings?api_key=${API_KEY}" \
    -H "Authorization: ${TOKEN}" \
    -H "stream-auth-type: jwt"
```

</tabs-item>

</tabs>

## 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](/video/docs/api/recording/composite/). For per-participant tracks without manual post-processing, consider [individual recording](/video/docs/api/recording/individual-track/).

## 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)

```bash
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](/video/docs/api/recording/audio-only/#raw-audio).

<tabs groupId="examples">

<tabs-item value="js" label="JavaScript">

```js
// 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",
    },
  },
});
```

</tabs-item>

<tabs-item value="py" label="Python">

```py
from getstream.models import CallSettingsRequest, RawRecordingSettingsRequest

# Per-call override
call.update(
    settings_override=CallSettingsRequest(
        raw_recording=RawRecordingSettingsRequest(
            mode="available",
        )
    ),
)

# Default for a call type
client.video.update_call_type(
    "default",
    settings=CallSettingsRequest(
        raw_recording=RawRecordingSettingsRequest(
            mode="auto-on",
        )
    ),
)
```

</tabs-item>

<tabs-item value="go" label="Golang">

```go
// Per-call override
call.Update(ctx, &getstream.UpdateCallRequest{
  SettingsOverride: &getstream.CallSettingsRequest{
    RawRecording: &getstream.RawRecordingSettingsRequest{
      Mode:        "available",
    },
  },
})

// Default for a call type
client.Video().UpdateCallType(ctx, "default", &getstream.UpdateCallTypeRequest{
  Settings: &getstream.CallSettingsRequest{
    RawRecording: &getstream.RawRecordingSettingsRequest{
      Mode:        "auto-on",
    },
  },
})
```

</tabs-item>

<tabs-item value="curl" label="cURL">

```bash
# Per-call override
curl -X PATCH "https://video.stream-io-api.com/api/v2/video/call/${CALL_TYPE}/${CALL_ID}?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt" \
  -H "Content-Type: application/json" \
  -d '{
    "settings_override": {
      "raw_recording": {
        "mode": "available"
      }
    }
  }'

# Default for a call type
curl -X PUT "https://video.stream-io-api.com/api/v2/video/calltypes/${CALL_TYPE_NAME}?api_key=${API_KEY}" \
  -H "Authorization: ${TOKEN}" \
  -H "stream-auth-type: jwt" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "raw_recording": {
        "mode": "auto-on"
      }
    }
  }'
```

</tabs-item>

</tabs>

## 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_started`](/video/docs/api/webhooks/events/#CallRecordingStartedEvent) when the recording has started
- [`call.recording_stopped`](/video/docs/api/webhooks/events/#CallRecordingStoppedEvent) when the recording has stopped
- [`call.recording_ready`](/video/docs/api/webhooks/events/#CallRecordingReadyEvent) when the recording is available for download
- [`call.recording_failed`](/video/docs/api/webhooks/events/#CallRecordingFailedEvent) when recording fails for any reason

## 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](https://github.com/GetStream/stream-cli).

The CLI also requires FFmpeg and GStreamer as dependencies:

**macOS:**

```bash
brew install ffmpeg gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly
```

**Ubuntu/Debian:**

```bash
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.

```bash
# 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.

```bash
# 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.

```bash
# 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.

```bash
# 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.

```bash
# 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.

```bash
# 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

```bash
# 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)

```bash
# 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

```bash
# 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

```bash
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
```


---

This page was last updated at 2026-06-23T15:36:56.676Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/api/recording/raw/](https://getstream.io/video/docs/api/recording/raw/).