hevc

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package hevc decodes an HEVC (H.265) bitstream, and encodes an intra-only one.

Decoder.DecodeNAL takes one NAL unit at a time and returns the pictures that are ready, which is not the same as the pictures it just decoded: a stream that codes out of display order is held back by sps_max_num_reorder_pics and released by picture order count. Decoder.Flush drains what is left at the end.

var d hevc.Decoder

for _, nal := range hevc.SplitAnnexB(data) {
	pics, err := d.DecodeNAL(nal)
	if err != nil {
		return err
	}

	for _, p := range pics {
		p.Release()
	}
}

SplitAnnexB frames a start-code delimited stream and SplitHVCC a length-prefixed one.

Pictures

A Picture holds its planes as either 8-bit or 16-bit samples, in Y/Cb/Cr or Y16/Cb16/Cr16, chosen by the sequence rather than by the plane: both are 16-bit if either Picture.BitDepth or Picture.BitDepthC exceeds 8, which 7.4.3.2 allows to differ. Width and Height stay as decoded because prediction reads the whole plane; CropX, CropY, CropW and CropH are what a caller should display.

Picture.Release hands the planes back to the decoder to be reused by a later picture. It is optional, since a picture that is never released is collected like any other value, but it keeps a long sequence from allocating a fresh set of planes per frame. Reading the planes afterwards is a mistake; releasing twice is not.

Threading

Decoder.Threads bounds the goroutines a picture may be spread over, across wavefront rows and the loop filter row bands. Zero means GOMAXPROCS and one decodes serially. A picture without entropy_coding_sync_enabled_flag, or one a single block wide, is serial whatever the bound.

Encoding

Encoder writes self-contained intra IDR access units from 8-bit 4:2:0 frames whose dimensions are non-zero and even. A picture that does not fill the coding grid is padded to it and cropped back by a conformance window. Every frame is coded on its own, so Encoder.Flush never has anything left to return.

enc, err := hevc.NewEncoder(hevc.EncoderOptions{Width: 1920, Height: 1080, QP: 26})
if err != nil {
	return err
}

nals, err := enc.Encode(hevc.Frame{Y: y, Cb: cb, Cr: cr, StrideY: ys, StrideC: cs})

MarshalAnnexB frames the result for a file and MarshalNAL writes one unit for a length-prefixed container, whose configuration record repeats the ProfileTierLevel of the sequence parameter set.

A picture is one slice of 64x64 coding tree blocks, coded as 32x32 units and as 16x16 ones along an edge a 32x32 does not fit. Prediction searches all 35 intra modes and the 8x8 transform blocks choose between one transform and four. EncoderOptions.Lossless codes the samples as PCM instead and ignores QP.

Errors

ErrInvalid means the bitstream is malformed. ErrUnsupported means it is valid and declares a coding tool this decoder does not implement, which is refused rather than decoded into a picture that merely looks plausible. Those tools are cross-component prediction, implicit and explicit RDPCM, and CABAC bypass alignment; everything else in the range extensions is applied.

Index

Constants

View Source
const MaxLumaSamples = 35651584

MaxLumaSamples is MaxLumaPs of Table A.8, which every level from 6.0 up shares. A picture larger than this has no level to be coded at, so a caller with one splits it over a grid of items instead.

Variables

View Source
var (
	ErrInvalid     = errors.New("hevc: invalid bitstream")
	ErrUnsupported = errors.New("hevc: unsupported feature")
)

ErrInvalid is returned for a bitstream that cannot be decoded, and ErrUnsupported for one using a feature this decoder does not implement.

View Source
var ErrInvalidEncodeInput = errors.New("hevc: invalid encode input")

ErrInvalidEncodeInput means the frame or the options do not describe something this encoder can code.

Functions

func MarshalAnnexB added in v0.2.0

func MarshalAnnexB(nals []NALUnit) []byte

func MarshalNAL added in v0.2.0

func MarshalNAL(nal NALUnit) []byte

func ProfileTierLevel added in v0.2.0

func ProfileTierLevel(sps []byte) ([]byte, bool)

ProfileTierLevel returns the twelve bytes of profile_tier_level, from general_profile_space through general_level_idc, out of a sequence parameter set RBSP. A container's decoder configuration record repeats them.

func SPSFormat added in v0.2.0

func SPSFormat(sps []byte) (chromaFormat, bitDepthLuma, bitDepthChroma int, ok bool)

SPSFormat is the chroma_format_idc and the two sample sizes a sequence parameter set declares. A container's decoder configuration record repeats them, and a reader that trusts it over the bitstream has to be told the truth.

Types

type ChromaFormat added in v0.2.0

type ChromaFormat int

ChromaFormat is the chroma sampling a picture is coded in, chroma_format_idc of 7.4.3.2 by another name. The zero value is 4:2:0.

const (
	Chroma420 ChromaFormat = iota
	Chroma422
	Chroma444
	ChromaMono
)

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder decodes a HEVC bitstream one NAL unit at a time.

func (*Decoder) DecodeNAL

func (d *Decoder) DecodeNAL(nal NALUnit) ([]*Picture, error)

func (*Decoder) Flush

func (d *Decoder) Flush() []*Picture

Flush ends the sequence and returns every picture still held back for reordering, in output order.

func (*Decoder) FrameSizeLimit added in v0.1.1

func (d *Decoder) FrameSizeLimit(n int)

DecodeNAL consumes one NAL unit and returns whatever pictures that completes, in output order. Reordering means a picture may surface several NAL units after the one that finished it. FrameSizeLimit refuses a sequence whose pictures are larger than n samples, with ErrUnsupported. Zero, the default, accepts anything the level allows.

func (*Decoder) Threads

func (d *Decoder) Threads(n int)

Threads bounds the goroutines decoding one picture's wavefront rows. Zero means GOMAXPROCS, one decodes serially. It is read once per slice segment, so changing it mid-stream takes effect at the next one.

type Encoder added in v0.2.0

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder writes self-contained intra IDR access units. It holds the working memory one picture needs and reuses it for the next, so it is not safe for concurrent use.

func NewEncoder added in v0.2.0

func NewEncoder(opts EncoderOptions) (*Encoder, error)

func (*Encoder) Encode added in v0.2.0

func (e *Encoder) Encode(frame Frame) ([]NALUnit, error)

Encode codes one frame as a complete access unit: a video, a sequence and a picture parameter set followed by the slice. The NAL units it returns own their bitstream and outlive the next call.

func (*Encoder) Flush added in v0.2.0

func (e *Encoder) Flush() ([]NALUnit, error)

Flush ends the sequence. Every frame is coded on its own, so there is never anything held back.

func (*Encoder) Threads added in v0.2.0

func (e *Encoder) Threads(n int)

Threads bounds the goroutines coding one picture's rows. More than one turns on the synchronisation of 9.3.1, which costs about 2% of bitrate; zero and one code serially and leave it out of the stream.

type EncoderOptions added in v0.2.0

type EncoderOptions struct {
	Width, Height int
	QP            int
	Lossless      bool
	// Chroma is the sampling to code in. The zero value is 4:2:0.
	Chroma ChromaFormat
	// BitDepth is the sample size, 8 through 12. The zero value is 8. Above
	// eight the samples come in the sixteen bit planes of [Frame].
	BitDepth int
	// SAO fits the offsets of 8.7.3 to the error left in each coding tree
	// block. It codes the picture twice, for about 2.2x the time, 3.5% of luma
	// bitrate and half a decibel of chroma.
	SAO bool
}

EncoderOptions configures an Encoder. Width and Height must be non-zero, a multiple of what Chroma resolves, and no more than MaxLumaSamples between them; anything the coding tree cannot fill is padded away behind a conformance window. QP runs from 1 through 51 and selects 26 when left at zero; Lossless codes the samples as PCM instead and ignores QP.

type Frame added in v0.2.0

type Frame struct {
	Y, Cb, Cr        []uint8
	Y16, Cb16, Cr16  []uint16
	StrideY, StrideC int
}

Frame is one picture in the EncoderOptions.Chroma sampling. StrideY and StrideC are in samples and may exceed the width, so a frame can be a window on a larger buffer. A monochrome frame leaves the chroma planes nil. Above eight bits the samples come in Y16, Cb16 and Cr16 instead.

type NALType

type NALType uint8

NALType is the nal_unit_type field of a NAL unit header.

const (
	NALTrailN NALType = iota
	NALTrailR
	NALTsaN
	NALTsaR
	NALStsaN
	NALStsaR
	NALRadlN
	NALRadlR
	NALRaslN
	NALRaslR
)
const (
	NALBlaWLP NALType = iota + 16
	NALBlaWRadl
	NALBlaNLP
	NALIdrWRadl
	NALIdrNLP
	NALCra
)
const (
	NALVPS NALType = iota + 32
	NALSPS
	NALPPS
	NALAUD
	NALEOS
	NALEOB
	NALFD
	NALPrefixSEI
	NALSuffixSEI
)

func (NALType) IsIDR

func (t NALType) IsIDR() bool

IsIDR reports whether the unit starts an instantaneous decoding refresh picture.

func (NALType) IsIRAP

func (t NALType) IsIRAP() bool

IsIRAP reports whether the unit starts an intra random access point picture.

func (NALType) IsVCL

func (t NALType) IsVCL() bool

IsVCL reports whether the unit carries a coded slice segment.

type NALUnit

type NALUnit struct {
	Type       NALType
	LayerID    uint8
	TemporalID uint8
	RBSP       []byte

	EPB []uint32
}

NALUnit is one parsed NAL unit.

func ParseNAL

func ParseNAL(data []byte) (NALUnit, bool)

ParseNAL parses a single NAL unit with a two-byte header and no framing.

func SplitAnnexB

func SplitAnnexB(data []byte) []NALUnit

SplitAnnexB splits a start-code delimited byte stream into NAL units.

func SplitHVCC

func SplitHVCC(data []byte, lengthSize int) []NALUnit

SplitHVCC splits length-prefixed NAL units, as framed inside hvcC.

func (NALUnit) NALOffset

func (n NALUnit) NALOffset(off int) int

NALOffset is the inverse of RBSPOffset.

func (NALUnit) RBSPOffset

func (n NALUnit) RBSPOffset(off int) int

RBSPOffset converts a payload-relative byte position, as entry point offsets count them, into an index into RBSP.

type Picture

type Picture struct {
	Width, Height int

	// CropW and CropH are the dimensions after the conformance window, which
	// is what a caller should display. Width and Height stay as decoded,
	// since prediction reads the whole plane.
	CropX, CropY int
	CropW, CropH int

	ChromaFormat int

	// BitDepth and BitDepthC are the luma and chroma sample depths, which
	// 7.4.3.2 lets differ. Both planes are stored 16-bit if either exceeds 8.
	BitDepth  int
	BitDepthC int

	// ColorPrimaries, ColorTransfer, ColorMatrix and FullRange are what the
	// sequence declares in its video usability information, as the code points
	// of ISO/IEC 23091-2. All three are 2, unspecified, when it declares none.
	ColorPrimaries uint16
	ColorTransfer  uint16
	ColorMatrix    uint16
	FullRange      bool

	POC int

	Y, Cb, Cr       []uint8
	Y16, Cb16, Cr16 []uint16

	StrideY, StrideC int
	WidthC, HeightC  int

	Col  []colMotion
	ColW int
	// contains filtered or unexported fields
}

Picture is one decoded picture. Planes hold either 8-bit or 16-bit samples depending on the bit depth, with Stride in samples.

func (*Picture) Release

func (p *Picture) Release()

Release hands the picture's memory back to the decoder that produced it, to be reused by a later picture. It is optional: one that is never released is collected as any other value would be. Reading the planes afterwards is a mistake; releasing twice is not, and does nothing.

Jump to

Keyboard shortcuts

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