Media Playback
Which video and audio formats packaged web apps can play, and how to encode them.
Supported formats
Board OS 2.3.0 and later support:
- Video: H.264/AVC and HEVC in MP4 or M4V, plus VP8, VP9, and AV1 in WebM
- Audio: AAC, MP3, Opus, Vorbis, and FLAC
Recommended encoding
H.264 MP4 at CRF 26 is visually transparent on the Board panel and keeps files small (about 6 to 7 MB per 30 seconds of 1080p):
ffmpeg -i input.mov -vf "scale=1920:1080" \
-c:v libx264 -profile:v high -preset slow -crf 26 -pix_fmt yuv420p \
-c:a aac -b:a 128k -movflags +faststart output.mp4
Why these parameters:
| Parameter | Why |
|---|---|
scale=1920:1080 |
The Board panel is 1920x1080, so higher resolutions spend bits the display can never show. Skip the filter if your source is already 1080p or smaller. |
libx264 -profile:v high |
H.264 decodes in hardware on Board. High profile compresses about 15% better than Main with no compatibility cost. |
-preset slow |
Better compression for the same quality. You encode once, so encode time does not matter. |
-crf 26 |
Measured VMAF 96 against the 1080p reference, above the 95 threshold generally treated as perceptually lossless. Use 22 if you want extra margin, 28 if you are fighting for space; below that quality visibly drops. |
-pix_fmt yuv420p |
Required for hardware decoder compatibility. Sources from screen recorders and design tools often default to 4:2:2 or 4:4:4, which will not play. |
-c:a aac -b:a 128k |
Transparent on the Board speakers. |
-movflags +faststart |
Moves the stream index to the front of the file so playback can start before the whole file is fetched. Matters for anything played over the network. |
WebM (VP9 with Opus audio) works equally well if you prefer an open toolchain:
ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 32 -b:v 0 -row-mt 1 -c:a libopus -b:a 96k output.webm
Guidance:
- Keep clips at or below 1920x1080 at 30 fps. The Board panel is 1920x1080.
- All supported video codecs (H.264, HEVC, VP8, VP9, AV1) decode in hardware.
- Bundle short clips inside your app. For long videos, stream from an HTTPS host that supports range requests. Seeking within large bundled files is limited.
- Remote video via
<video src="https://...">works directly. Loading remote media withfetch()additionally requires CORS headers on the host.
Autoplay
Playback does not require a user gesture. Muted autoplay and programmatic play() both work as soon as your app loads.
Troubleshooting
Playback failures raise a MediaError on the video element. Listen for the error event and log it; your app’s console output streams off the device with board-connect logs <appId>:
video.addEventListener('error', () => {
console.error('video error', video.error?.code, video.error?.message);
});