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

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 with fetch() 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);
});