Metadata format (TMF)¶
Recordings from current firmware are self-contained. Besides video and audio, every .mp4
written by a Trinet camera carries the Trinet Metadata Format (TMF):
| Where in the MP4 | What it holds |
|---|---|
A timed metadata track (tmfd) |
The motion data, per-frame timestamps and thermal telemetry, in about one-second chunks |
moov/udta/tmfm |
Recording metadata as JSON: camera, firmware, codec, sync state, frame-drop summary, temperature range |
moov/udta/tmfc |
The camera's calibration, as a binary blob |
Sharing just the .mp4 is therefore enough: the .imu, .vts and .tel files can be rebuilt from
it byte for byte. Video players ignore the extra track; ffprobe lists it as a data stream.
Re-encoding removes TMF
Anything that re-encodes or re-muxes the file — video editors, many converters, some upload
services — drops the metadata track and boxes. Keep the original files, or keep the .imu, .vts
and .tel files the camera writes alongside the video; they hold the same data.
Read it¶
With the Python toolkit installed:
python3 -m trinet_tools.tmf info take0001.mp4 # summary
python3 -m trinet_tools.tmf extract take0001.mp4 --out recovered/
extract rebuilds the .imu, .vts and .tel files plus the metadata JSON and the calibration
blob. From Python:
from trinet_tools.tmf import read_tmf
from trinet_tools import calib_blob
rec = read_tmf("take0001.mp4")
print(rec.meta) # recording metadata (dict)
if rec.calib_blob: # absent if the camera had no calibration stored
calib = calib_blob.unpack(rec.calib_blob) # intrinsics, extrinsics, IMU model (dict)
Recording metadata (tmfm)¶
UTF-8 JSON. Keys are omitted when unknown, and new keys may be added without changing the schema number — read the keys you need and ignore the rest.
{
"tmf_schema": 2,
"source": "sd",
"device_id": "64ea7f5da17f71e411e8f1a8103a1558",
"fw_version": "0.5.9",
"hw_generation": "v6",
"eye": "L",
"pair_file": "take0004_R.mp4",
"codec": "h264",
"imu_version": 5,
"imu_rate_hz": 400,
"vts_version": 4,
"sync": {"offset_ns": 0, "skew_ppb": 0, "quality_us": 0, "flags": 0},
"drops": {"nominal_ms": 33.33, "expected": 3413, "recorded": 3413,
"gap_count": 0, "gaps": []},
"thermal": {"min_c": 38.7, "max_c": 52.3, "samples": 117}
}
| Key | Meaning |
|---|---|
tmf_schema |
Schema version of this JSON (current recordings: 2) |
source |
Where the recording was made — sd for a recording to the camera's memory card |
generator |
The camera software component that wrote the metadata |
device_id |
The camera's public 32-hex device ID |
fw_version, hw_generation |
Firmware version and hardware generation that made the recording |
eye, pair_file |
Stereo cameras only: L or R, and the file name of the other eye |
codec |
h264 or h265 |
imu_version, imu_rate_hz, vts_version |
Versions of the embedded motion and timestamp records, and the motion sample rate |
sync |
The multi-camera clock state for the take: offset to the kit's clock (ns), rate mismatch (ppb), estimated uncertainty (µs) and flags — the same values as the .vts header |
drops |
The camera's own frame-continuity check: nominal frame interval, frames expected and recorded, and up to 64 gaps found from the per-frame timestamps |
thermal |
Lowest and highest camera temperature during the take, and the number of readings |
Calibration (tmfc)¶
The calibration the camera holds, embedded verbatim in every recording (absent if the camera had no
calibration stored when it recorded): a little-endian binary blob
starting with the magic TBLC and ending with a CRC-32. Two layouts exist:
- Single camera (200 bytes): image size, fisheye projection model,
fx fy cx cyand up to five distortion coefficients, camera–IMU rotation and translation, camera–IMU time offset, IMU noise parameters, biases and calibration quality. - Stereo (300 bytes): the same header, one block per eye (
cam0= the scene-left_Leye), the camera–IMU transform, the stereo extrinsicT_cam1_cam0(its translation is the baseline) and a shared IMU block.
The time-offset convention is t_imu = t_cam + timeshift_cam_imu_s. calib_blob.unpack() returns the
same structure as a calibration.json. See Calibration.
Embedded data track (tmfd)¶
Each sample of the metadata track covers about one second and is a key–length–value (KLV) structure, little-endian throughout. It carries, as separate streams:
| Stream | Content | Rate |
|---|---|---|
TSNS |
Motion sample timestamps (ns, camera clock) | Motion rate |
ACCL, GYRO, MAGN |
Accelerometer (m/s², including gravity), gyroscope (rad/s), magnetometer (µT) | Motion rate |
TMPC |
Motion-sensor temperature (°C) | Motion rate |
TFRM |
Per-frame timestamp records — the .vts entries |
Per frame |
TSOC |
Thermal telemetry records — the .tel entries |
1 Hz |
The first chunk also carries the original .imu, .vts and .tel file headers, which is what makes a
byte-identical rebuild possible. The complete byte-level layout is in the toolkit's
data format reference ↗.
Recording-meta sidecar (.json)¶
A small, human-readable JSON file sits next to each recording. For Wrist Kit takes it shows, at a glance, which take a file belongs to and how the camera relates to the others:
{
"format": "trinet-recording-meta/1",
"session": 72593,
"group": 48477,
"role": "slave",
"device_id": "64ea7f5da17f71e411e8f1a8103a1558",
"device_tag": "64ea7f5d",
"segment": 1,
"synced": true,
"master_clock_offset_ns": -4642873007,
"clock_skew_ppb": 28617,
"sync_quality_us": 610,
"sync_flags": 1,
"mode": "single",
"basename": "grp72593_64ea7f5d_1",
"created_unix": 79
}
| Field | Meaning |
|---|---|
session |
Recording-session ID — shared by every camera in one synchronized take (0 = recorded on its own) |
group |
The kit the camera is paired into (0 = unpaired) |
role |
master (the kit's clock reference), slave or unpaired |
device_id, device_tag |
The camera's ID, and its first 8 characters as used in file names |
segment |
Part number when a long take is split into several files (from 1) |
synced, sync_flags |
Whether the offset to the master clock is valid (sync_flags: 1 synced, 2 master — so master = 3) |
master_clock_offset_ns |
Add to this camera's frame timestamps to put them on the master's clock (0 on the master) |
clock_skew_ppb |
Remaining clock-rate difference to the master, for drift correction on long takes |
sync_quality_us |
Estimated sync uncertainty, microseconds |
basename |
The shared base name of this camera's files, grp<session>_<device-tag>_<segment> |
created_unix |
Seconds since the camera powered on — not wall-clock time (the camera has no real-time clock; see UTC for wireless recordings) |
The sync values are a convenience copy of the .vts header, which is what the toolkit uses:
read_vts(path).global_sof_ns() returns a camera's frame times already on the master's clock.
Related¶
File formats · Timing and sync · Calibration · Sample data — every sample recording contains TMF.