Skip to main content
One connection is one camera or encoder. Mixpeek cuts the stream into fixed-length segments, and each completed segment becomes one bucket object.

Overview

Each sync run opens the stream, captures segment_seconds * segments_per_run seconds, and closes it.
Capture is windowed, not gapless. Whatever the camera shows between runs is not captured. A sync polling every 300 seconds with a 30 second window keeps 10% of the wall clock.An RTSP connection samples the stream. It does not archive it.
Coverage is segment_seconds * segments_per_run divided by polling_interval_seconds. Raise either of the first two to widen it, or shorten the poll interval. A longer poll interval narrows coverage, because it is the divisor.
Keep the run shorter than the interval. A run holds the stream open for the whole capture plus about 9 seconds of setup. Runs overlap when that total exceeds polling_interval_seconds.This caps coverage below 100%. On a 300 second interval the ceiling is 97%; on a 60 second interval it is 84%.

Prerequisites

  • A camera or encoder reachable over rtsp:// or rtsps://.
  • Network access from Mixpeek to that host and port.
  • The stream’s username and password, if it is protected.

Configuration

Connection-level fields

Sync-level fields

Credentials go in credentials, never in the URL

Most camera vendors document an RTSP URL as rtsp://user:pass@host:554/stream. Mixpeek rejects that form at validation time.
A URL carrying user:pass@ travels through every log line, error message, and connection listing that prints the endpoint. Splitting the fields keeps the endpoint safe to display. Write it this way instead:
The scheme check rejects anything other than rtsp:// and rtsps://. Many public camera feeds published as “streams” are HLS, which needs a different connector.

segment_seconds is a target, not a guarantee

Segment cuts land on keyframes. A camera sending a keyframe every 3 seconds cannot produce a cut at an arbitrary second. Measured against such a camera, a request for 10 second segments produced segments of 11.8, 9.0, and 12.0 seconds.
Put the camera’s keyframe interval in your budget before you size a downstream limit on segment length or file size.

Choosing segment_seconds

Start at the default of 30. Capture cost does not depend on segment length. Setup costs about 9.4 seconds per run whatever the configuration, and the marginal cost per segment is zero. Capture uses stream copy, so cutting more segments does not re-encode anything. Measured against a live camera, four ways of capturing the same 24 seconds: The cost that scales is downstream. Each segment is one object, one storage write, and one pipeline invocation. Halving segment length doubles the invocation count for the same footage. Size the segment against what your pipeline charges per invocation, not against capture. Shorten a segment to get a result sooner. Latency to a first result is roughly segment_seconds plus 9.4 seconds plus your pipeline’s time. A 30 second segment means you learn about a moment about 40 seconds after it happens. A static scene argues for longer segments. Deduplication granularity is the segment, so a segment holding 5 seconds of activity still counts as changed.
Ask the camera for its keyframe interval before picking a number below 30.
A camera sending a keyframe every 10 seconds cannot produce 5 second segments.

Dropping segments that do not matter

An overnight camera captures the same empty room every run. Two filters drop those before they cost extraction, and they run in that order. scene_change_threshold needs no model. It is a Hamming distance over a 64-bit perceptual hash of each segment’s first frame. A segment is dropped when its distance from the previous kept segment is at or below the number. It costs a few milliseconds of CPU per segment, so it runs first and anything it drops never reaches the gate. A gate judges each segment, and there are two ways to write one. They are mutually exclusive. Reach for the retriever first. The policy is then an object you can read, edit, and version per camera, and changing what a camera keeps does not need a deploy. A plugin is the right answer when the decision is not a similarity question. gate_retriever_min_score keeps the segment when the retriever’s top result scores at or above it. Leave it unset to keep on any result, which is right when the retriever’s own stages already filter. It is a second filter on top of the retriever, not a replacement for one. Omit all of these and every captured segment is published.
gate_on_error decides what happens when the gate itself fails, times out, or returns something unreadable. It defaults to publish, so a broken gate costs extraction rather than footage.Choose drop only when storing an unjudged segment is worse than losing it. A dropped segment of a live stream is gone, and the moment it covered cannot be recaptured.
Keep gate_timeout_seconds well below segment_seconds * segments_per_run, or the gate becomes the bottleneck and runs start overlapping.

Setup

1

Create the storage connection

2

Test the connection

Confirm Mixpeek can open the stream before you create a sync.
cURL
A camera that does not start delivering within connect_timeout_seconds fails here rather than on the first sync run.
3

Create a bucket whose schema declares the segment property

cURL
The response carries bucket_id. The name segment is yours to choose, and the next three steps all have to use the same one.
4

Create the sync, with a schema mapping

cURL
Name the blob property, even though schema_mapping is optional. Without it each segment lands on content, the default blob property, and not on the property your bucket schema declares.A bucket declaring a segment video property with input_mappings of {"video": "segment"} then passes collection validation and the batch matches nothing. You get a clean run over zero documents rather than an error.

Getting capture and gate values onto the object

A segment carries more than video. Capture records where it came from and when; a gate records what it decided. None of that reaches the object unless a schema_mapping names it, and the bucket schema declares a property to hold it. Use rtsp_field as the source type and name the field without a prefix.
gate_applied: false is the useful one. It records that the gate could not be reached and the segment was published unjudged.Without it an unjudged segment and a segment the gate passed look identical, so a run where the gate was down reads as a run where nothing moved.
target_type takes field or blob. There is no metadata value: a sync created with one saves, then returns 400 on every read and disappears from syncs/list.

Searching what you captured

Segments in a bucket are not searchable yet. A collection processes them into documents, and a saved retriever searches those documents. That second half is why the first half exists.
1

Create a collection over the bucket

cURL
input_mappings points the extractor at the blob property your sync writes. It has to match the blob_property you set in schema_mapping, or the collection processes nothing.
2

Save a retriever over the collection

A retriever needs retriever_name and stages, and each stage is {"stage_name": ..., "config": {"stage_id": ..., "parameters": {...}}}.
cURL
See Retrievers for a complete feature_search stage, including the feature_uri to search against. input_schema is a flat dict of field names, not JSON Schema.
3

Execute it

cURL
The response carries documents, each one a segment the retriever scored against your query.
4

Record what the user did with the results

cURL
Interactions are the signal a saved retriever learns from, which is the reason to save one rather than run an ad-hoc query. retriever_id and query_snapshot are optional and both are worth sending: without them an interaction cannot be tied back to what produced it.
A saved retriever can also run at the other end of this pipeline. Set gate_retriever_id on the connection and it decides which segments are worth keeping in the first place, before extraction is paid for.
The schema_mapping on your sync decides whether any of this finds anything. With no mapping each segment lands on content, the default blob property, and the fields your mapping would have written are never written at all.A collection whose input_mappings name segment then processes nothing, and a retriever over that collection returns zero documents. Every step reports success.

Transport

tcp interleaves RTP over the RTSP control connection. It survives NAT and firewalls that drop the separate UDP ports, which is why it is the default. Use udp when the camera or the network path does not handle interleaved TCP well.

Sync configuration

Polling intervals, reconciliation, and scheduling.

Retrievers

Stages, input schemas, and what execution returns.

Interactions

The feedback signal a saved retriever learns from.

Mux

Video assets from a hosted platform rather than a live camera.