gps-video-sync
Align action-camera telemetry with an IGC flight track. The tool calculates a video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera GPS time when available and falls back to motion matching.
My GoPro clock often has the wrong date or time. I use this tool to align the video with the GPS track after recording.
Install
Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
pip install gps-video-sync
pip install "gps-video-sync[verify]"
From a source checkout, run pip install . or pip install ".[verify]".
The verify extra installs Matplotlib for plots. Frame extraction also needs
ffmpeg on PATH. GoPro MP4 and LRV extraction needs no external program.
Align recordings
Match one or more recordings against one flight track:
gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
Recordings can have gaps, overlap, or come from different cameras. You can give them in any order. Unrelated recordings keep their independent results.
The tool detects continuous chapters from sensor-clock junctions and exact cumulative sample counters inside the files. Names do not establish continuity. Confirmed chapters share one alignment, so a short chapter can use motion or GPS evidence from its neighbours. Each chapter keeps its own result and local timeline. Adjacent chapter boundaries are exactly equal, including clock drift. A subset of a continuous recording can give a different alignment.
The tool first matches each file independently. A branch, a failed group solve,
a new GPS time step, or a contradiction with an accepted independent mapping
keeps the independent results. Missing continuity evidence also keeps files
independent. Each result has a shot object with single, joined, or
ambiguous status. Only joined means that a shared alignment was applied.
The output uses schema 2.0.0 with a videos list, even for one input. The
list has one entry per input, in input order. Each entry has its own status,
offset, drift, and confidence data. A failed or ambiguous recording does not
stop the others.
Give each recording only once. A repeated path, including a symbolic link to
the same file, fails at each later position. An input with the same recording
identity and timeline as an earlier ok or ambiguous result also fails as a
duplicate. For example, two exports of the same telemetry are duplicates.
To inspect one recording or check its saved alignment:
gps-video-sync info ridge.MP4
gps-video-sync telemetry ridge.MP4 --out ridge.npz
gps-video-sync verify ridge.MP4 --track flight.igc \
--result sync.json --output ridge.png --frames 10 120
info, telemetry, and verify each accept exactly one recording. Their times
are local to that file and start at zero. --frames values must be 0 or more
and less than the recording duration. verify selects the entry in the saved
result that has the same recording identity and local timeline. It draws a plot
only for an ok result. Run gps-video-sync COMMAND --help for all options.
Exit codes for sync and verify:
0: all matches areok.2: at least one match isambiguousand no match isfailed. An ambiguous result has no usable offset.1: at least one match isfailed, or an input or run error occurs. Invalid command-line arguments also give1.
sync writes all per-file results before it returns a failure exit code.
Results, info output, telemetry exports, and cache files contain absolute
recording paths. Paths can show user names and directory names. Exports and
cache files can also contain camera GPS times and serial numbers. Results
contain recording dates and times. Remove this data before you share these files.
Use the Python API
from gps_video_sync import match_videos, read_track, read_video, synchronize
track = read_track("flight.igc")
results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
for result in results:
print(result.video, result.status, result.offset_seconds)
telemetry = read_video("ridge.MP4", hz=10)
result = synchronize(track, telemetry)
match_videos returns one result per input, in input order. It applies shared
alignment only to confirmed continuous chapters. A file error gives a failed
result for that file only. read_video reads one recording. synchronize
matches one recording and also accepts array-based Telemetry.
To match exported telemetry, use source="npz" in the API or --source npz in
the CLI. NPZ exports from telemetry --out keep the original recording
identity, duration, media offset, and optional continuity evidence.
Telemetry.slice clears continuity evidence. In a batch result, input_file
identifies the NPZ archive.
The result maps video seconds to UTC seconds from midnight of the track date:
utc_seconds = video_seconds * clockRate + offset_seconds
Supported cameras
The bundled test recordings are from a GoPro HERO11 Black. They test motion alignment but contain no GPS data. GPS extraction and alignment have synthetic tests only. GoPro documents compatible metadata for the other models below, but this package has no recordings from them. Their extraction, alignment, and frame verification are untested.
The reader needs a timed GPMF track with sample format gpmd in the original
video. It uses these fields:
GPS9supplies UTC and fix status for GPS alignment. The olderGPS5format needsGPSUfor UTC andGPSFfor fix status. The reader accepts only valid 3D fixes.GPS9takes priority when both formats occur in a packet.GYROandACCLsupply the motion features. Motion alignment works without camera GPS, within the limits below.- The reader also extracts
GRAV,CORI, andIORIwhen present. Alignment does not need these fields. The matcher estimates gravity fromACCL.
| Camera | GPS metadata for UTC alignment | GYRO + ACCL |
GRAV + CORI + IORI |
Compatibility / test status |
|---|---|---|---|---|
| HERO5 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
| HERO6 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO7 Black | GPS5 + GPSU + GPSF |
Yes | Not documented | GPS and motion expected; untested |
| HERO8 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO9 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO10 Black | GPS5 + GPSU + GPSF |
Yes | Yes | GPS and motion expected; untested |
| HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
| HERO11 Black | GPS9, plus legacy GPS5 |
Yes | Yes | Motion tested with bundled recordings; GPS untested |
| HERO12 Black | None | Yes | Yes | Motion only expected; untested |
| HERO13 Black | GPS9 |
Yes | Yes | GPS and motion expected; untested |
| Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
| MAX | GPS5 + GPSU + GPSF |
Yes | Yes | Metadata compatible; 360 workflow untested |
| MAX2 | GPS9 |
Yes | Yes | Metadata compatible; 360 workflow untested |
| MISSION 1 family | GPS9 |
Yes | Yes | Metadata compatible per GoPro; untested |
Use original, normal-speed recordings. Enable GPS on cameras that have it and
wait for a satellite lock before you record. Some clips can still lack usable
fixes. Edited, transcoded, or stitched exports can omit the metadata track.
Check each recording with gps-video-sync info.
The 360 camera rows cover sensor metadata only. Stitching, projection, and frame extraction from native 360 files are untested.
The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4 and earlier cameras, external GPS accessories, or special recording modes. A model number or GPS chip alone does not establish compatibility.
Sources:
- GoPro's model-by-model GPMF tables. The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
- Gyroflow's GoPro motion-data notes.
Firmware and recording mode can change the available streams. The reader checks for metadata keys, not camera model names.
Limits
GoPro extraction allows at most 1,000,000 resampled samples per recording.
Lower --hz if the recording duration in seconds multiplied by --hz exceeds
this limit.
Motion matching needs at least 90 seconds of usable overlap. The overlap must also cover at least 65% of the shorter feature sequence. Each independent file or confirmed continuous group needs enough varying motion. Repeated turns, data gaps, or low motion can produce ambiguous or false matches. Check the plot and distinctive events before you use an alignment. Short clips cannot establish clock stability.
See the API and format reference for data contracts, plugins, telemetry formats, and matching limits.
Run the tests
pip install -e '.[dev,verify]'
python -m pytest
python scripts/check_independent_videos.py
python scripts/check_continuous_videos.py
Run the tests from a repository checkout. Release archives exclude tests and fixtures. The bundled fixtures contain flight coordinates, times, and media.
- Extraction and alignment tests need no external program.
- Parity tests use the
ffprobemarker and needffprobe9 or later. - Frame tests need
ffmpegand Matplotlib. - To skip integration tests, run
python -m pytest -m 'not integration'.
scripts/check_continuous_videos.py needs the verify extra and ffmpeg.
It runs the API and the CLI on the bundled recordings with cold and warm
caches. It checks exact boundaries, reordered inputs, subsets, weak chapters,
non-unit drift, gaps, overlaps, counters, contradictory fits, and failures.
It also checks cache rebuilds, NPZ and JSON exports, crops, saved-result
selection, plots, and frames against direct FFmpeg seeks.
scripts/check_independent_videos.py checks independent motion crops and GPS
recordings with gaps, overlaps, shuffled inputs, and isolated failures. It also
checks exports and file-local frame extraction on the bundled videos.
License
MIT.
Metadata
Release files for gps-video-sync 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| gps_video_sync-1.0.0.tar.gz | 43.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gps_video_sync-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.8 kB
Release files / gps_video_sync-1.0.0.tar.gz
| Download URL | gps_video_sync-1.0.0.tar.gz |
|---|---|
| Size | 43.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d5da9612eef44a56be954a54b332b5265e2a87fba0d377c54c490b36c1a8d896
|
|
BLAKE2b-256 checksum How to use checksums |
372d52483848ef669e44c9266c10ed054962dfa278a6de55a4512b1f497d2a5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / gps_video_sync-1.0.0-py3-none-any.whl
| Download URL | gps_video_sync-1.0.0-py3-none-any.whl |
|---|---|
| Size | 38.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8163f148161928f16d295d69d42b170508b189c12f35f46941621b3ee70d94b2
|
|
BLAKE2b-256 checksum How to use checksums |
1ce4d3e765f9ee83e00340d647ba06dbed697063f477934c712054924eccc288
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for gps-video-sync 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for gps_video_sync-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.