AlexxIT/go2rtc · error

unsupported iso audio

Error message

unsupported iso audio: ${codec}

What it means

WriteAudio in the ISO BMFF (MP4) muxer panics when given an audio codec with no implemented sample entry. Supported entries include Opus, PCMU (ulaw), PCMA (alaw) and others handled by the switch; any unlisted core.Codec reaches the default branch and panics.

Solutions

  1. Transcode the audio to a supported codec (e.g. Opus, PCMU/PCMA) before muxing.
  2. Guard the call: check the codec against the supported set and return an error instead of panicking.
  3. Extend the switch in pkg/iso/codecs.go WriteAudio with the needed sample entry atom if the codec must be supported.
  4. Wrap muxing with recover to translate the panic into an error for callers.

Example fix

// before
m.WriteAudio(trackID, core.CodecMP3, ...) // panics: unsupported iso audio: MP3
// after
switch codec {
case core.CodecOpus, core.CodecPCMU, core.CodecPCMA:
    m.WriteAudio(trackID, codec, ...)
default:
    return fmt.Errorf("mp4 muxer: unsupported audio codec %s", codec)
}
Defensive patterns

Strategy: validation

Validate before calling

var supportedAudio = map[core.Codec]bool{core.CodecOpus: true, core.CodecPCMU: true, core.CodecPCMA: true}
if !supportedAudio[codec] {
    return fmt.Errorf("unsupported iso audio codec: %s", codec)
}
m.WriteAudio(trackID, codec, w, timescale)

Type guard

func isSupportedISOAudio(c core.Codec) bool {
    switch c {
    case core.CodecOpus, core.CodecPCMU, core.CodecPCMA:
        return true
    }
    return false
}

Try / catch

func safeWriteAudio(m *iso.Muxer, args...) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("iso muxer panic: %v", r)
        }
    }()
    m.WriteAudio(args...)
    return nil
}

Prevention

When it happens

Trigger: Calling WriteAudio with a codec not covered by the switch (e.g. AAC variants not mapped, G.722, MP3) while muxing an audio track into MP4.

Common situations: Camera or SIP stream publishing an audio codec the muxer does not know; passing codec values straight through from an RTSP SDP without mapping; new codec added to core.Codec without updating pkg/iso.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/52dcb2809b4204bd. Report an issue: GitHub.

Appendix: source

Thrown at pkg/iso/codecs.go:65

	m.EndAtom()

	m.EndAtom() // AVC1
}

func (m *Movie) WriteAudio(codec string, channels uint16, sampleRate uint32, conf []byte) {
	switch codec {
	case core.CodecAAC, core.CodecMP3:
		m.StartAtom("mp4a") // supported in all players and browsers
	case core.CodecFLAC:
		m.StartAtom("fLaC") // supported in all players and browsers
	case core.CodecOpus:
		m.StartAtom("Opus") // supported in Chrome and Firefox
	case core.CodecPCMU:
		m.StartAtom("ulaw")
	case core.CodecPCMA:
		m.StartAtom("alaw")
	default:
		panic("unsupported iso audio: " + codec)
	}

	if channels == 0 {
		channels = 1
	}

	m.Skip(6)
	m.WriteUint16(1)                    // data_reference_index
	m.Skip(2)                           // version
	m.Skip(2)                           // revision
	m.Skip(4)                           // vendor
	m.WriteUint16(channels)             // channel_count
	m.WriteUint16(16)                   // sample_size
	m.Skip(2)                           // compression id
	m.Skip(2)                           // reserved
	m.WriteFloat32(float64(sampleRate)) // sample_rate

	switch codec {

View on GitHub (pinned to c245815e75)