Mobile's own video transcode produces MP4s the relay rejects (422 media contains metadata)
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
Self-hoster report, reproduced against a live relay. Sending a video from the Android app fails well under any size limit:
```
upload failed (422): {"error":"media contains metadata or a non-canonical metadata channel"}
```
## Reproduction (relay side, minimal)
```bash
# ffmpeg default output — carries metadata
ffmpeg -f lavfi -i testsrc=duration=2:size=320x240:rate=15 -pix_fmt yuv420p -movflags +faststart vid-normal.mp4
buzz upload file --file vid-normal.mp4
# -> relay error 422: {"error":"media contains metadata or a non-canonical metadata channel"}
# same content, metadata stripped
ffmpeg -i vid-normal.mp4 -map_metadata -1 -fflags +bitexact -movflags +faststart -c copy vid-stripped.mp4
buzz upload file --file vid-stripped.mp4
# -> 200, stored and served
```
So the relay policy works as designed: `crates/buzz-media/src/validation.rs::validate_mp4_metadata_free` walks the atom tree and rejects `meta` / non-canonical `udta` content. No complaint about the policy itself — stripping metadata from user media is a good default.
## The bug
`mobile/lib/shared/relay/media_upload.dart::uploadVideo` transcodes the picked video with `_transcodeVideoToMp4` and then uploads the result — but that output still carries metadata, so **the app's own pipeline produces files its own relay refuses**. From the user's side, sending a phone video simply fails with an error that reads like corruption.
Images do not have this problem because they go through `sanitizeImageForUpload` / `sanitizeAnimatedImageForUpload`. Video has no equivalent sanitisation step, and it looks like the generic file path may be exposed the same way.
## Suggested fix
Add metadata stripping to the transcode invocation — the equivalent of `-map_metadata -1` (plus bitexact) — so the transcoder emits exactly the canonical shape the validator accepts. That keeps the privacy property the relay is enforcing, and does it once, in the place that is already re-encoding the file anyway.
Two smaller things worth considering alongside it:
- surface the relay's actual reason in the UI, since "contains metadata" is not actionable for a user who only picked a video from their gallery;
- the same validator applies to plain file uploads, so a `.mp4` attached as a file rather than a video would hit it too.
Related: #5414 (mobile hardcodes 100 MB caps and cannot read the relay's real limits). Different failure, same area — a self-hosted relay currently cannot receive video from the mobile app at any size.
Contributor guide
Research direction
Start in mobile/lib/shared/relay/media_upload.dart at uploadVideo and _transcodeVideoToMp4, then compare the generated MP4 with the metadata-free command in the issue. Confirm the output satisfies crates/buzz-media/src/validation.rs::validate_mp4_metadata_free and that a video produced by the Android pipeline is accepted by the relay.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, dart, flutter
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100