---
sidebar:
  hidden: true
title: roboto.experimental.video.frames
---
On-demand extraction of decoded frames from a time range of compressed video.

A time range of video usually starts mid-GOP: its leading delta frames are only decodable after the keyframe that precedes the range. This module hides that — callers provide a way to load `(log_time, data)` messages for a time range, ask for a range, and receive one decoded frame per decodable frame in it. When the range starts mid-GOP, the preceding keyframe is found by walking backward (bounded by `keyframe_lookback_ns`) and the prefix is decoded but not emitted.

## Module Contents

### DEFAULT_KEYFRAME_LOOKBACK_NS

```python
roboto.experimental.video.frames.DEFAULT_KEYFRAME_LOOKBACK_NS: int = 10000000000
```

`from roboto.experimental.video import DEFAULT_KEYFRAME_LOOKBACK_NS`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/video/frames.py#L39-L39)

How far before a requested range to search for the keyframe that makes the range's leading delta frames decodable. Bounds the cost of the backward walk on streams with pathological keyframe intervals.

### MessageRangeLoader

```python
roboto.experimental.video.frames.MessageRangeLoader
```

`from roboto.experimental.video import MessageRangeLoader`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/video/frames.py#L30-L30)

Loads a topic's `(log_time, data)` messages for a `(start_time, end_time)` range.

Both times are nanoseconds since Unix epoch; messages must come back in ascending log-time order. Whether `end_time` is inclusive follows the underlying reader — frame emission is bounded by log-time filtering, not by the loader's boundary semantics.

### decode_frames_in_range()

```python
def roboto.experimental.video.frames.decode_frames_in_range(
    load_messages: MessageRangeLoader,
    start_time: int,
    end_time: int,
    keyframe_lookback_ns: int = DEFAULT_KEYFRAME_LOOKBACK_NS,
    codec: roboto.experimental.video.codec.VideoCodec = H264,
) -> collections.abc.Generator[roboto.experimental.video.decoder.DecodedVideoFrame, None, None]
```

`from roboto.experimental.video import decode_frames_in_range`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/experimental/video/frames.py#L45-L100)

Decode the compressed-video frames of a time range.

Frames in the range that are undecodable — e.g. delta frames whose keyframe lies further back than `keyframe_lookback_ns` — are silently omitted; no player could render them either.

**Parameters**

- **load_messages** (`MessageRangeLoader`): Loads the topic's `(log_time, data)` messages for a time range; called once for the requested range and, when the range starts mid-GOP, once more for the keyframe lookback before it.
- **start_time** (`int`): Start of the range in nanoseconds since Unix epoch (inclusive).
- **end_time** (`int`): End of the range in nanoseconds since Unix epoch; passed through to `load_messages`.
- **keyframe_lookback_ns** (`int`): Upper bound on how far before `start_time` to search for the keyframe that anchors the range's leading delta frames.
- **codec** (`roboto.experimental.video.codec.VideoCodec`): The codec of every loaded frame (defaults to H.264); supplies keyframe detection and the PyAV decoder. Resolve it from the stream's `format` token via [`resolve_codec()`](/reference/python-sdk/roboto/experimental/video/codec#roboto.experimental.video.codec.resolve_codec).

**Yields**

- One [`DecodedVideoFrame`](/reference/python-sdk/roboto/experimental/video/decoder#roboto.experimental.video.decoder.DecodedVideoFrame) per decodable frame with `log_time >= start_time`, in ascending log-time order.

**Raises**

- `ImportError`: If PyAV is not installed (`roboto[video]`).

**Returns**

- `collections.abc.Generator[roboto.experimental.video.decoder.DecodedVideoFrame, None, None]`

**Usage**

```python
from roboto.experimental.video import decode_frames_in_range
frames = decode_frames_in_range(
    load_messages=my_loader, start_time=start_ns, end_time=end_ns
)
next(frames).to_image()
```
