block / block/buzz

Mobile's own video transcode produces MP4s the relay rejects (422 media contains metadata)

Open Beginner friendly
#5,415 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.