> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.stereolabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.stereolabs.com/_mcp/server.

# Video Recording

The ZED SDK allows you to record large video datasets using H.264, H.265 or lossless compression. The ZED SDK uses StereoLabs' SVO format to store videos along with additional metadata such as timestamps and sensor data.

When loading SVO files, the ZED API will behave as if a ZED was connected and a live feed was available. Every module of the ZED API will be available: depth, tracking, spatial mapping and more. To record SVO videos, you can use the [ZED Explorer](/docs/development/zed-tools/zed-explorer) or [ZED Studio](/docs/development/zed-tools/zed-studio#svo-recording) applications in GUI or command-line mode or build your own recording app using the [ZED API](#video-recording).

## What an SVO File Contains

An SVO file is a recording of what the **camera** outputs, not of what the **ZED SDK** computes. It only contains:

* **The unrectified video stream**, as output by the camera after its [ISP](#what-cannot-be-changed-at-playback): left and right images for a stereo camera, a single image for a monocular camera, encoded with the selected [compression mode](#compression-modes).
* **The sensor data** that the camera actually provides, stored as metadata alongside the video: IMU (accelerometer and gyroscope) and temperature on the models that embed them, and magnetometer and barometer on the ZED 2 and ZED 2i only. Check the [sensor availability table](/docs/development/zed-sdk/modules/sensors/#sensor-availability) for your camera model. With [SVO2](#using-svo2), each sensor is recorded at its own full rate instead of the image frame rate.
* **The information required to replay the stream**: camera model and serial number, resolution, frame rate, image and sensor timestamps, and any [custom data](#using-svo2) you recorded yourself.

> **Note**
>
> The [calibration parameters](/docs/development/zed-sdk/modules/camera/camera-calibration) are **not** stored in the SVO file. They are resolved at playback time from the `settings` folder of the machine reading the file, using the serial number recorded in it, and downloaded from the StereoLabs servers if no local file is found. Replacing that `SNxxxxxxx.conf` file with a [custom calibration](/docs/development/zed-sdk/modules/camera/camera-calibration#the-calibration-file) therefore changes how an existing recording is rectified and how its depth is computed.

No processed data is ever written to the file. Rectified images, depth maps, point clouds, normal maps, confidence maps, camera poses, spatial maps, detected objects and bodies are **not** stored in an SVO file.

### Everything Is Computed Live at Playback

While an SVO file is played back, the ZED SDK processes the raw frames exactly as it does with a live camera: rectification, depth estimation, positional tracking, spatial mapping, object and body detection all run on your machine as the file is read.

The configuration used during the recording session therefore does not constrain the playback session. A file recorded with `DEPTH_MODE::NONE` and no module enabled can be replayed with `DEPTH_MODE::NEURAL_PLUS`, positional tracking and object detection all enabled, and every one of them will produce results. The same applies to the depth range, the confidence and texture thresholds, the coordinate system and the unit system: they are chosen when you open the file, not when you record it.

> **Note**
>
> **Can I get depth from a file recorded with no depth mode?** Yes, on a stereo camera. Depth is never stored in the file, it is always computed from the raw left and right images at playback time, so `DEPTH_MODE` is a playback choice. Monocular recordings (ZED X One) contain a single image stream and cannot produce depth.

> **Tip**
>
> Because everything is recomputed, the output of a given SVO file depends on the machine and the ZED SDK version used for the playback. Replaying the same file on a different GPU or after an SDK update can yield slightly different depth or tracking results.

### What Cannot Be Changed at Playback

The recorded images have already been processed by the camera [ISP](/docs/development/zed-sdk/modules/camera/camera-controls#adjusting-camera-settings), so everything applied before the frames were written is baked into the pixels:

* **Camera control settings** ([`sl::VIDEO_SETTINGS`](https://www.stereolabs.com/docs/api/group__Video__group.html#ga7bab4c6ca4fd971055eca1fdd9f4223d)): exposure, gain, white balance, gamma, saturation, sharpness, denoising, and so on. Calling `setCameraSettings()` has no effect during playback.
* **Resolution and frame rate**, which are fixed when the file is created.

Changing any of these requires a new recording, which is why you should make sure the image settings are correct before starting a long capture session.

## Compression Modes

SVO videos can be recorded using various compression modes. We provide both lossless and compressed modes to preserve image quality or reduce file size.

| Compression Mode    | Average Size (% of RAW) |
| ------------------- | ----------------------- |
| LOSSLESS (PNG/ZSTD) | 42%                     |
| H.264 (AVCHD)       | 1%                      |
| H.265 (HEVC)        | 1%                      |
| H.264 LOSSLESS      | 25%                     |
| H.265 LOSSLESS      | 25%                     |

### Benefits of Hardware Encoding

For optimal performance, we recommend using the `H.264` and `H.265` recording modes. They have been designed to use the hardware-based encoder (referred to as NVENC) built into NVIDIA® graphics cards. With encoding offloaded to NVENC, the GPU and the CPU are free for other operations. For example, in a compute-heavy scenario, it is possible to record video at a full frame rate with minimal impact on the main application.

### Encoding Quality

At a given bitrate, hardware encoding quality can vary depending on your GPU generation. Newer NVENC generations typically produce better quality than older ones at the same bitrate. Check the [NVENC support matrix](https://developer.nvidia.com/video-encode-decode-gpu-support-matrix) for the encoder generation of your GPU.

### Using SVO2

Release 4.1 of the ZED SDK introduced the SVO2 file format, designed to store high-frequency data from the camera and to record *custom data* in order to store data from external sensors.
This recording format is enabled by *default* in the ZED SDK starting from version 4.1.

* **High-frequency data**: SVO(1) files would previously only record sensor data at the camera's image frame rate (15-120 Hz). With SVO2, sensors are recorded at their respective frequency, enabling all ZED SDK algorithms which use high-frequency data, for example positional tracking with IMU fusion.
* **Custom Data**: the SVO2 format exposes methods to record custom data. This data can be anything defined by the user, from metadata to label the specific SVO sequence, to data from external sensors, such as IMU, GPS, etc. All custom data is timestamped to be read alongside the original ZED data. More information on the Custom Data API here: [C++](https://www.stereolabs.com/docs/api/classsl_1_1Camera.html#ae493b5b56aca8f7c3d5bf83c951d8892) / [Python](https://www.stereolabs.com/docs/api/python/classpyzed_1_1sl_1_1Camera.html#ad69ac7a2182115324600687ebf7c1824) / [C#](https://www.stereolabs.com/docs/api/csharp/classsl_1_1Camera.html#a3d8460a4f331a4b3d394f48ececfc36d) / [C](https://www.stereolabs.com/docs/api/c/zed__interface_8h.html#a9064eb0c1b06559aa6836d87f7b7f919).

Please check out the custom data [Recording](https://github.com/stereolabs/zed-sdk/tree/master/recording/recording/external_data) and [Playback](https://github.com/stereolabs/zed-sdk/tree/master/recording/playback/external_data) samples on GitHub for more information.

## Recording with Multiple Cameras

You can record videos with multiple cameras connected to the same PC. When using hardware encoding (H.264, H.265), make sure to check the [NVENC support matrix](https://developer.nvidia.com/video-encode-decode-gpu-support-matrix) or the [Jetson one](https://www.stereolabs.com/blog/h-264-h-265-video-encoding-support-matrix-for-nvidia-jetson/), which shows the maximum number of concurrent recording sessions that can be started on a single NVIDIA® GPU. You can also add multiple GPUs to a single server to increase the number of recording sessions with hardware encoding.

## Using the Recording API

### Video Recording

To record SVO files, you need to enable the Recording module with `enableRecording()`. Specify an output file name (eg: *output.svo*) and `SVO_COMPRESSION_MODE`, then save each grabbed frame. SVO lets you record video and associated metadata (timestamp, IMU data and more if available).

**`C++`**

```cpp C++
// Create and open a ZED camera object
Camera zed;
InitParameters init_parameters;
ERROR_CODE err = zed.open(init_parameters);
if (err != ERROR_CODE::SUCCESS)
    exit(-1);

// Enable recording with the filename specified in argument
String output_path(argv[1]);
RecordingParameters recordingParameters;
recordingParameters.compression_mode = SVO_COMPRESSION_MODE::H264;
recordingParameters.video_filename = output_path;
err = zed.enableRecording(recordingParameters);

while (!exit_app) {
    // Each new frame is added to the SVO file
    zed.grab();      
}
// Disable recording
zed.disableRecording();
```

**`Python`**

```python Python
# Create and open a ZED camera object
zed = sl.Camera()
init_parameters = sl.InitParameters()
err = zed.open(init_parameters)
if err != sl.ERROR_CODE.SUCCESS:
    exit(-1)

# Enable recording with the filename specified in argument
output_path = sys.argv[1]
recordingParameters = sl.RecordingParameters()
recordingParameters.compression_mode = sl.SVO_COMPRESSION_MODE.H264
recordingParameters.video_filename = output_path
err = zed.enable_recording(recordingParameters)

while not exit_app:
    # Each new frame is added to the SVO file
    zed.grab()

# Disable recording
zed.disable_recording()
```

**`C#`**

```csharp C#
// Create and open a ZED camera object
sl.Camera zed = new sl.Camera(0);
sl.InitParameters init_parameters = new sl.InitParameters();
sl.ERROR_CODE err = zed.Open(ref init_parameters);
if (err != sl.ERROR_CODE.SUCCESS)
    Environment.Exit(-1);
sl.RuntimeParameters runtimeParameters = new sl.RuntimeParameters();
// Enable recording with the filename specified in argument
string output_path = args[0];
sl.RecordingParameters recordingParameters = new sl.RecordingParameters();
recordingParameters.compressionMode = sl.SVO_COMPRESSION_MODE.H264_BASED;
recordingParameters.videoFilename = output_path;
err = zed.EnableRecording(recordingParameters);

while (!exit_app) {
    // Each new frame is added to the SVO file
    zed.Grab(ref runtimeParameters);
}
// Disable recording
zed.DisableRecording();
```

### Video Playback

To play SVO files, simply add the file path as an argument in `setFromSVOFile()`.  When loading SVO files, the ZED API will behave as if a ZED was connected and a live feed was available. Every module of the ZED API will be available: depth, tracking, spatial mapping and more, regardless of what was enabled during the recording session, as explained in [What an SVO File Contains](#what-an-svo-file-contains). When an SVO file is read entirely, `END_OF_SVOFILE_REACHED` error code is returned.

**`C++`**

```cpp C++
// Create a ZED camera object
Camera zed;

// Set SVO path for playback
String input_path(argv[1]);
InitParameters init_parameters;
init_parameters.input.setFromSVOFile(input_path);

// Open the ZED
ERROR_CODE err = zed.open(init_parameters);

sl::Mat svo_image;
while (!exit_app) {
    ERROR_CODE grab_status = zed.grab();
    if (grab_status == ERROR_CODE::SUCCESS) {
        // Read side by side frames stored in the SVO
        zed.retrieveImage(svo_image, VIEW::SIDE_BY_SIDE);
        // Get frame count      
        int svo_position = zed.getSVOPosition();
    }
    else if (grab_status == ERROR_CODE::END_OF_SVOFILE_REACHED) {
        std::cout << "SVO end has been reached. Looping back to first frame" << std::endl;
        zed.setSVOPosition(0);
    }
}
```

**`Python`**

```python Python
# Create a ZED camera object
zed = sl.Camera()

# Set SVO path for playback
input_path = sys.argv[1]
init_parameters = sl.InitParameters()
init_parameters.set_from_svo_file(input_path)

# Open the ZED
err = zed.open(init_parameters)

svo_image = sl.Mat()
while not exit_app:
  grab_status = zed.grab()
  if grab_status == sl.ERROR_CODE.SUCCESS:
    # Read side by side frames stored in the SVO
    zed.retrieve_image(svo_image, sl.VIEW.SIDE_BY_SIDE)
    # Get frame count
    svo_position = zed.get_svo_position()
  elif grab_status == sl.ERROR_CODE.END_OF_SVOFILE_REACHED:
    print("SVO end has been reached. Looping back to first frame")
    zed.set_svo_position(0)
```

**`C#`**

```csharp C#
// Create a ZED camera object
sl.Camera zed = new sl.Camera(0);

sl.RuntimeParameters runtimeParameters = new sl.RuntimeParameters();

// Set SVO path for playback
string input_path = args[0];
sl.InitParameters init_parameters = new sl.InitParameters();
init_parameters.inputType = sl.INPUT_TYPE.SVO;
init_parameters.pathSVO = input_path;

// Open the ZED
sl.ERROR_CODE err = zed.Open(ref init_parameters);

sl.Mat svo_image = new sl.Mat();
while (!exit_app) {
    if (zed.Grab(ref runtimeParameters) == sl.ERROR_CODE.SUCCESS) {
        // Read side by side frames stored in the SVO
        zed.RetrieveImage(svo_image, sl.VIEW.SIDE_BY_SIDE, sl.MEM.CPU);
        // Get frame count      
        int svo_position = zed.GetSVOPosition();
    }
    if (zed.GetSVOPosition() >= zed.GetSVONumberOfFrames() - 1) {
        Console.WriteLine("SVO end has been reached. Looping back to first frame");
        zed.SetSVOPosition(0);
    }
}
```

### Extracting IMU Data from an SVO2 File

SVO2 files store sensor data at each sensor's own frequency, as described in [Using SVO2](#using-svo2). During playback, that data is read back with the same `getSensorsData()` call used with a live camera, and the `TIME_REFERENCE` you pass decides what you get:

* `TIME_REFERENCE::IMAGE` returns the sample closest to the current video frame, so you get one sample per `grab()` call. This is what the example below does.
* `TIME_REFERENCE::CURRENT` returns the most recent sample independently of `grab()`. Call it more often than you call `grab()` and compare timestamps to walk through the samples recorded between two frames, as explained in [Retrieve New Sensor Data](/docs/development/zed-sdk/modules/sensors/using-the-api/#retrieve-new-sensor-data).

**`C++`**

```cpp C++
// Open the SVO file (same as standard playback)
Camera zed;
InitParameters init_parameters;
init_parameters.input.setFromSVOFile("recording.svo2");
zed.open(init_parameters);

SensorsData sensors_data;
while (!exit_app) {
    if (zed.grab() == ERROR_CODE::SUCCESS) {
        // Retrieve the IMU sample closest to the current frame
        zed.getSensorsData(sensors_data, TIME_REFERENCE::IMAGE);

        SensorsData::IMUData imu = sensors_data.imu;
        // imu.linear_acceleration : accelerometer, in m/s²
        // imu.angular_velocity    : gyroscope, in deg/s
        // imu.pose                : orientation (IMU 6-DoF fusion)
    }
}
```

**`Python`**

```python Python
import pyzed.sl as sl

# Open the SVO file (same as standard playback)
zed = sl.Camera()
init_parameters = sl.InitParameters()
init_parameters.set_from_svo_file("recording.svo2")
zed.open(init_parameters)

sensors_data = sl.SensorsData()
while not exit_app:
    if zed.grab() == sl.ERROR_CODE.SUCCESS:
        # Retrieve the IMU sample closest to the current frame
        zed.get_sensors_data(sensors_data, sl.TIME_REFERENCE.IMAGE)

        imu = sensors_data.get_imu_data()
        # imu.get_linear_acceleration() : accelerometer, in m/s²
        # imu.get_angular_velocity()    : gyroscope, in deg/s
        # imu.get_pose()                : orientation (IMU 6-DoF fusion)
```

**`C#`**

```csharp C#
// Open the SVO file (same as standard playback)
sl.Camera zed = new sl.Camera(0);
sl.InitParameters init_parameters = new sl.InitParameters();
init_parameters.inputType = sl.INPUT_TYPE.SVO;
init_parameters.pathSVO = "recording.svo2";
zed.Open(ref init_parameters);

sl.RuntimeParameters runtimeParameters = new sl.RuntimeParameters();
sl.SensorsData sensors_data = new sl.SensorsData();
while (!exit_app) {
    if (zed.Grab(ref runtimeParameters) == sl.ERROR_CODE.SUCCESS) {
        // Retrieve the IMU sample closest to the current frame
        zed.GetSensorsData(ref sensors_data, sl.TIME_REFERENCE.IMAGE);

        sl.ImuData imu = sensors_data.imu;
        // imu.linearAcceleration : accelerometer, in m/s²
        // imu.angularVelocity    : gyroscope, in deg/s
        // imu.fusedOrientation   : orientation quaternion
    }
}
```

> **Note**
>
> Like every other module, those that rely on sensor data can be run at playback time even if they were not enabled during the recording session. You can, for example, call `enablePositionalTracking()` while reading an SVO2 file and get a trajectory from a recording made without positional tracking.
>
> Cameras without an IMU record no motion data, so nothing can be recovered from their SVO files. Check the [sensor availability table](/docs/development/zed-sdk/modules/sensors/#sensor-availability) for your camera model.

## Code Example

Check out the [SVO Recording](https://github.com/stereolabs/zed-sdk/tree/master/recording/recording), [SVO Playback](https://github.com/stereolabs/zed-sdk/tree/master/recording/playback) and [SVO Export](https://github.com/stereolabs/zed-sdk/tree/master/recording/export) samples on GitHub.