Skip to content

Codec & client support

Lumastra plays your files directly whenever the client can handle them, and only remuxes (repackages the streams) or transcodes (re-encodes) when it has to. The less transcoding, the lower the CPU/GPU cost and the better the quality, so it’s worth knowing what plays natively.

Lumastra picks one of three delivery paths, in order:

PathUsed byNotes
Direct fileAny client, when the codec and container are supportedThe raw file is streamed as-is: no processing.
Native HLSSafari onlySafari’s built-in player handles HEVC, AC-3/E-AC-3, and more than other browsers.
MSE HLS (hls.js)Chrome, Firefox, Edge, …The universal fallback; Lumastra remuxes or transcodes into it as needed.

The server always prefers direct file when the file is fully compatible, then native HLS, then MSE HLS. It isn’t biased toward HLS for any browser.

Legend for the tables: ✓ direct play · R remux (copied, no re-encode) · T transcode (re-encoded) · — transcoded/unsupported.

CodecChrome / EdgeFirefoxSafariApple TVAndroid TV
H.264 (8-bit)✓✓✓✓✓
H.264 10-bit (Hi10P)TTTTT
HEVC / H.265✓¹✓¹✓✓✓
VP9✓✓TT✓
AV1✓²✓²✓T✓
MPEG-2 / VC-1 / MPEG-4TTTT✓³

¹ HEVC needs a recent browser and hardware decode support.
² AV1 direct-plays from a file, but Lumastra disables AV1 over hls.js by default (a known parser bug), so AV1 may transcode on the web HLS path.
³ Android TV / ExoPlayer direct-plays several legacy codecs others can’t.

When an audio track can’t be played, Lumastra transcodes that track to AAC (stereo for browsers, up to 5.1 on Safari/Apple). Video is untouched.

CodecChrome / Edge / FirefoxSafari / AppleAndroid TV
AAC, MP3✓✓✓
AC-3 (Dolby Digital), E-AC-3 (DD+)T⁴✓✓
FLAC✓ (direct file) / T (HLS)T✓
Opus✓T✓
ALACT✓✓
DTS, DTS-HD, TrueHD / Atmos, PCM, VorbisTT✓⁵

⁴ AC-3/E-AC-3 copy fine on Safari’s native HLS and on direct file, but transcode to AAC on the web (MSE HLS) path.
⁵ Android TV’s profile passes through DTS/TrueHD.

ContainerResult
MP4 / M4V / MOV✓ direct file
MKV / MatroskaR: remuxed (streams copied into HLS, no re-encode)
WebM✓ on Chrome/Firefox · remux on Safari
MPEG-TS / M2TSServed via HLS
AVIR / T: remux or transcode

A container that isn’t directly playable only needs a remux (cheap) as long as its codecs are supported. Re-encoding is only triggered by an unsupported codec.

SourceDV-capable (Safari, Apple TV)HDR10-capable (Chrome / Edge)No HDR (Firefox, SDR screens)
SDR✓✓✓
HDR10 / HLG✓ pass-through✓ pass-throughTone-mapped to SDR
Dolby Vision (Profile 8)✓ pass-through (full DV)Transcoded to HDR10¹Tone-mapped to SDR
Dolby Vision (Profile 5/7)✓ pass-through (full DV)Tone-mapped to SDRTone-mapped to SDR

¹ Profile 8 Dolby Vision carries an HDR10 base layer. When Lumastra has to transcode it for an HDR-capable (but not DV) client, it keeps the picture in HDR: it outputs HDR10 (or HLG) and copies the mastering metadata, instead of flattening it to SDR. Many servers tone-map all Dolby Vision to SDR on transcode; Lumastra preserves HDR10 here by default (ENABLE_HDR10_PRESERVATION, default on). Profile 5/7 has no HDR10 base layer, so it tone-maps to SDR for non-DV clients.

When a client can’t display the source HDR, Lumastra tone-maps to SDR during transcode (curve set by TONEMAPPING_ALGORITHM, default bt2390). Direct play and remux always preserve the source HDR untouched, including full Dolby Vision.

Most subtitle formats render without burning into the video. Text formats use the native player, and image formats (PGS, VobSub) and styled ASS/SSA render client-side via WebAssembly. Only two formats force a burn-in (which forces a video transcode):

FormatRendering
SRT / SubRip, WebVTT, MOV_TEXT, TTMLNative text track
ASS / SSAClient-side (AkariSub)
PGS, VobSub / DVDClient-side (libbitsub)
DVB-sub, XSUBBurned in (server-side): forces a video transcode

For a given file and client, Lumastra decides in this order:

  1. Dolby Vision on a non-DV client → full transcode (tone-map).
  2. Video codec unsupported, or a constraint (level, profile, resolution, bitrate) exceeded → full transcode.
  3. Audio has too many channels, or no compatible track → transcode the audio.
  4. Container unsupported but codecs are fine → remux.
  5. Multiple audio tracks the client can’t switch between in direct file → remux.
  6. Everything supported → direct play.

When transcoding video, Lumastra prefers hardware encoders (AV1 → HEVC → H.264) and falls back to software (H.264 first for speed); audio always transcodes to AAC.

For the widest direct-play across clients, prefer:

  • Video: H.264 (8-bit) or HEVC
  • Audio: AAC (or AC-3 if your clients are mostly Apple)
  • Container: MP4

That combination direct-plays on essentially every client with zero transcoding.

Standalone music playback (/api/music, /api/v1/music, and the Subsonic-compatible /subsonic surface) is a separate delivery path from video, with its own decision logic and codec matrix.

Direct play is the default for music too. A track only gets transcoded when one of these applies:

  • The client requests a format that differs from the source container.
  • A maxBitrateKbps cap is requested and the source exceeds it.
  • Playback starts at a startPositionSeconds offset (a seek before the stream begins).
  • The client is an OpenSubsonic app that negotiated a transcode through the transcoding/transcodeOffset extensions (see the API reference).

Requesting format=raw skips transcoding entirely and goes straight to direct play, but only after authentication, the track/access lookup, and path resolution have already run. It doesn’t bypass those.

CodecContainerBitrate ladder (kbps)Default
MP3MPEG64, 96, 128, 160, 192, 256, 320192
OpusOgg32, 48, 64, 96, 128, 160, 192192
AACADTS64, 96, 128, 160, 192, 256, 320192