{"record":{"id":"906a20fa244536ea","repo":"pion/webrtc","slug":"w-ambisonics-family-type-3-is-not-supported","errorCode":null,"errorMessage":"%w: ambisonics family type 3 is not supported","messagePattern":"%w: ambisonics family type 3 is not supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/media/oggreader/oggreader.go","lineNumber":245,"sourceCode":"\theader.PreSkip = binary.LittleEndian.Uint16(payload[10:12])\n\theader.SampleRate = binary.LittleEndian.Uint32(payload[12:16])\n\theader.OutputGain = binary.LittleEndian.Uint16(payload[16:18])\n\theader.ChannelMap = payload[18]\n\n\treturn header\n}\n\n// parseChannelMapping parses channel mapping data based on the channel map family.\n// https://datatracker.ietf.org/doc/html/rfc7845#section-5.1.1\n// family mapping of 2 and 3 are defined in https://datatracker.ietf.org/doc/html/rfc8486\nfunc parseChannelMapping(header *OggHeader, payload []byte) error {\n\tswitch header.ChannelMap {\n\tcase 0:\n\t\treturn validatePayloadLength(payload, idPageBasePayloadLength)\n\tcase 1, 2, 255:\n\t\treturn parseExtendedChannelMapping(header, payload)\n\tcase 3:\n\t\treturn fmt.Errorf(\"%w: ambisonics family type 3 is not supported\", errUnsupportedChannelMappingFamily)\n\tdefault:\n\t\treturn errUnsupportedChannelMappingFamily\n\t}\n}\n\nfunc validatePayloadLength(payload []byte, expectedLen int) error {\n\tif len(payload) != expectedLen {\n\t\treturn errBadIDPageLength\n\t}\n\n\treturn nil\n}\n\nfunc parseExtendedChannelMapping(header *OggHeader, payload []byte) error {\n\texpectedPayloadLen := 21 + int(header.Channels)\n\tif err := validatePayloadLength(payload, expectedPayloadLen); err != nil {\n\t\treturn err\n\t}","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/pion/webrtc/blob/8c25dc09fa9e7c09aac4309ead88093d760432b0/pkg/media/oggreader/oggreader.go#L227-L263","documentation":"ParseOpusHead refuses to parse an Opus identification header whose ChannelMappingFamily byte is 3 (ambisonics projection). The library supports family 0 (RTP/vorbis order), 1/255 (discrete), and 2 (extended mapping); family 3 requires ambisonics channel ordering the reader does not implement. The returned error wraps errUnsupportedChannelMappingFamily so callers can detect it with errors.Is.","triggerScenarios":"Calling oggreader.ParseOpusHead on an OpusHead packet whose byte at offset 18 (ChannelMappingFamily) equals 3; readOpusHeader reaches parseChannelMapping and hits the case 3 branch.","commonSituations":"Decoding ambisonic/VR surround streams (e.g. YouTube 360 audio, B-format first-order ambisonics Opus streams); working with recently standardized ambisonics Opus content that older tooling, including this reader, does not yet support.","solutions":["Remap or transcode the stream to channel mapping family 1 or 2 before feeding it to ParseOpusHead (e.g. with ffmpeg -c:a libopus -mapping_family 1 or 0).","If ambisonics metadata is not needed, patch parseChannelMapping to treat family 3 as discrete channels (route it to parseExtendedChannelMapping) and document the limitation.","Handle the error at the call site: check errors.Is(err, oggreader.ErrUnsupportedChannelMappingFamily) and skip or reject the stream gracefully."],"exampleFix":"// before\n# ffmpeg -i in.opus -c copy out.opus\n// after\n# force a supported channel mapping family\n# ffmpeg -i in.opus -c:a libopus -mapping_family 1 out.opus","handlingStrategy":"validation","validationCode":"func isAmbisonicsOpus(head []byte) bool {\n    return len(head) > 18 && head[0]=='O' && head[1]=='p' && head[2]=='u' && head[3]=='s' &&\n        head[4]=='H' && head[5]=='e' && head[6]=='a' && head[7]=='d' && head[18] == 3\n}\n// skip or transcode when isAmbisonicsOpus(packet) is true","typeGuard":"func isSupportedOpusHead(p []byte) bool {\n    if len(p) < 19 || string(p[:8]) != \"OpusHead\" {\n        return false\n    }\n    switch p[18] {\n    case 0, 1, 2, 255:\n        return true\n    default:\n        return false\n    }\n}","tryCatchPattern":null,"preventionTips":["Before parsing, inspect the ChannelMappingFamily byte at offset 18 of OpusHead and reject family 3 upstream.","Configure encoders/transcoders with -mapping_family 0 or 1 unless ambisonics is explicitly required.","Add a test with a family-3 OpusHead fixture so the rejection path is exercised.","Match errors.Is(err, oggreader.ErrUnsupportedChannelMappingFamily) to distinguish this from corrupt-data errors."],"tags":["go","ogg","opus","unsupported-feature","ambisonics"],"backgroundTag":"unsupported-channel-mapping-family","analyzedSha":"8c25dc09fa9e7c09aac4309ead88093d760432b0","analyzedAt":"2026-09-03T21:56:56.182Z","contentChangedAt":"2026-09-03T21:56:56.182Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}