HLS manifest declares mp4a.40.2 for any unrecognised audio codec, making the stream unplayable
Disclaimer: the issue was AI generated, but I support it to enable FLAC audio streaming in the web player.
Describe the current behavior
getAudioStreamCodec() in server/core/helpers/ffmpeg/codecs.ts maps an audio
codec name to the CODECS attribute written into the HLS master playlist. It
knows four codecs and falls back to AAC for everything else:
if (audioCodecName === 'opus') return 'opus'
if (audioCodecName === 'vorbis') return 'vorbis'
if (audioCodecName === 'aac') return 'mp4a.40.2'
if (audioCodecName === 'mp3') return 'mp4a.40.34'
logger.warn('Cannot get audio codec of %s.', path, { audioStream })
return 'mp4a.40.2' // FallbackThat value is written verbatim into #EXT-X-STREAM-INF:...,CODECS="..." in
server/core/lib/hls.ts. When an audio rendition holds any other codec, the
manifest tells the player the audio is AAC when it is not. hls.js creates a
SourceBuffer for mp4a.40.2, appending the real segments fails, and playback
stops.
With a FLAC audio rendition, the file on disk is correct:
$ ffprobe -select_streams a:0 -show_entries stream=codec_name,channels,bit_rate ...-0-fragmented.mp4
codec_name=flac
channels=2
bit_rate=194386while the manifest describes it as AAC:
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="Audio",AUTOSELECT=YES,DEFAULT=YES,URI="....m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=923594,RESOLUTION=1280x720,FRAME-RATE=30,CODECS="avc1.64001f,mp4a.40.2",AUDIO="audio"and the player fails:
HLS.js error: mediaError - fatal: false - bufferAppendingError
HLS.js error: mediaError - fatal: false - bufferAppendError
VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) The media could not be loaded,
either because the server or network failed or because the format is not supported.Everything else in the chain works: ffmpeg muxes FLAC into fMP4 without complaint,
the transcoding job completes, the video is published, and the video renditions are
unaffected. Only the codec string is wrong, and the fallback is silent apart from a
logger.warn, so the failure surfaces only in the browser.
Note that stock PeerTube cannot currently produce a non-AAC HLS audio rendition:
the audio encoders are libfdk_aac and aac, and canDoQuickAudioTranscode()
requires the source to be AAC. Today this is reached through a transcoding plugin
whose audio builder returns copy: true for a non-AAC stream, which the plugin
API explicitly allows. The same silent breakage would apply to any codec added
later.
Steps to reproduce
- Register a transcoding plugin whose audio builder returns
{ copy: true }for a non-AAC source. - Upload a video whose audio track is FLAC, stereo, 44.1 kHz, with HLS enabled.
- Open the video once transcoding finishes.
Describe the expected behavior
The manifest describes the codec the rendition actually contains, and the browser plays it. Chromium supports FLAC in MP4, including through Media Source Extensions, which is what hls.js uses:
document.createElement('video').canPlayType('audio/mp4; codecs="flac"') // "probably"
MediaSource.isTypeSupported('audio/mp4; codecs="flac"') // true
MediaSource.isTypeSupported('audio/mp4; codecs="fLaC"') // trueAdding the mapping would be enough for this case:
if (audioCodecName === 'flac') return 'fLaC'fLaC is the ISO BMFF sample entry name for FLAC; Chromium accepts either
spelling.
Separately, and independent of FLAC: returning a wrong codec string as a
fallback guarantees an unplayable stream, whereas CODECS is an optional
attribute in the HLS specification. Returning '' for an unknown codec — which
hls.ts already filters out of the joined list — would let the player work it out
instead of being told something false, so any unrecognised codec would fail softly
rather than fatally.
Additional information
PeerTube instance:
- URL: local test instance, not publicly reachable
- Version: 8.2.4
- NodeJS version: v22.23.2
- Ffmpeg version: 7.1.5-0+deb13u1 (as shipped in
chocobozzz/peertube:v8.2.4-trixie)
Browser name, version and platforms on which you could reproduce the bug: Chromium 151.0.7922.173, Linux
Link to browser console log if relevant: quoted above —
bufferAppendErrorfrom hls.js, followed byMEDIA_ERR_SRC_NOT_SUPPORTEDfrom video.jsLink to server log if relevant: the transcoding job logs no error; it completes normally and publishes the video
Configuration: HLS enabled with split audio and video.
Source: Chocobozzz/PeerTube