gps-video-sync 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gps_video_sync-0.1.0/.gitignore +16 -0
- gps_video_sync-0.1.0/LICENSE +21 -0
- gps_video_sync-0.1.0/PKG-INFO +241 -0
- gps_video_sync-0.1.0/README.md +210 -0
- gps_video_sync-0.1.0/docs/reference.md +440 -0
- gps_video_sync-0.1.0/pyproject.toml +52 -0
- gps_video_sync-0.1.0/src/gps_video_sync/__init__.py +63 -0
- gps_video_sync-0.1.0/src/gps_video_sync/__main__.py +3 -0
- gps_video_sync-0.1.0/src/gps_video_sync/cli.py +140 -0
- gps_video_sync-0.1.0/src/gps_video_sync/gopro.py +243 -0
- gps_video_sync-0.1.0/src/gps_video_sync/gpmf.py +124 -0
- gps_video_sync-0.1.0/src/gps_video_sync/igc.py +57 -0
- gps_video_sync-0.1.0/src/gps_video_sync/models.py +78 -0
- gps_video_sync-0.1.0/src/gps_video_sync/mp4.py +403 -0
- gps_video_sync-0.1.0/src/gps_video_sync/npz.py +37 -0
- gps_video_sync-0.1.0/src/gps_video_sync/plugins.py +37 -0
- gps_video_sync-0.1.0/src/gps_video_sync/sync.py +483 -0
- gps_video_sync-0.1.0/src/gps_video_sync/verify.py +185 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Manuel Seeger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gps-video-sync
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Synchronize action camera telemetry with GPS tracks
|
|
5
|
+
Project-URL: Homepage, https://github.com/manuelseeger/gps-video-sync
|
|
6
|
+
Project-URL: Documentation, https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md
|
|
7
|
+
Project-URL: Repository, https://github.com/manuelseeger/gps-video-sync
|
|
8
|
+
Project-URL: Issues, https://github.com/manuelseeger/gps-video-sync/issues
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: numpy>=1.24
|
|
24
|
+
Requires-Dist: scipy>=1.10
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
28
|
+
Provides-Extra: verify
|
|
29
|
+
Requires-Dist: matplotlib>=3.7; extra == 'verify'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# gps-video-sync
|
|
33
|
+
|
|
34
|
+
Align action-camera telemetry with an IGC flight track. The tool calculates a
|
|
35
|
+
video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera
|
|
36
|
+
GPS time when available and falls back to motion matching.
|
|
37
|
+
|
|
38
|
+
My GoPro clock often has the wrong date or time. I use this tool to align the
|
|
39
|
+
video with the GPS track after recording.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pip install gps-video-sync
|
|
47
|
+
pip install "gps-video-sync[verify]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
From a source checkout, run `pip install .` or `pip install ".[verify]"`.
|
|
51
|
+
|
|
52
|
+
The `verify` extra installs Matplotlib for plots. Frame extraction also needs
|
|
53
|
+
`ffmpeg` on `PATH`. GoPro MP4 and LRV extraction needs no external program.
|
|
54
|
+
|
|
55
|
+
## Align recordings
|
|
56
|
+
|
|
57
|
+
Match one or more recordings against one flight track:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Each file is an independent recording. The tool matches each file against the
|
|
64
|
+
track with no data from the other files. The recordings can have gaps between
|
|
65
|
+
them, can overlap in time, and can come from different cameras. You can give
|
|
66
|
+
them in any order. Adding, removing, or reordering the other files does not
|
|
67
|
+
change the result for a file.
|
|
68
|
+
|
|
69
|
+
The tool does not join split files. A GoPro splits a long recording into
|
|
70
|
+
chapter files, for example `GX010042.MP4` and `GX020042.MP4`. Each chapter file
|
|
71
|
+
gets its own result, offset, and timeline that starts at zero.
|
|
72
|
+
|
|
73
|
+
The output uses schema `2.0.0` with a `videos` list, even for one input. The
|
|
74
|
+
list has one entry per input, in input order. Each entry has its own status,
|
|
75
|
+
offset, drift, and confidence data. A failed or ambiguous recording does not
|
|
76
|
+
stop the others.
|
|
77
|
+
|
|
78
|
+
Give each recording only once. A repeated path, including a symbolic link to
|
|
79
|
+
the same file, fails at each later position. An input with the same recording
|
|
80
|
+
identity and timeline as an earlier `ok` or `ambiguous` result also fails as a
|
|
81
|
+
duplicate. For example, two exports of the same telemetry are duplicates.
|
|
82
|
+
|
|
83
|
+
To inspect one recording or check its saved alignment:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
gps-video-sync info ridge.MP4
|
|
87
|
+
gps-video-sync telemetry ridge.MP4 --out ridge.npz
|
|
88
|
+
gps-video-sync verify ridge.MP4 --track flight.igc \
|
|
89
|
+
--result sync.json --output ridge.png --frames 10 120
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`info`, `telemetry`, and `verify` each accept exactly one recording. Their times
|
|
93
|
+
are local to that file and start at zero. `--frames` values must be 0 or more
|
|
94
|
+
and less than the recording duration. `verify` selects the entry in the saved
|
|
95
|
+
result that has the same recording identity and local timeline. It draws a plot
|
|
96
|
+
only for an `ok` result. Run `gps-video-sync COMMAND --help` for all options.
|
|
97
|
+
|
|
98
|
+
Exit codes for `sync` and `verify`:
|
|
99
|
+
|
|
100
|
+
- `0`: all matches are `ok`.
|
|
101
|
+
- `2`: at least one match is `ambiguous` and no match is `failed`. An ambiguous
|
|
102
|
+
result has no usable offset.
|
|
103
|
+
- `1`: at least one match is `failed`, or an input or run error occurs. Invalid
|
|
104
|
+
command-line arguments also give `1`.
|
|
105
|
+
|
|
106
|
+
`sync` writes all per-file results before it returns a failure exit code.
|
|
107
|
+
|
|
108
|
+
Results, `info` output, telemetry exports, and cache files contain absolute
|
|
109
|
+
recording paths. Paths can show user names and directory names. Exports and
|
|
110
|
+
cache files can also contain camera GPS times, and results contain recording
|
|
111
|
+
dates and times. Remove this data before you share these files.
|
|
112
|
+
|
|
113
|
+
## Use the Python API
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from gps_video_sync import match_videos, read_track, read_video, synchronize
|
|
117
|
+
|
|
118
|
+
track = read_track("flight.igc")
|
|
119
|
+
results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
|
|
120
|
+
for result in results:
|
|
121
|
+
print(result.video, result.status, result.offset_seconds)
|
|
122
|
+
|
|
123
|
+
telemetry = read_video("ridge.MP4", hz=10)
|
|
124
|
+
result = synchronize(track, telemetry)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`match_videos` matches each path independently and returns the results in input
|
|
128
|
+
order. A file error gives a `failed` result for that file only. `read_video`
|
|
129
|
+
reads one recording. `synchronize` also accepts array-based `Telemetry`.
|
|
130
|
+
|
|
131
|
+
To match exported telemetry, use `source="npz"` in the API or `--source npz` in
|
|
132
|
+
the CLI. NPZ exports from `telemetry --out` keep the original recording
|
|
133
|
+
identity, duration, and media offset. In a batch result, `input_file`
|
|
134
|
+
identifies the NPZ archive.
|
|
135
|
+
|
|
136
|
+
The result maps video seconds to UTC seconds from midnight of the track date:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
utc_seconds = video_seconds * clockRate + offset_seconds
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Supported cameras
|
|
143
|
+
|
|
144
|
+
The bundled test recordings are from a GoPro HERO11 Black. They test motion
|
|
145
|
+
alignment but contain no GPS data. GPS extraction and alignment have synthetic
|
|
146
|
+
tests only. GoPro documents compatible metadata for the other models below, but
|
|
147
|
+
this package has no recordings from them. Their extraction, alignment, and frame
|
|
148
|
+
verification are untested.
|
|
149
|
+
|
|
150
|
+
The reader needs a timed GPMF track with sample format `gpmd` in the original
|
|
151
|
+
video. It uses these fields:
|
|
152
|
+
|
|
153
|
+
- `GPS9` supplies UTC and fix status for GPS alignment. The older `GPS5` format
|
|
154
|
+
needs `GPSU` for UTC and `GPSF` for fix status. The reader accepts only valid
|
|
155
|
+
3D fixes. `GPS9` takes priority when both formats occur in a packet.
|
|
156
|
+
- `GYRO` and `ACCL` supply the motion features. Motion alignment works without
|
|
157
|
+
camera GPS, within the limits below.
|
|
158
|
+
- The reader also extracts `GRAV`, `CORI`, and `IORI` when present. Alignment
|
|
159
|
+
does not need these fields. The matcher estimates gravity from `ACCL`.
|
|
160
|
+
|
|
161
|
+
| Camera | GPS metadata for UTC alignment | `GYRO` + `ACCL` | `GRAV` + `CORI` + `IORI` | Compatibility / test status |
|
|
162
|
+
| --- | --- | --- | --- | --- |
|
|
163
|
+
| HERO5 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
164
|
+
| HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
|
|
165
|
+
| HERO6 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
166
|
+
| HERO7 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
167
|
+
| HERO8 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
168
|
+
| HERO9 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
169
|
+
| HERO10 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
170
|
+
| HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
|
|
171
|
+
| HERO11 Black | `GPS9`, plus legacy `GPS5` | Yes | Yes | Motion tested with bundled recordings; GPS untested |
|
|
172
|
+
| HERO12 Black | None | Yes | Yes | Motion only expected; untested |
|
|
173
|
+
| HERO13 Black | `GPS9` | Yes | Yes | GPS and motion expected; untested |
|
|
174
|
+
| Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
|
|
175
|
+
| MAX | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | Metadata compatible; 360 workflow untested |
|
|
176
|
+
| MAX2 | `GPS9` | Yes | Yes | Metadata compatible; 360 workflow untested |
|
|
177
|
+
| MISSION 1 family | `GPS9` | Yes | Yes | Metadata compatible per GoPro; untested |
|
|
178
|
+
|
|
179
|
+
Use original, normal-speed recordings. Enable GPS on cameras that have it and
|
|
180
|
+
wait for a satellite lock before you record. Some clips can still lack usable
|
|
181
|
+
fixes. Edited, transcoded, or stitched exports can omit the metadata track.
|
|
182
|
+
Check each recording with `gps-video-sync info`.
|
|
183
|
+
|
|
184
|
+
The 360 camera rows cover sensor metadata only. Stitching, projection, and frame
|
|
185
|
+
extraction from native 360 files are untested.
|
|
186
|
+
|
|
187
|
+
The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4
|
|
188
|
+
and earlier cameras, external GPS accessories, or special recording modes. A
|
|
189
|
+
model number or GPS chip alone does not establish compatibility.
|
|
190
|
+
|
|
191
|
+
Sources:
|
|
192
|
+
|
|
193
|
+
- [GoPro's model-by-model GPMF tables](https://github.com/gopro/gpmf-parser/blob/9a7150632892c7356c91145c889016f07b0ed48d/README.md#where-to-find-gpmf-data).
|
|
194
|
+
The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
|
|
195
|
+
- [Gyroflow's GoPro motion-data notes](https://github.com/gyroflow/gyroflow-docs/blob/main/docs/cams/gopro.md).
|
|
196
|
+
|
|
197
|
+
Firmware and recording mode can change the available streams. The reader checks
|
|
198
|
+
for metadata keys, not camera model names.
|
|
199
|
+
|
|
200
|
+
## Limits
|
|
201
|
+
|
|
202
|
+
GoPro extraction allows at most 1,000,000 resampled samples per recording.
|
|
203
|
+
Lower `--hz` if the recording duration in seconds multiplied by `--hz` exceeds
|
|
204
|
+
this limit.
|
|
205
|
+
|
|
206
|
+
Motion matching needs at least 90 seconds of usable overlap. The overlap must
|
|
207
|
+
also cover at least 65% of the shorter feature sequence. Each file needs enough
|
|
208
|
+
varying motion. Repeated turns, data gaps, or low motion can produce ambiguous
|
|
209
|
+
or false matches. Check the plot and distinctive events before you use an
|
|
210
|
+
alignment. Short clips cannot establish clock stability.
|
|
211
|
+
|
|
212
|
+
See the [API and format reference](https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md)
|
|
213
|
+
for data contracts, plugins, telemetry formats, and matching limits.
|
|
214
|
+
|
|
215
|
+
## Run the tests
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
pip install -e '.[dev,verify]'
|
|
219
|
+
python -m pytest
|
|
220
|
+
python scripts/check_independent_videos.py
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Run the tests from a repository checkout. Release archives exclude tests and
|
|
224
|
+
fixtures. The [bundled fixtures](https://github.com/manuelseeger/gps-video-sync/tree/main/tests/fixtures)
|
|
225
|
+
contain flight coordinates, times, and media.
|
|
226
|
+
|
|
227
|
+
- Extraction and alignment tests need no external program.
|
|
228
|
+
- Parity tests use the `ffprobe` marker and need `ffprobe` 9 or later.
|
|
229
|
+
- Frame tests need `ffmpeg` and Matplotlib.
|
|
230
|
+
- To skip integration tests, run `python -m pytest -m 'not integration'`.
|
|
231
|
+
|
|
232
|
+
`scripts/check_independent_videos.py` needs the `verify` extra and `ffmpeg`. It
|
|
233
|
+
runs the API and the CLI on the bundled recordings. It checks that each
|
|
234
|
+
per-file result stays the same across input orders and subsets, cold and warm
|
|
235
|
+
caches, and NPZ exports. It also checks plots and compares extracted frames
|
|
236
|
+
with direct FFmpeg seeks. Cropped motion recordings and synthetic GPS
|
|
237
|
+
recordings test gaps, overlaps, and failures in a mixed batch.
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
[MIT](https://github.com/manuelseeger/gps-video-sync/blob/main/LICENSE).
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# gps-video-sync
|
|
2
|
+
|
|
3
|
+
Align action-camera telemetry with an IGC flight track. The tool calculates a
|
|
4
|
+
video-to-UTC offset, checks clock drift, and plots the alignment. It uses camera
|
|
5
|
+
GPS time when available and falls back to motion matching.
|
|
6
|
+
|
|
7
|
+
My GoPro clock often has the wrong date or time. I use this tool to align the
|
|
8
|
+
video with the GPS track after recording.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
Requires Python 3.10 or later. Tests cover Python 3.10 to 3.15.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pip install gps-video-sync
|
|
16
|
+
pip install "gps-video-sync[verify]"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
From a source checkout, run `pip install .` or `pip install ".[verify]"`.
|
|
20
|
+
|
|
21
|
+
The `verify` extra installs Matplotlib for plots. Frame extraction also needs
|
|
22
|
+
`ffmpeg` on `PATH`. GoPro MP4 and LRV extraction needs no external program.
|
|
23
|
+
|
|
24
|
+
## Align recordings
|
|
25
|
+
|
|
26
|
+
Match one or more recordings against one flight track:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
gps-video-sync sync takeoff.MP4 ridge.MP4 landing.MP4 --track flight.igc --json sync.json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Each file is an independent recording. The tool matches each file against the
|
|
33
|
+
track with no data from the other files. The recordings can have gaps between
|
|
34
|
+
them, can overlap in time, and can come from different cameras. You can give
|
|
35
|
+
them in any order. Adding, removing, or reordering the other files does not
|
|
36
|
+
change the result for a file.
|
|
37
|
+
|
|
38
|
+
The tool does not join split files. A GoPro splits a long recording into
|
|
39
|
+
chapter files, for example `GX010042.MP4` and `GX020042.MP4`. Each chapter file
|
|
40
|
+
gets its own result, offset, and timeline that starts at zero.
|
|
41
|
+
|
|
42
|
+
The output uses schema `2.0.0` with a `videos` list, even for one input. The
|
|
43
|
+
list has one entry per input, in input order. Each entry has its own status,
|
|
44
|
+
offset, drift, and confidence data. A failed or ambiguous recording does not
|
|
45
|
+
stop the others.
|
|
46
|
+
|
|
47
|
+
Give each recording only once. A repeated path, including a symbolic link to
|
|
48
|
+
the same file, fails at each later position. An input with the same recording
|
|
49
|
+
identity and timeline as an earlier `ok` or `ambiguous` result also fails as a
|
|
50
|
+
duplicate. For example, two exports of the same telemetry are duplicates.
|
|
51
|
+
|
|
52
|
+
To inspect one recording or check its saved alignment:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
gps-video-sync info ridge.MP4
|
|
56
|
+
gps-video-sync telemetry ridge.MP4 --out ridge.npz
|
|
57
|
+
gps-video-sync verify ridge.MP4 --track flight.igc \
|
|
58
|
+
--result sync.json --output ridge.png --frames 10 120
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`info`, `telemetry`, and `verify` each accept exactly one recording. Their times
|
|
62
|
+
are local to that file and start at zero. `--frames` values must be 0 or more
|
|
63
|
+
and less than the recording duration. `verify` selects the entry in the saved
|
|
64
|
+
result that has the same recording identity and local timeline. It draws a plot
|
|
65
|
+
only for an `ok` result. Run `gps-video-sync COMMAND --help` for all options.
|
|
66
|
+
|
|
67
|
+
Exit codes for `sync` and `verify`:
|
|
68
|
+
|
|
69
|
+
- `0`: all matches are `ok`.
|
|
70
|
+
- `2`: at least one match is `ambiguous` and no match is `failed`. An ambiguous
|
|
71
|
+
result has no usable offset.
|
|
72
|
+
- `1`: at least one match is `failed`, or an input or run error occurs. Invalid
|
|
73
|
+
command-line arguments also give `1`.
|
|
74
|
+
|
|
75
|
+
`sync` writes all per-file results before it returns a failure exit code.
|
|
76
|
+
|
|
77
|
+
Results, `info` output, telemetry exports, and cache files contain absolute
|
|
78
|
+
recording paths. Paths can show user names and directory names. Exports and
|
|
79
|
+
cache files can also contain camera GPS times, and results contain recording
|
|
80
|
+
dates and times. Remove this data before you share these files.
|
|
81
|
+
|
|
82
|
+
## Use the Python API
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from gps_video_sync import match_videos, read_track, read_video, synchronize
|
|
86
|
+
|
|
87
|
+
track = read_track("flight.igc")
|
|
88
|
+
results = match_videos(track, ["takeoff.MP4", "ridge.MP4", "landing.MP4"])
|
|
89
|
+
for result in results:
|
|
90
|
+
print(result.video, result.status, result.offset_seconds)
|
|
91
|
+
|
|
92
|
+
telemetry = read_video("ridge.MP4", hz=10)
|
|
93
|
+
result = synchronize(track, telemetry)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`match_videos` matches each path independently and returns the results in input
|
|
97
|
+
order. A file error gives a `failed` result for that file only. `read_video`
|
|
98
|
+
reads one recording. `synchronize` also accepts array-based `Telemetry`.
|
|
99
|
+
|
|
100
|
+
To match exported telemetry, use `source="npz"` in the API or `--source npz` in
|
|
101
|
+
the CLI. NPZ exports from `telemetry --out` keep the original recording
|
|
102
|
+
identity, duration, and media offset. In a batch result, `input_file`
|
|
103
|
+
identifies the NPZ archive.
|
|
104
|
+
|
|
105
|
+
The result maps video seconds to UTC seconds from midnight of the track date:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
utc_seconds = video_seconds * clockRate + offset_seconds
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Supported cameras
|
|
112
|
+
|
|
113
|
+
The bundled test recordings are from a GoPro HERO11 Black. They test motion
|
|
114
|
+
alignment but contain no GPS data. GPS extraction and alignment have synthetic
|
|
115
|
+
tests only. GoPro documents compatible metadata for the other models below, but
|
|
116
|
+
this package has no recordings from them. Their extraction, alignment, and frame
|
|
117
|
+
verification are untested.
|
|
118
|
+
|
|
119
|
+
The reader needs a timed GPMF track with sample format `gpmd` in the original
|
|
120
|
+
video. It uses these fields:
|
|
121
|
+
|
|
122
|
+
- `GPS9` supplies UTC and fix status for GPS alignment. The older `GPS5` format
|
|
123
|
+
needs `GPSU` for UTC and `GPSF` for fix status. The reader accepts only valid
|
|
124
|
+
3D fixes. `GPS9` takes priority when both formats occur in a packet.
|
|
125
|
+
- `GYRO` and `ACCL` supply the motion features. Motion alignment works without
|
|
126
|
+
camera GPS, within the limits below.
|
|
127
|
+
- The reader also extracts `GRAV`, `CORI`, and `IORI` when present. Alignment
|
|
128
|
+
does not need these fields. The matcher estimates gravity from `ACCL`.
|
|
129
|
+
|
|
130
|
+
| Camera | GPS metadata for UTC alignment | `GYRO` + `ACCL` | `GRAV` + `CORI` + `IORI` | Compatibility / test status |
|
|
131
|
+
| --- | --- | --- | --- | --- |
|
|
132
|
+
| HERO5 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
133
|
+
| HERO5 Session | None | Yes | Not documented | Motion only expected; untested |
|
|
134
|
+
| HERO6 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
135
|
+
| HERO7 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Not documented | GPS and motion expected; untested |
|
|
136
|
+
| HERO8 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
137
|
+
| HERO9 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
138
|
+
| HERO10 Black | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | GPS and motion expected; untested |
|
|
139
|
+
| HERO10 Black Bones | None | Yes | Yes | Motion only expected; untested |
|
|
140
|
+
| HERO11 Black | `GPS9`, plus legacy `GPS5` | Yes | Yes | Motion tested with bundled recordings; GPS untested |
|
|
141
|
+
| HERO12 Black | None | Yes | Yes | Motion only expected; untested |
|
|
142
|
+
| HERO13 Black | `GPS9` | Yes | Yes | GPS and motion expected; untested |
|
|
143
|
+
| Fusion | Not documented in the cited table | Yes | Not documented | Motion metadata compatible; 360 workflow untested |
|
|
144
|
+
| MAX | `GPS5` + `GPSU` + `GPSF` | Yes | Yes | Metadata compatible; 360 workflow untested |
|
|
145
|
+
| MAX2 | `GPS9` | Yes | Yes | Metadata compatible; 360 workflow untested |
|
|
146
|
+
| MISSION 1 family | `GPS9` | Yes | Yes | Metadata compatible per GoPro; untested |
|
|
147
|
+
|
|
148
|
+
Use original, normal-speed recordings. Enable GPS on cameras that have it and
|
|
149
|
+
wait for a satellite lock before you record. Some clips can still lack usable
|
|
150
|
+
fixes. Edited, transcoded, or stitched exports can omit the metadata track.
|
|
151
|
+
Check each recording with `gps-video-sync info`.
|
|
152
|
+
|
|
153
|
+
The 360 camera rows cover sensor metadata only. Stitching, projection, and frame
|
|
154
|
+
extraction from native 360 files are untested.
|
|
155
|
+
|
|
156
|
+
The table does not cover Silver, White, Mini, or entry-level HERO models, HERO4
|
|
157
|
+
and earlier cameras, external GPS accessories, or special recording modes. A
|
|
158
|
+
model number or GPS chip alone does not establish compatibility.
|
|
159
|
+
|
|
160
|
+
Sources:
|
|
161
|
+
|
|
162
|
+
- [GoPro's model-by-model GPMF tables](https://github.com/gopro/gpmf-parser/blob/9a7150632892c7356c91145c889016f07b0ed48d/README.md#where-to-find-gpmf-data).
|
|
163
|
+
The documented firmware includes HERO7 v1.8, HERO8 v2.5, and MAX v2.0.
|
|
164
|
+
- [Gyroflow's GoPro motion-data notes](https://github.com/gyroflow/gyroflow-docs/blob/main/docs/cams/gopro.md).
|
|
165
|
+
|
|
166
|
+
Firmware and recording mode can change the available streams. The reader checks
|
|
167
|
+
for metadata keys, not camera model names.
|
|
168
|
+
|
|
169
|
+
## Limits
|
|
170
|
+
|
|
171
|
+
GoPro extraction allows at most 1,000,000 resampled samples per recording.
|
|
172
|
+
Lower `--hz` if the recording duration in seconds multiplied by `--hz` exceeds
|
|
173
|
+
this limit.
|
|
174
|
+
|
|
175
|
+
Motion matching needs at least 90 seconds of usable overlap. The overlap must
|
|
176
|
+
also cover at least 65% of the shorter feature sequence. Each file needs enough
|
|
177
|
+
varying motion. Repeated turns, data gaps, or low motion can produce ambiguous
|
|
178
|
+
or false matches. Check the plot and distinctive events before you use an
|
|
179
|
+
alignment. Short clips cannot establish clock stability.
|
|
180
|
+
|
|
181
|
+
See the [API and format reference](https://github.com/manuelseeger/gps-video-sync/blob/main/docs/reference.md)
|
|
182
|
+
for data contracts, plugins, telemetry formats, and matching limits.
|
|
183
|
+
|
|
184
|
+
## Run the tests
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
pip install -e '.[dev,verify]'
|
|
188
|
+
python -m pytest
|
|
189
|
+
python scripts/check_independent_videos.py
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Run the tests from a repository checkout. Release archives exclude tests and
|
|
193
|
+
fixtures. The [bundled fixtures](https://github.com/manuelseeger/gps-video-sync/tree/main/tests/fixtures)
|
|
194
|
+
contain flight coordinates, times, and media.
|
|
195
|
+
|
|
196
|
+
- Extraction and alignment tests need no external program.
|
|
197
|
+
- Parity tests use the `ffprobe` marker and need `ffprobe` 9 or later.
|
|
198
|
+
- Frame tests need `ffmpeg` and Matplotlib.
|
|
199
|
+
- To skip integration tests, run `python -m pytest -m 'not integration'`.
|
|
200
|
+
|
|
201
|
+
`scripts/check_independent_videos.py` needs the `verify` extra and `ffmpeg`. It
|
|
202
|
+
runs the API and the CLI on the bundled recordings. It checks that each
|
|
203
|
+
per-file result stays the same across input orders and subsets, cold and warm
|
|
204
|
+
caches, and NPZ exports. It also checks plots and compares extracted frames
|
|
205
|
+
with direct FFmpeg seeks. Cropped motion recordings and synthetic GPS
|
|
206
|
+
recordings test gaps, overlaps, and failures in a mixed batch.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[MIT](https://github.com/manuelseeger/gps-video-sync/blob/main/LICENSE).
|