AlexxIT/go2rtc · error
bitstream: unsupported header:
Error message
bitstream: unsupported header:
What it means
bitstream.Open is given a raw Annex-B H.264/H.265 elementary stream. After the magic.Open sniffing dispatches on the annexb start code, bitstream peeks 256 bytes and requires the first NAL unit to be an H.264 SPS or an H.265 VPS so it can build codec parameters. If the first NAL is anything else (an IDR/non-IDR slice, AUD, SEI, etc.), it cannot determine the codec and throws this error with the first 8 bytes hex-encoded.
Solutions
- Ensure the bitstream begins with the parameter sets: prepend the SPS/PPS (H.264) or VPS/SPS/PPS (H.265) NAL units in Annex-B format before the first slice.
- Re-encode or remux the source with parameter sets inline, e.g. `ffmpeg -i in -c copy -bsf:v h264_mp4toannexb out.h264`, which inserts SPS/PPS at each keyframe.
- Check that the producer that created the stream was started before the camera/encoder began, so the first bytes captured are the parameter sets rather than a mid-stream slice.
- Verify the input is actually H.264/H.265 Annex-B; if it is MP4-format (AVCC, length-prefixed) or another container, use the matching package instead of the bitstream one.
Example fix
// before: feeding a dump that starts with an IDR slice
f, _ := os.Open("gop-dump.h264")
p, err := magic.Open(f) // error: bitstream: unsupported header: 410102...
// after: prepend SPS/PPS captured at stream start
f, _ := os.Open("gop-dump.h264")
r := io.MultiReader(bytes.NewReader(spsppsAnnexB), f)
p, err := magic.Open(r) Defensive patterns
Strategy: validation
Validate before calling
func isAnnexBParameterStream(data []byte) bool {
// after start code (00 00 00 01), first NAL must be SPS(7) or VPS(32)
if !bytes.HasPrefix(data, []byte{0, 0, 0, 1}) {
return false
}
n := data[4] & 0x7E // nal type = (b>>1)&0x3F
nalType := (data[4] >> 1) & 0x3F
return nalType == 7 || nalType == 32
}
// call before magic.Open: if !ok, prepend/prepend SPS/PPS or transcode first Try / catch
p, err := magic.Open(rd)
if err != nil && strings.Contains(err.Error(), "bitstream: unsupported header") {
// re-open after prepending parameter sets or transcoding
p, err = magic.Open(io.MultiReader(bytes.NewReader(spsPpsAnnexB), original))
} Prevention
- Always capture the stream from its start so SPS/PPS (H.264) or VPS/SPS/PPS (H.265) are the first NAL units
- Use ffmpeg with h264_mp4toannexb/hevc_mp4toannexb bitstream filters to guarantee inline parameter sets
- Validate the first bytes (start code + NAL type) before handing a reader to magic.Open
When it happens
Trigger: Calling magic.Open (or bitstream.Open directly) on a stream whose Annex-B start code is followed by a NAL unit that is not SPS (H.264 type 7) or VPS (H.265 type 32) — e.g. the capture started mid-GOP on an IDR slice, the stream begins with AUD/SEI NALs, or non-Annex-B data was fed in and only coincidentally matched the start code.
Common situations: Dumping an RTP/RTSP track to file and feeding the dump back for consumption; ffmpeg piped output missing SPS because the encoder emits it only periodically; reading a truncated file whose SPS was cut off; piping H.265 streams whose first NAL is VPS in a different order than expected.
Related errors
- producer without tracks
- wrong status:
- ivideon: wrong message type:
- magic: unsupported header:
- mp4: unsupported codec:
AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07).
Data as JSON: /api/errors/bc170c377f9f7017.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/magic/bitstream/producer.go:41
buf, err := rd.Peek(256)
if err != nil {
return nil, err
}
buf = annexb.EncodeToAVCC(buf) // won't break original buffer
var codec *core.Codec
var format string
switch {
case h264.NALUType(buf) == h264.NALUTypeSPS:
codec = h264.AVCCToCodec(buf)
format = "h264"
case h265.NALUType(buf) == h265.NALUTypeVPS:
codec = h265.AVCCToCodec(buf)
format = "hevc"
default:
return nil, errors.New("bitstream: unsupported header: " + hex.EncodeToString(buf[:8]))
}
medias := []*core.Media{
{
Kind: core.KindVideo,
Direction: core.DirectionRecvonly,
Codecs: []*core.Codec{codec},
},
}
return &Producer{
Connection: core.Connection{
ID: core.NewID(),
FormatName: format,
Medias: medias,
Transport: r,
},
rd: rd,
}, nilView on GitHub (pinned to c245815e75)