Help us shape the future of Go! Take the 2026 Go Developer Survey

h265

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: BSD-3-Clause Imports: 3 Imported by: 0

README

h265

Pure-Go (CGO=0) reader for the H.265/HEVC bitstream: the NAL layer, the parameter sets, the slice segment header, and the boundaries between coded pictures.

units, err := h265.SplitAnnexB(data)
sps, err := h265.ParseSPS(spsUnit)
pps, err := h265.ParsePPS(ppsUnit)
pics, err := h265.SplitPictures(units, pps)   // without decoding any

What it is not

It does not decode pictures. There is no CABAC, no residual, no transform, no prediction and no deblocking — nothing here turns a bitstream into pixels. It reads the syntax a caller needs in order to describe a track, cut a stream on picture boundaries, or hand slices to something that does decode.

What is in it

SplitAnnexB, SplitLengthPrefixed, Unit, UnitType the NAL layer, both framings
ParseSPS, ParsePPS the parameter sets
ParseSliceSegmentHeader, SliceSegmentHeader, SliceType the slice segment header
SplitPictures, Picture coded pictures, from first_slice_segment_in_pic_flag
ShortHeaderError a unit that ended inside the syntax, said as such

⛔ HEVC's slice types are not H.264's. Here B is 0, P is 1 and I is 2 — the reverse of H.264, where P is 0 and B is 1. A reader that shares a constant between the two formats reads every B slice as a P slice and every P slice as a B one, and the stream still parses.

A picture may be carried by several slice segments, and only the one with first_slice_segment_in_pic_flag set begins a new picture. That is what SplitPictures counts, which is how a stream's pictures are counted without decoding any of them.

Sibling

go-avkit/h264 is the same layer for H.264/AVC, and goes further: it also carries the derivations of clause 8.2 (picture order counts, the reference set, the reference lists, the prediction weights). HEVC's equivalents are not here yet.

Windows, macOS, Linux; six 64-bit architectures. 100% statement coverage, gated in CI.

Licence

BSD-3-Clause.

Documentation

Overview

Package h265 reads an H.265/HEVC bitstream in pure Go, with no libavcodec linkage and no external binaries.

The framing and the bit reading are go-avkit/bitstream's, shared with H.264: the two formats separate their units identically and differ in how the header bytes of a unit are read -- H.264 spends one and this spends two -- and in what the parameter sets hold. This package is that difference.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotPPS means the unit handed over is not a picture parameter set.
	ErrNotPPS = errors.New("h265: unit is not a picture parameter set")
	// ErrUnsupportedPPS means the set does not end where a set must end, so
	// something in it was read wrongly.
	ErrUnsupportedPPS = errors.New("h265: picture parameter set was not consumed exactly")
)

Errors a picture parameter set can be refused with.

View Source
var (
	// ErrNotSlice means the unit handed over does not carry a slice segment.
	ErrNotSlice = errors.New("h265: unit does not carry a slice segment")
	// ErrSliceHeader means a segment states something this reader does not follow.
	ErrSliceHeader = errors.New("h265: unsupported slice segment header")
)

Errors a slice segment header can be refused with.

View Source
var (
	// ErrNotSPS means the unit handed over is not a sequence parameter set.
	ErrNotSPS = errors.New("h265: unit is not a sequence parameter set")
	// ErrUnsupportedSPS means the set states something this reader does not
	// follow.
	ErrUnsupportedSPS = errors.New("h265: unsupported sequence parameter set")
)

Errors a sequence parameter set can be refused with.

Functions

This section is empty.

Types

type PPS

type PPS struct {
	ID                     uint32
	SPSID                  uint32
	DependentSlicesEnabled bool
	OutputFlagPresent      bool
	ExtraSliceHeaderBits   uint8
	SignDataHiding         bool
	CABACInitPresent       bool
	NumRefIdxL0            uint32
	NumRefIdxL1            uint32
	InitQP                 int32 // already un-biased
	ConstrainedIntraPred   bool
	TransformSkip          bool
	CUQPDeltaEnabled       bool
	CbQPOffset             int32
	CrQPOffset             int32
	SliceChromaQPOffsets   bool
	WeightedPred           bool
	WeightedBipred         bool
	TransquantBypass       bool
	TilesEnabled           bool
	EntropySyncEnabled     bool
	TileColumns            uint32
	TileRows               uint32
	UniformTileSpacing     bool
	DeblockingOverride     bool
	DeblockingDisabled     bool
	BetaOffsetDiv2         int32
	TCOffsetDiv2           int32
	ListsModification      bool
	SliceHeaderExtension   bool
}

PPS is what a picture parameter set says about the slices that refer to it.

Two of these fields are what a slice segment header cannot be read without: whether a segment may depend on the one before it, and how many bits of the header the stream reserves before the slice type. A reader without them is misaligned from the first field that matters.

func ParsePPS

func ParsePPS(u Unit) (PPS, error)

ParsePPS reads a picture parameter set.

⛔ Unlike H.264's, this needs no other set to be consumed exactly: HEVC states the number of scaling lists by the size class rather than by the chroma format, so the variable parts here depend only on fields the set itself carries.

type Picture

type Picture struct {
	Units []Unit
	Type  SliceType
	// TypeKnown is false when no segment of this picture stated a type, which
	// happens when a stream is joined part way through a picture.
	TypeKnown bool
	Sync      bool
	Bytes     int
}

Picture is one coded picture: the units that carry it, and what it is.

Units are in the order they arrived, parameter sets and all, so a caller writing a sample table has the bytes of a sample and nothing else to gather.

func SplitPictures

func SplitPictures(units []Unit, pps PPS) ([]Picture, error)

SplitPictures groups a stream's units into the pictures they carry.

⛔ Where H.264 needs several fields compared between neighbouring slices to find a boundary, HEVC states it outright: one flag per segment says whether it begins a picture. That is the whole rule here, and it is worth saying that the simpler format is simpler rather than carrying the other one's machinery over.

Parameter sets and delimiters that arrive before a picture's first segment belong to that picture: a decoder must hold them before the segment that uses them.

type SPS

type SPS struct {
	VPSID          uint8
	MaxSubLayers   uint8
	ProfileSpace   uint8
	Tier           uint8
	ProfileIDC     uint8
	LevelIDC       uint8
	ID             uint32
	ChromaFormat   uint8
	SeparatePlanes bool
	CodedWidth     uint32
	CodedHeight    uint32
	WinLeft        uint32
	WinRight       uint32
	WinTop         uint32
	WinBottom      uint32
	Width          uint32
	Height         uint32
	BitDepthLuma   uint8
	BitDepthChroma uint8
	Log2MaxPOCLSB  uint8
}

SPS is what a sequence parameter set says about the pictures that follow.

Width and Height are what a player shows and what a container states: the coded size less the conformance window. Unlike H.264 the coded size is stated in luma samples directly rather than in macroblocks, so there is no multiply -- but the window still has to be taken off, and its offsets are in CHROMA units.

func ParseSPS

func ParseSPS(u Unit) (SPS, error)

ParseSPS reads a sequence parameter set.

type ShortHeaderError

type ShortHeaderError struct {
	Unit  int
	Bytes int
}

ShortHeaderError means a unit is too short to hold the two bytes of header this format spends.

func (*ShortHeaderError) Error

func (e *ShortHeaderError) Error() string

type SliceSegmentHeader

type SliceSegmentHeader struct {
	First        bool // first_slice_segment_in_pic_flag
	Dependent    bool // only meaningful when First is false
	PPSID        uint32
	Type         SliceType
	TypeRead     bool
	RandomAccess bool // the unit type says this picture can stand alone
	// NoOutputOfPriorPics is stated only by a random access picture.
	NoOutputOfPriorPics bool
}

SliceSegmentHeader is the beginning of a slice segment: which picture it belongs to, whether it starts one, and -- when it starts one -- how that picture is coded.

⛔ Type is read only for a segment that BEGINS a picture, and TypeRead says so. Reaching the type of a later segment means first reading its address, whose width comes from the coding tree block size in the sequence set -- a field this package does not read yet. Reporting a type it had not read would be worse than saying it did not: a picture's type would then come from whichever segment was looked at.

func ParseSliceSegmentHeader

func ParseSliceSegmentHeader(u Unit, pps PPS) (SliceSegmentHeader, error)

ParseSliceSegmentHeader reads the beginning of a slice segment.

It needs the picture parameter set, which states whether a segment may depend on the one before it and how many bits the stream reserves before the slice type. Both change where the type sits, so a reader without them is misaligned from the first field that matters.

type SliceType

type SliceType uint8

SliceType is how a slice is coded. HEVC numbers them the other way round from H.264, and naming them here rather than reusing a number is the point.

const (
	SliceB SliceType = 0
	SliceP SliceType = 1
	SliceI SliceType = 2
)

The slice types.

func (SliceType) String

func (t SliceType) String() string

String names a slice type the way tools report picture types.

type Unit

type Unit struct {
	Type UnitType
	// LayerID is which layer of a scalable stream this belongs to. A plain
	// stream states zero throughout, and a decoder that ignored it would mix the
	// layers of one that does not.
	LayerID uint8
	// TemporalID is the sub-layer, one more than the field on the wire. A stream
	// can be thinned by dropping the higher ones, which is what it is for.
	TemporalID uint8
	Payload    []byte
}

Unit is one NAL unit: its two header bytes read, and its payload still escaped.

Payload is a window into the caller's bytes and is not copied.

func SplitAnnexB

func SplitAnnexB(data []byte) ([]Unit, error)

SplitAnnexB finds the NAL units of a byte stream and reads each one's header.

func SplitLengthPrefixed

func SplitLengthPrefixed(data []byte, lengthSize int) ([]Unit, error)

SplitLengthPrefixed finds the units of the form an MP4 sample carries, where each is preceded by its length in lengthSize bytes, and reads each one's header.

lengthSize comes from the hvcC record.

func (Unit) Unescape

func (u Unit) Unescape() []byte

Unescape undoes the escaping inside this unit's payload.

type UnitType

type UnitType uint8

UnitType is what a NAL unit holds. Only the ones this package acts on are named; the rest travel as their number.

const (
	UnitTrailN    UnitType = 0
	UnitTrailR    UnitType = 1
	UnitBLAWLP    UnitType = 16
	UnitIDRWRADL  UnitType = 19
	UnitIDRNLP    UnitType = 20
	UnitCRA       UnitType = 21
	UnitVPS       UnitType = 32
	UnitSPS       UnitType = 33
	UnitPPS       UnitType = 34
	UnitAUD       UnitType = 35
	UnitEOS       UnitType = 36
	UnitEOB       UnitType = 37
	UnitFD        UnitType = 38
	UnitPrefixSEI UnitType = 39
	UnitSuffixSEI UnitType = 40
)

The unit types a reader has to tell apart.

⛔ A picture's type is not one flag here as it is in H.264. HEVC names sixteen kinds of slice segment, and which of them can be decoded without anything before it is what a sample table needs: the ones from BLA_W_LP to CRA are exactly that set, and IsRandomAccess is where the boundary is written down rather than scattered.

func (UnitType) IsRandomAccess

func (t UnitType) IsRandomAccess() bool

IsRandomAccess says whether a picture of this type can be decoded with nothing before it, which is what a sample table calls a sync sample.

func (UnitType) IsSliceSegment

func (t UnitType) IsSliceSegment() bool

IsSliceSegment says whether a unit carries coded picture data.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL