|
Bogdan Timofte
authored
a month ago
|
1
|
# VariaReEncoder
|
|
|
2
|
|
|
|
3
|
Batch transcoder for Garmin Varia dashcam footage. Converts H.264 source clips to HEVC MP4,
|
|
|
4
|
optimized for Apple Photos / QuickTime compatibility, with sidecar JSON copy and a telemetry
|
|
|
5
|
manifest placeholder for a future FIT sync pipeline.
|
|
|
6
|
|
|
|
7
|
## Requirements
|
|
|
8
|
|
|
|
9
|
- macOS or Linux
|
|
|
10
|
- `ffmpeg` and `ffprobe` in `PATH` (any recent version with libx265/libx264; videotoolbox on macOS)
|
|
|
11
|
- Bash 3.2+
|
|
|
12
|
|
|
|
13
|
## Quick Start
|
|
|
14
|
|
|
|
15
|
```bash
|
|
|
16
|
# Transcode everything in SampleFootage → Output (hardware encoder on macOS)
|
|
|
17
|
./garmin_varia_transcode.sh -s SampleFootage -d Output
|
|
|
18
|
|
|
|
19
|
# Preview what would happen without writing any files
|
|
|
20
|
./garmin_varia_transcode.sh -s SampleFootage -d Output --dry-run
|
|
|
21
|
|
|
|
22
|
# Single file, verbose output
|
|
|
23
|
./garmin_varia_transcode.sh -s SampleFootage/Day/clip.mp4 -d Output --verbose
|
|
|
24
|
|
|
|
25
|
# Transcode + delete originals after validation
|
|
Bogdan Timofte
authored
a month ago
|
26
|
./garmin_varia_transcode.sh -s SampleFootage -d Output --delete-source
|
|
|
27
|
|
|
|
28
|
# Long unattended run preset (delete-source + keep-going)
|
|
|
29
|
./garmin_varia_transcode.sh -s SampleFootage -d Output --unattended
|
|
|
30
|
|
|
|
31
|
# Media cleanup helper (Apple artifacts, zero-byte MP4, duplicate suffix normalization)
|
|
|
32
|
./cleanup_garmin_varia_media_folder.sh --dry-run ~/Autofs/xdev/autonas/ext01/@Camera/import
|
|
Bogdan Timofte
authored
a month ago
|
33
|
```
|
|
|
34
|
|
|
|
35
|
## Encoding Modes
|
|
|
36
|
|
|
|
37
|
| Mode | Encoder | Speed (30s clip) | Power\* | Use when |
|
|
|
38
|
|------------|--------------------|-----------------|-----------|----------|
|
|
|
39
|
| `hardware` | hevc_videotoolbox | ~4-5s | ~35W | Default — battery, large libraries, dashcam footage |
|
|
|
40
|
| `auto` | videotoolbox → x265 → x264 | varies | varies | Cross-platform or unknown machine |
|
|
|
41
|
| `quality` | libx265 (CRF 20) | ~50s | ~80W | Archival, cinema, complex sources |
|
|
|
42
|
| `compat` | libx264 (CRF 19) | ~30s | ~70W | Older TVs, players without HEVC support |
|
|
|
43
|
|
|
|
44
|
\* Measured on Apple Silicon MacBook Pro.
|
|
|
45
|
|
|
|
46
|
## CLI Reference
|
|
|
47
|
|
|
|
48
|
```
|
|
|
49
|
Options:
|
|
|
50
|
-s, --source, --input DIR Source directory or single file (default: current directory)
|
|
|
51
|
-d, --destination, --output DIR Destination directory (default: current directory)
|
|
|
52
|
--mode MODE hardware|auto|quality|compat (default: hardware)
|
|
|
53
|
--crf N CRF for quality/compat modes (lower = better; default: 20/19)
|
|
|
54
|
--no-overwrite Skip files that already exist at destination
|
|
|
55
|
--dry-run Print actions without writing files
|
|
|
56
|
--no-recursive Process only the top-level source directory
|
|
|
57
|
--extensions LIST Comma-separated extensions (default: mp4,mov,avi,m4v)
|
|
|
58
|
--verbose Full per-operation logs + ffmpeg/ffprobe output
|
|
Bogdan Timofte
authored
a month ago
|
59
|
--delete-source Delete source after strict post-encode validation
|
|
|
60
|
--keep-going Continue after source-file failures (default: stop)
|
|
|
61
|
--unattended Preset for long runs: --delete-source + --keep-going
|
|
|
62
|
--no-apple-repack-fallback Disable macOS avconvert fallback for unreadable MP4/MOV sources
|
|
|
63
|
--apple-repack-fallback Enable macOS avconvert fallback (default)
|
|
Bogdan Timofte
authored
a month ago
|
64
|
-h, --help Show full help with encoding mode details
|
|
|
65
|
```
|
|
|
66
|
|
|
|
67
|
## Output Conventions
|
|
|
68
|
|
|
|
69
|
- Output is always `.mp4`
|
|
|
70
|
- HEVC outputs tagged `hvc1` (required for Apple Photos / QuickTime)
|
|
|
71
|
- Directory structure under source is preserved at destination
|
|
|
72
|
- File timestamps copied from source (`touch -r`)
|
|
|
73
|
- JSON sidecars copied 1:1 to destination
|
|
|
74
|
- `telemetry_manifest.json` written to destination as a placeholder for a future FIT sync pipeline
|
|
|
75
|
|
|
|
76
|
## Output Format (quiet mode, default)
|
|
|
77
|
|
|
|
78
|
```
|
|
|
79
|
2026-05-04 12:08:00 : Transcoding SampleFootage/Day/2026-04-04_18-10-36.mp4 ... done in 5s
|
|
|
80
|
2026-05-04 12:08:04 : Transcoding SampleFootage/Day/2026-04-04_18-10-05.mp4 ... done in 4s
|
|
|
81
|
[2026-05-04 12:08:35] [INFO] Summary: videos_processed=10 videos_skipped=0 errors=0
|
|
|
82
|
[2026-05-04 12:08:35] [INFO] Timing: total_elapsed=44s, video_encode_avg=4s
|
|
|
83
|
```
|
|
|
84
|
|
|
|
85
|
## Safety
|
|
|
86
|
|
|
|
87
|
- Destination inside source is rejected with an error
|
|
Bogdan Timofte
authored
a month ago
|
88
|
- `--delete-source` only deletes source after codec + duration validation passes
|
|
Bogdan Timofte
authored
a month ago
|
89
|
- `--dry-run` never writes files
|
|
Bogdan Timofte
authored
a month ago
|
90
|
- `Ctrl+C` behavior during long runs:
|
|
|
91
|
first press requests stop after current file; second press force-stops current encode
|
|
|
92
|
- On macOS, unreadable MP4/MOV sources automatically try `avconvert --preset PresetPassthrough`
|
|
|
93
|
as fallback before being marked as unreadable
|
|
|
94
|
|
|
|
95
|
Backward-compatible aliases:
|
|
|
96
|
- `--move-source` maps to `--delete-source`
|
|
|
97
|
- `--continue-on-error` maps to `--keep-going`
|
|
|
98
|
|
|
|
99
|
## Media Cleanup Utility
|
|
|
100
|
|
|
|
101
|
`cleanup_garmin_varia_media_folder.sh` is a companion utility for import and transcoded media folders.
|
|
|
102
|
|
|
|
103
|
What it does:
|
|
|
104
|
- Removes AppleDouble artifacts (`._*`) up to 4096 bytes
|
|
|
105
|
- Removes zero-size `.mp4` files
|
|
|
106
|
- Normalizes duplicate timestamp files:
|
|
|
107
|
if `YYYY-MM-DD_HH-MM-SS.mp4` is missing and exactly one
|
|
|
108
|
`YYYY-MM-DD_HH-MM-SS_<n>.mp4` exists, it renames it to base name
|
|
|
109
|
- Reports blocked duplicate groups when base exists or multiple suffixed duplicates exist
|
|
|
110
|
|
|
|
111
|
Usage:
|
|
|
112
|
|
|
|
113
|
```bash
|
|
|
114
|
# Preview only
|
|
|
115
|
./cleanup_garmin_varia_media_folder.sh --dry-run ~/Autofs/xdev/autonas/ext01/@Camera/import
|
|
|
116
|
|
|
|
117
|
# Apply changes
|
|
|
118
|
./cleanup_garmin_varia_media_folder.sh ~/Autofs/xdev/autonas/ext01/@Camera/import
|
|
|
119
|
|
|
|
120
|
# Example on transcoded output folder
|
|
|
121
|
./cleanup_garmin_varia_media_folder.sh --dry-run ~/Autofs/xdev/is-baobab/nvme0n1/@backup/Garmin
|
|
|
122
|
```
|
|
|
123
|
|
|
|
124
|
Exit codes:
|
|
|
125
|
- `0`: cleanup completed with no blocked duplicate groups
|
|
|
126
|
- `1`: cleanup completed but blocked duplicate groups require manual review
|
|
|
127
|
- `2`: invalid invocation or runtime error
|
|
Bogdan Timofte
authored
a month ago
|
128
|
|
|
|
129
|
## Source Files
|
|
|
130
|
|
|
|
131
|
```
|
|
|
132
|
SampleFootage/ Original clips (gitignored)
|
|
|
133
|
Output/ Transcoded output (gitignored)
|
|
|
134
|
garmin_varia_transcode.sh Main script
|
|
Bogdan Timofte
authored
a month ago
|
135
|
cleanup_garmin_varia_media_folder.sh Media cleanup utility (import + transcoded folders)
|
|
Bogdan Timofte
authored
a month ago
|
136
|
CHANGELOG.md Version history
|
|
|
137
|
DEVLOG.md Development decisions and rationale
|
|
|
138
|
```
|