Video ingest#

Upload a video file as a channel on a dataset with add_video(). With the right start time, it plays in sync with the telemetry in the same run.

Connect the client#

This guide assumes you have installed the client and setup your API keys.

from nominal.core import NominalClient

client = NominalClient.from_profile("default")

Convert the video#

add_video takes H.264 or H.265 video. For smooth playback, it also needs YUV420p pixels and regular keyframes. normalize_video() converts a file to that format with FFmpeg, which must be installed with ffprobe:

from nominal.experimental.video_processing import normalize_video

normalize_video("engine_fire.mov", "engine_fire.mp4", force=False)  # force=False keeps an existing output

It writes H.264 with a keyframe about every 2 seconds, converting any audio to AAC. resolution scales the video down, keeping its aspect ratio. Normalizing can change the number of frames, and logs a warning when it does.

Upload with a start time#

start is the absolute time of the first frame. Use the real capture time, or the video won’t line up with its telemetry.

from datetime import datetime, timezone

dataset = client.create_dataset("Engine fire tests")
file = dataset.add_video(
    "engine_fire.mp4",
    channel="engine_cam",
    start=datetime(2026, 9, 10, 14, 38, 19, tzinfo=timezone.utc),
)
file.poll_until_ingestion_completed()

If you know each frame’s capture time, pass them as epoch nanoseconds in frame_timestamps instead of start.

More files on the same channel add to it. overwrite_overlapping=True deletes earlier segments that overlap the new file.

For MCAP files, see MCAP video ingest. The standalone Video APIs, such as client.create_video(), are deprecated in favor of video channels.

Review it in a run#

Create a run over the file’s time range and attach the dataset:

assert file.bounds is not None  # set once ingestion succeeds
run = client.create_run("Engine fire", start=file.bounds.start, end=file.bounds.end)
run.add_dataset("engine_video", dataset)

In a workbook on the run, add a video panel and select engine_cam. See Runs for other ways to organize data.

Inspect and remove files#

list_video_files() lists a dataset’s video files and their metadata:

for video in dataset.list_video_files():
    print(video.name, video.num_frames, video.media_frame_rate, video.bounds)

To delete one file, call delete() on it. dataset.archive() archives the whole dataset, including its telemetry.

To process frames before uploading, see Object identification.