codec

package
v0.0.0-...-3c84d77 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package codec provides shared video and audio codec infrastructure for Switchframe's server-side pipeline.

It includes AVC1/Annex B NALU conversion, ADTS header construction and parsing, and unified encoder/decoder factories that auto-detect the best available backend at startup (NVENC, VA-API, VideoToolbox, libx264, or OpenH264 fallback).

Key functions:

Build tags control codec availability:

  • cgo && !noffmpeg: FFmpeg libavcodec (primary backend)
  • cgo && openh264: OpenH264 fallback encoder/decoder
  • Non-cgo builds: stub implementations that return errors

Index

Examples

Constants

View Source
const (
	MinBitrate = 1_000_000
	MaxBitrate = 50_000_000
)

Bitrate limits for encoder configuration.

View Source
const (
	HEVCNALTrailN     = 0
	HEVCNALTrailR     = 1
	HEVCNALBlaWLP     = 16
	HEVCNALBlaWRadl   = 17
	HEVCNALBlaNLP     = 18
	HEVCNALIDRWRadl   = 19
	HEVCNALIDRNLP     = 20
	HEVCNALCraNut     = 21
	HEVCNALVPS        = 32
	HEVCNALSPS        = 33
	HEVCNALPPS        = 34
	HEVCNALAUD        = 35
	HEVCNALFillerData = 38
	HEVCNALSEIPrefix  = 39
	HEVCNALSEISuffix  = 40
)

HEVC NAL unit type constants as defined in ITU-T H.265 Table 7-1.

Variables

View Source
var FFmpegOpenMu sync.Mutex

FFmpegOpenMu serializes all avcodec_open2 and avformat_open_input calls. NVENC initializes the CUDA runtime inside avcodec_open2. When the SRT decoder calls avformat_open_input (which probes codecs) concurrently with an NVENC encoder open, the CUDA driver crashes with SIGSEGV. This mutex ensures only one FFmpeg codec/format initialization runs at a time. Once open, encode/decode operations are thread-safe per-context.

View Source
var SwitchframeUUID = [16]byte{
	0x8a, 0x4f, 0x93, 0x2b, 0x6d, 0x1e, 0x4c, 0x7a,
	0x9f, 0x3d, 0x5c, 0x8b, 0x2e, 0x7a, 0x1d, 0x4f,
}

SwitchframeUUID is the unique identifier for Switchframe encoder metadata in SEI user_data_unregistered messages (payload type 5). This is a v4 UUID hardcoded so every Switchframe installation uses the same value, making Switchframe-encoded streams identifiable by downstream tools.

UUID: 8a4f932b-6d1e-4c7a-9f3d-5c8b2e7a1d4f

Functions

func ADTSFrameLen

func ADTSFrameLen(data []byte) int

ADTSFrameLen extracts the total frame length (including header) from an ADTS header. Returns 0 if the data is not ADTS or too short.

func ADTSHeaderLen

func ADTSHeaderLen(data []byte) int

ADTSHeaderLen returns the ADTS header length for data that starts with an ADTS sync word: 7 bytes when protection_absent=1 (no CRC), 9 bytes when protection_absent=0 (CRC present). Returns 0 if the data is not ADTS or too short.

func AVC1ToAnnexB

func AVC1ToAnnexB(avc1 []byte) []byte

AVC1ToAnnexB converts AVC1 format (4-byte length-prefixed NALUs) to Annex B format (start-code-prefixed NALUs). Returns nil for nil or empty input.

Example
package main

import (
	"fmt"

	"github.com/zsiec/switchframe/server/codec"
)

func main() {
	// AVC1: 4-byte big-endian length prefix + NALU data.
	// This encodes a single 3-byte NALU (length=3).
	avc1 := []byte{0x00, 0x00, 0x00, 0x03, 0x65, 0x01, 0x02}

	annexB := codec.AVC1ToAnnexB(avc1)

	// Annex B: 4-byte start code (00 00 00 01) replaces the length prefix.
	fmt.Printf("%x\n", annexB)
}
Output:
00000001650102

func AVC1ToAnnexBInto

func AVC1ToAnnexBInto(avc1 []byte, dst []byte) []byte

AVC1ToAnnexBInto converts AVC1 format to Annex B format, writing into dst. If dst has insufficient capacity, a new buffer is allocated. Returns the result slice (possibly resliced dst). Pass dst[:0] to reuse a buffer.

func AnnexBToAVC1

func AnnexBToAVC1(annexB []byte) []byte

AnnexBToAVC1 converts Annex B format (start-code-prefixed NALUs) to AVC1 format (4-byte length-prefixed NALUs). Handles both 3-byte (0x000001) and 4-byte (0x00000001) start codes. Returns nil for nil or empty input.

func AnnexBToAVC1Into

func AnnexBToAVC1Into(annexB []byte, dst []byte) []byte

AnnexBToAVC1Into converts Annex B format to AVC1 format, appending the result to dst. Returns the (possibly grown) dst slice. Pass dst[:0] to reuse a buffer without allocating. Returns nil for nil or empty input.

func AnnexBToHVC1

func AnnexBToHVC1(annexB []byte) []byte

AnnexBToHVC1 converts Annex B format (start-code-prefixed NALUs) to HVC1 format (4-byte length-prefixed NALUs). The wire format is identical to AVC1, so this delegates to the existing implementation. Returns nil for nil or empty input.

func AnnexBToHVC1Into

func AnnexBToHVC1Into(annexB []byte, dst []byte) []byte

AnnexBToHVC1Into converts Annex B format to HVC1 format, appending the result to dst. Returns the (possibly grown) dst slice. Pass dst[:0] to reuse a buffer without allocating.

func AnnexBToWire

func AnnexBToWire(annexB []byte) []byte

AnnexBToWire converts Annex B (start-code-prefixed NALUs) to wire format (4-byte length-prefixed NALUs). Works for both AVC and HEVC.

func AnnexBToWireInto

func AnnexBToWireInto(annexB []byte, dst []byte) []byte

AnnexBToWireInto is the in-place version of AnnexBToWire.

func BuildADTS

func BuildADTS(sampleRate, channels, frameLen int) []byte

BuildADTS constructs a 7-byte ADTS header for an AAC-LC frame.

The header assumes MPEG-4 (ID=0), AAC-LC (audioObjectType=2, ADTS profile field=objectType-1=1), no CRC (protection absent=1).

Frame length in the header includes the 7-byte header itself.

ADTS header layout (7 bytes, no CRC):

Byte 0:    sync word high 8 bits (0xFF)
Byte 1:    sync word low 4 bits (0xF) | ID (0=MPEG-4) | layer (00) | protection absent (1)
Byte 2:    profile (2 bits) | sample rate index (4 bits) | private (1 bit) | channel config high (1 bit)
Byte 3:    channel config low (2 bits) | originality (1) | home (1) | copyright ID (1) | copyright start (1) | frame length high (2 bits)
Byte 4:    frame length mid (8 bits)
Byte 5:    frame length low (3 bits) | buffer fullness high (5 bits)
Byte 6:    buffer fullness low (6 bits) | number of AAC frames - 1 (2 bits)
Example
package main

import (
	"fmt"

	"github.com/zsiec/switchframe/server/codec"
)

func main() {
	// Build a 7-byte ADTS header for a 10-byte AAC-LC frame at 48kHz stereo.
	header := codec.BuildADTS(48000, 2, 10)

	fmt.Println("length:", len(header))
	// Verify sync word (first 12 bits = 0xFFF).
	fmt.Printf("sync: %02x %02x\n", header[0], header[1]&0xF0)
	fmt.Println("is_adts:", codec.IsADTS(header))
}
Output:
length: 7
sync: ff f0
is_adts: true

func BuildEncoderIdentSEI

func BuildEncoderIdentSEI(codec string, info EncoderIdentInfo) []byte

BuildEncoderIdentSEI creates an SEI NALU containing Switchframe encoder identification metadata as a user_data_unregistered message (payload type 5). Injected on keyframes so downstream tools can identify the encoding source.

The codec parameter selects the NALU framing:

  • "h264" or "": H.264 SEI (1-byte header 0x06)
  • "h265": HEVC PREFIX_SEI (2-byte header 0x4E 0x01)

The NALU structure is:

[start_code 00 00 00 01] [nalu_header] [payload_type 05] [payload_size]
[uuid 16 bytes] [user_data UTF-8 string] [rbsp_trailing 80]

The user data string format:

Switchframe/1.0 encoder=<name> profile=<profile> format=<height>p<fps>

func CreateHWDeviceCtx

func CreateHWDeviceCtx(typeName string) unsafe.Pointer

CreateHWDeviceCtx attempts to create a hardware device context for the given type (e.g. "videotoolbox", "cuda", "vaapi"). Returns the context pointer on success, nil on failure. The caller must not free the returned pointer — it is managed by AVBufferRef reference counting.

func EnsureADTS

func EnsureADTS(data []byte, sampleRate, channels int) []byte

EnsureADTS returns data with an ADTS header prepended. If data already starts with an ADTS sync word, it is returned unchanged.

func ExtractHEVCParamSets

func ExtractHEVCParamSets(annexB []byte) (vps, sps, pps []byte)

ExtractHEVCParamSets scans Annex B data for VPS (type 32), SPS (type 33), and PPS (type 34) NALUs. Returns copies of the first VPS, SPS, and PPS found, or nil for any that are not present. The returned slices are independent copies (safe to store).

func ExtractNALUs

func ExtractNALUs(avc1 []byte) [][]byte

ExtractNALUs extracts individual NALUs from AVC1 format data, returning each NALU body without the 4-byte length prefix. The returned slices are sub-slices of the input — callers that need to own the data (e.g. SPS/PPS storage) must copy it themselves. Returns nil for nil or empty input.

func HEVCNALUType

func HEVCNALUType(firstByte byte) byte

HEVCNALUType extracts the NAL unit type from the first byte of an HEVC NALU. HEVC NAL header is 2 bytes: forbidden(1) | type(6) | layerID(6) | tid(3). The type field occupies bits 1-6 of the first byte.

func HVC1ToAnnexB

func HVC1ToAnnexB(hvc1 []byte) []byte

HVC1ToAnnexB converts HVC1 format (4-byte length-prefixed NALUs) to Annex B format (start-code-prefixed NALUs). The wire format is identical to AVC1, so this delegates to the existing implementation. Returns nil for nil or empty input.

func HVC1ToAnnexBInto

func HVC1ToAnnexBInto(hvc1 []byte, dst []byte) []byte

HVC1ToAnnexBInto converts HVC1 format to Annex B format, writing into dst. If dst has insufficient capacity, a new buffer is allocated. Returns the result slice (possibly resliced dst). Pass dst[:0] to reuse a buffer.

func HWDeviceCtx

func HWDeviceCtx() unsafe.Pointer

HWDeviceCtx returns the cached hardware device context pointer. Returns nil for software codecs (libx264, openh264). The returned pointer is an *AVBufferRef suitable for passing to FFmpeg encoder/decoder creation functions.

ProbeEncoders() must be called before this function returns a meaningful value.

func IsADTS

func IsADTS(data []byte) bool

IsADTS reports whether data begins with an ADTS sync word (0xFFF in the top 12 bits of the first two bytes). Returns false for nil or data shorter than 2 bytes.

Example
package main

import (
	"fmt"

	"github.com/zsiec/switchframe/server/codec"
)

func main() {
	adtsFrame := []byte{0xFF, 0xF1, 0x50, 0x80, 0x02, 0x00, 0xFC}
	rawAAC := []byte{0x01, 0x02, 0x03}

	fmt.Println(codec.IsADTS(adtsFrame))
	fmt.Println(codec.IsADTS(rawAAC))
}
Output:
true
false

func IsHEVCKeyframe

func IsHEVCKeyframe(nalType byte) bool

IsHEVCKeyframe returns true if the NAL type represents an HEVC random access point (BLA types 16-18, IDR types 19-20, or CRA type 21).

func IsHEVCVCL

func IsHEVCVCL(nalType byte) bool

IsHEVCVCL returns true if the NAL type is a Video Coding Layer unit (types 0-31).

func NewHEVCVideoDecoder

func NewHEVCVideoDecoder() (transition.VideoDecoder, error)

NewHEVCVideoDecoder creates an HEVC video decoder using the FFmpeg software decoder. Returns an error if FFmpeg HEVC decoding is not available.

func NewHEVCVideoDecoderSingleThread

func NewHEVCVideoDecoderSingleThread() (transition.VideoDecoder, error)

NewHEVCVideoDecoderSingleThread creates an HEVC video decoder with single-threaded decoding. This eliminates frame-level multithreading buffering delay, ensuring each Decode() call produces output immediately. Use for clip/replay decoders.

func NewHEVCVideoEncoder

func NewHEVCVideoEncoder(width, height, bitrate, fpsNum, fpsDen int, opts ...*EncoderOptions) (transition.VideoEncoder, error)

NewHEVCVideoEncoder creates an HEVC video encoder using the best available backend. The first call triggers HEVC codec probing (via ProbeHEVCEncoders) which tests available hardware and software encoders in priority order.

func NewHEVCVideoEncoder10bit

func NewHEVCVideoEncoder10bit(width, height, bitrate, fpsNum, fpsDen int, opts ...*EncoderOptions) (transition.VideoEncoder, error)

NewHEVCVideoEncoder10bit creates an HEVC encoder configured for 10-bit YUV422P10LE input. Probes for available HEVC encoders and selects the best one. For libx265: uses main422-10 profile. For NVENC: uses main10 profile.

func NewPreviewEncoder

func NewPreviewEncoder(width, height, bitrate, fpsNum, fpsDen int, preset ...string) (transition.VideoEncoder, error)

NewPreviewEncoder creates a preview encoder for browser multiview. When a hardware encoder (NVENC) is available, it uses that — GPU encode is essentially free and avoids consuming CPU cores. Falls back to libx264 with baseline profile when no hardware encoder is detected. Optional preset parameter (default "ultrafast") is only used for the software fallback path.

fpsNum/fpsDen express the frame rate as a rational number (e.g. 30/1 for 30fps).

func NewVideoDecoder

func NewVideoDecoder() (transition.VideoDecoder, error)

NewVideoDecoder creates a video decoder using the best available backend. The first call triggers codec probing (via ProbeEncoders) which tests available encoders and selects a decoder strategy.

When FFmpeg is available, the FFmpeg software H.264 decoder is used (universally available, supports all profiles). Falls back to OpenH264 if FFmpeg probing indicates it's unavailable.

func NewVideoDecoderForCodec

func NewVideoDecoderForCodec(codec string) (transition.VideoDecoder, error)

NewVideoDecoderForCodec creates a video decoder for the specified codec. Accepts "h264" or "h265"/"hevc". Returns an error for unknown codecs.

func NewVideoDecoderForCodecNative10bit

func NewVideoDecoderForCodecNative10bit(codec string) (transition.VideoDecoder, error)

NewVideoDecoderForCodecNative10bit creates a native 10-bit decoder for the specified codec. For HEVC sources, preserves 10-bit 422 output. For H.264 sources (which don't support 10-bit 422), falls back to standard decoding.

func NewVideoDecoderForCodecNative10bitSingleThread

func NewVideoDecoderForCodecNative10bitSingleThread(codec string) (transition.VideoDecoder, error)

NewVideoDecoderForCodecNative10bitSingleThread creates a single-threaded native 10-bit decoder for the specified codec.

func NewVideoDecoderForCodecSingleThread

func NewVideoDecoderForCodecSingleThread(codec string) (transition.VideoDecoder, error)

NewVideoDecoderForCodecSingleThread creates a single-threaded video decoder for the specified codec. Accepts "h264" or "h265"/"hevc".

func NewVideoDecoderNative10bit

func NewVideoDecoderNative10bit() (transition.VideoDecoder, error)

NewVideoDecoderNative10bit creates a video decoder that preserves 10-bit 422 output when the source provides 10-bit content. For 10-bit HEVC sources, the decoder outputs YUV422P10LE (w*h*4 bytes) instead of downconverting to 8-bit 420, eliminating the lossy round-trip when the pipeline is in professional mode. For 8-bit sources, falls back to standard YUV420P 8-bit output.

func NewVideoDecoderNative10bitSingleThread

func NewVideoDecoderNative10bitSingleThread() (transition.VideoDecoder, error)

NewVideoDecoderNative10bitSingleThread creates a native 10-bit decoder with single-threaded decoding. Eliminates frame-level multithreading buffering delay. Use for clip/replay decoders where immediate per-frame output is needed.

func NewVideoDecoderSingleThread

func NewVideoDecoderSingleThread() (transition.VideoDecoder, error)

NewVideoDecoderSingleThread creates a video decoder with single-threaded decoding. This eliminates frame-level multithreading buffering delay, ensuring each Decode() call produces output immediately (only B-frame reordering delay remains). Use for clip/replay decoders where immediate per-frame output is needed.

func NewVideoEncoder

func NewVideoEncoder(width, height, bitrate, fpsNum, fpsDen int, opts ...*EncoderOptions) (transition.VideoEncoder, error)

NewVideoEncoder creates a video encoder using the best available backend. The first call triggers codec probing (via ProbeEncoders) which tests available hardware and software encoders in priority order.

fpsNum/fpsDen express the frame rate as a rational number (e.g. 30000/1001 for 29.97fps). The encoder always uses constrained VBR (cVBR): ABR with a tight 1.2x VBV ceiling for predictable SRT output. Transport-level CBR padding is handled by the output layer's CBR pacer.

func ParseADTSInfo

func ParseADTSInfo(data []byte) (sampleRate, channels int)

ParseADTSInfo extracts the sample rate and channel count from an ADTS header. Returns (0, 0) if data is too short or not ADTS.

func ParseSPSCodecString

func ParseSPSCodecString(sps []byte) string

ParseSPSCodecString returns a WebCodecs-compatible codec string from SPS NALU bytes. The SPS NALU format is: [nalu_type_byte] [profile_idc] [constraint_flags] [level_idc] ... Example: "avc1.640028" for High profile Level 4.0.

func PrependSPSPPS

func PrependSPSPPS(sps, pps, annexBData []byte) []byte

PrependSPSPPS prepends SPS and PPS NALUs with Annex B start codes to the given Annex B data. Safely handles nil/empty SPS or PPS.

func PrependSPSPPSInto

func PrependSPSPPSInto(sps, pps, annexBData []byte, dst []byte) []byte

PrependSPSPPSInto prepends SPS and PPS NALUs to annexBData, writing into dst. Returns the result slice. Pass dst[:0] to reuse a buffer.

func PrependVPSSPSPPS

func PrependVPSSPSPPS(vps, sps, pps, annexBData []byte) []byte

PrependVPSSPSPPS prepends VPS, SPS, and PPS NALUs with Annex B start codes to the given Annex B data. Safely handles nil/empty individual parameters.

func PrependVPSSPSPPSInto

func PrependVPSSPSPPSInto(vps, sps, pps, annexBData []byte, dst []byte) []byte

PrependVPSSPSPPSInto prepends VPS, SPS, and PPS NALUs to annexBData, writing into dst. Returns the result slice. Pass dst[:0] to reuse a buffer.

func ProbeEncoders

func ProbeEncoders() (string, string)

ProbeEncoders tests available H.264 encoder backends and caches the result. It tries hardware-accelerated encoders first, falling back to libx264, then OpenH264 as a last resort.

Returns (encoderName, decoderName). Both will be non-empty on any machine with at least FFmpeg or OpenH264 available.

Safe to call from multiple goroutines; probing runs exactly once.

func ProbeHEVCEncoders

func ProbeHEVCEncoders() (string, string)

ProbeHEVCEncoders tests available HEVC encoder backends and caches the result. It tries hardware-accelerated encoders first, falling back to libx265.

Returns (encoderName, decoderName). decoderName is "hevc" when FFmpeg's HEVC decoder is available. encoderName may be "none" if no HEVC encoder works.

Safe to call from multiple goroutines; probing runs exactly once.

func SplitADTSFrames

func SplitADTSFrames(data []byte) [][]byte

SplitADTSFrames splits concatenated ADTS frames into individual raw AAC payloads (headers stripped). If the data is not ADTS, it is returned as a single raw payload.

func WireToAnnexB

func WireToAnnexB(data []byte) []byte

WireToAnnexB converts wire format (4-byte length-prefixed NALUs) to Annex B (start-code-prefixed NALUs). Works for both AVC and HEVC since the wire format is identical.

func WireToAnnexBInto

func WireToAnnexBInto(data []byte, dst []byte) []byte

WireToAnnexBInto is the in-place version of WireToAnnexB.

Types

type EncoderIdentInfo

type EncoderIdentInfo struct {
	EncoderName string // e.g., "hevc_nvenc", "libx265", "libx264"
	Profile     string // e.g., "main422-10", "high", "main"
	Width       int
	Height      int
	FPSNum      int
	FPSDen      int
	BitDepth    int    // 8 or 10
	Chroma      string // "420" or "422"
}

EncoderIdentInfo contains encoder parameters for the identification SEI.

type EncoderInfo

type EncoderInfo struct {
	Name        string `json:"name"`
	DisplayName string `json:"displayName"`
	IsDefault   bool   `json:"isDefault"`
}

EncoderInfo describes an available video encoder backend.

func ListAvailableEncoders

func ListAvailableEncoders() []EncoderInfo

ListAvailableEncoders returns all functional H.264 encoder backends on this system. Each entry includes the encoder name, a human-readable display name, and whether it is the current default (as selected by ProbeEncoders).

The list is computed once and cached; safe to call from multiple goroutines. Reuses probe results from ProbeEncoders() to avoid re-creating encoder instances a second time.

func ListAvailableHEVCEncoders

func ListAvailableHEVCEncoders() []EncoderInfo

ListAvailableHEVCEncoders returns all functional HEVC encoder backends on this system. Each entry includes the encoder name, a human-readable display name, and whether it is the current default (as selected by ProbeHEVCEncoders).

The list is computed once and cached; safe to call from multiple goroutines.

type EncoderOptions

type EncoderOptions struct {
	Bitrate  int          // 0 means auto
	GOPSecs  int          // 0 means use default
	Preset   OutputPreset // "" means use default
	Timecode bool
}

EncoderOptions holds user-configurable encoder parameters.

func (EncoderOptions) BitrateWarning

func (o EncoderOptions) BitrateWarning(width, height, fpsNum, fpsDen int) string

BitrateWarning returns a non-empty warning string if the configured bitrate is below a format-appropriate floor. Bitrate 0 (auto) never warns.

func (EncoderOptions) Validate

func (o EncoderOptions) Validate() error

Validate checks that all fields are within acceptable ranges. Zero values are valid and mean "use default".

func (EncoderOptions) WithDefaults

func (o EncoderOptions) WithDefaults() EncoderOptions

WithDefaults returns a copy with zero-value fields replaced by sensible defaults. Bitrate 0 is left as-is (meaning auto).

type FFmpegDecoder

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

FFmpegDecoder wraps an FFmpeg libavcodec decoder and implements transition.VideoDecoder. It decodes Annex B H.264 or H.265/HEVC bitstream to packed YUV420 planar.

When output10bit422 is true and the source provides 10-bit content, the decoder outputs YUV422P10LE (w*h*4 bytes) instead of downconverting to YUV420P 8-bit. This preserves quality when the pipeline is in professional (10-bit 422) mode.

FFmpegDecoder is NOT safe for concurrent use. Callers must synchronize access externally.

func NewFFmpegDecoder

func NewFFmpegDecoder(hwDeviceCtx unsafe.Pointer) (*FFmpegDecoder, error)

NewFFmpegDecoder creates a new FFmpeg H.264 decoder with auto thread count. hwDeviceCtx is reserved for future hardware acceleration (pass nil for software).

func NewFFmpegDecoderNative10bit

func NewFFmpegDecoderNative10bit(hwDeviceCtx unsafe.Pointer) (*FFmpegDecoder, error)

NewFFmpegDecoderNative10bit creates an H.264 decoder that preserves 10-bit 422 output. When the decoded frame is YUV422P (8-bit) or YUV420P10/YUV422P10, the decoder outputs YUV422P10LE (w*h*4 bytes) instead of downconverting.

func NewFFmpegDecoderNative10bitWithThreads

func NewFFmpegDecoderNative10bitWithThreads(hwDeviceCtx unsafe.Pointer, threadCount int) (*FFmpegDecoder, error)

NewFFmpegDecoderNative10bitWithThreads creates an H.264 decoder with explicit thread count and native 10-bit output support.

func NewFFmpegDecoderWithThreads

func NewFFmpegDecoderWithThreads(hwDeviceCtx unsafe.Pointer, threadCount int) (*FFmpegDecoder, error)

NewFFmpegDecoderWithThreads creates an FFmpeg H.264 decoder with explicit thread count. threadCount=0 means auto (ncpu, 2-8). threadCount=1 disables frame-level multithreading, which eliminates buffering delay (important for clip decoders that need immediate output).

func NewFFmpegHEVCDecoder

func NewFFmpegHEVCDecoder(hwDeviceCtx unsafe.Pointer) (*FFmpegDecoder, error)

NewFFmpegHEVCDecoder creates a new FFmpeg HEVC decoder with auto thread count. hwDeviceCtx is reserved for future hardware acceleration (pass nil for software).

func NewFFmpegHEVCDecoderNative10bit

func NewFFmpegHEVCDecoderNative10bit(hwDeviceCtx unsafe.Pointer) (*FFmpegDecoder, error)

NewFFmpegHEVCDecoderNative10bit creates an HEVC decoder that preserves 10-bit 422 output. When the decoded frame is YUV422P10LE or YUV420P10LE, the decoder outputs YUV422P10LE (w*h*4 bytes) instead of downconverting to 8-bit 420. For 8-bit sources, falls back to standard YUV420P 8-bit output (w*h*3/2).

func NewFFmpegHEVCDecoderNative10bitWithThreads

func NewFFmpegHEVCDecoderNative10bitWithThreads(hwDeviceCtx unsafe.Pointer, threadCount int) (*FFmpegDecoder, error)

NewFFmpegHEVCDecoderNative10bitWithThreads creates an HEVC decoder with explicit thread count and native 10-bit output support.

func NewFFmpegHEVCDecoderWithThreads

func NewFFmpegHEVCDecoderWithThreads(hwDeviceCtx unsafe.Pointer, threadCount int) (*FFmpegDecoder, error)

NewFFmpegHEVCDecoderWithThreads creates an FFmpeg HEVC decoder with explicit thread count. threadCount=0 means auto (ncpu, 2-8). threadCount=1 disables frame-level multithreading.

func (*FFmpegDecoder) Close

func (d *FFmpegDecoder) Close()

Close releases the decoder resources. Safe to call multiple times.

func (*FFmpegDecoder) Decode

func (d *FFmpegDecoder) Decode(data []byte) ([]byte, int, int, error)

Decode decodes Annex B encoded H.264 data into packed YUV420 planar bytes. Returns the YUV buffer (Y: w*h, U: w/2*h/2, V: w/2*h/2), width, height, and any error.

The returned byte slice is an independent copy that is safe to retain across subsequent Decode or ReceiveFrame calls.

func (*FFmpegDecoder) DecodeInto

func (d *FFmpegDecoder) DecodeInto(data []byte, dst []byte) ([]byte, int, int, error)

DecodeInto decodes H.264 data, writing the result into dst if it fits. If dst is large enough for the decoded YUV420, the frame is copied directly into dst (eliminating the intermediate allocation). If dst is nil or too small, falls back to standard allocation behavior. Returns the YUV buffer (which may or may not be dst), width, height, error.

func (*FFmpegDecoder) Flush

func (d *FFmpegDecoder) Flush()

Flush resets the decoder's internal state (reference frames, reorder buffer) without destroying it. Use when the input source changes to prevent stale reference frame warnings. The decoder can immediately accept new input.

func (*FFmpegDecoder) ReceiveFrame

func (d *FFmpegDecoder) ReceiveFrame() ([]byte, int, int, error)

ReceiveFrame receives a decoded frame without sending new input. Returns the YUV buffer, width, height, and any error. Returns an error when no more frames are available (EAGAIN/EOF).

The returned byte slice is an independent copy that is safe to retain across subsequent Decode or ReceiveFrame calls.

func (*FFmpegDecoder) SendEOS

func (d *FFmpegDecoder) SendEOS() error

SendEOS signals end-of-stream to the decoder so buffered frames (from B-frame reordering) can be drained via ReceiveFrame().

type FFmpegEncoder

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

FFmpegEncoder wraps an FFmpeg libavcodec encoder and implements transition.VideoEncoder. It encodes packed YUV420 planar frames to Annex B H.264 bitstream.

FFmpegEncoder is NOT safe for concurrent use. Callers must synchronize access externally.

func NewFFmpegEncoder

func NewFFmpegEncoder(codecName string, width, height, bitrate, fpsNum, fpsDen, gopSecs int, hwDeviceCtx unsafe.Pointer, opts ...*EncoderOptions) (*FFmpegEncoder, error)

NewFFmpegEncoder creates a new FFmpeg encoder using the named codec.

codecName is the FFmpeg encoder name (e.g. "libx264", "h264_videotoolbox"). width, height, bitrate, fpsNum, and fpsDen configure the output stream. fpsNum/fpsDen express the frame rate as a rational number (e.g. 30000/1001 for 29.97fps). gopSecs sets the IDR keyframe interval in seconds. hwDeviceCtx is reserved for future hardware acceleration (pass nil for software).

The encoder always uses constrained VBR (cVBR): ABR with a tight 1.2x VBV ceiling. This produces predictable bitrate for SRT transport while preserving per-frame quality flexibility. Transport-level CBR padding is handled by the CBR pacer in the output layer, not by the encoder.

func NewFFmpegEncoder10bit

func NewFFmpegEncoder10bit(codecName string, width, height, bitrate, fpsNum, fpsDen, gopSecs int, hwDeviceCtx unsafe.Pointer, opts ...*EncoderOptions) (*FFmpegEncoder, error)

NewFFmpegEncoder10bit creates a 10-bit YUV422P10LE encoder. Uses the same codec selection as NewFFmpegEncoder but configures the encoder for 10-bit 4:2:2 input (professional pipeline profile). For libx265: profile=main422-10. For NVENC: profile=main10.

func NewFFmpegPreviewEncoder

func NewFFmpegPreviewEncoder(width, height, bitrate, fpsNum, fpsDen, gopSecs int, preset ...string) (*FFmpegEncoder, error)

NewFFmpegPreviewEncoder creates a lightweight preview encoder using libx264 with the given preset (e.g. "ultrafast", "veryfast") and baseline profile. It always uses software encoding — hardware encoders are reserved for the program output path.

width, height, bitrate, fpsNum, and fpsDen configure the output stream. gopSecs sets the IDR keyframe interval in seconds.

func (*FFmpegEncoder) Close

func (e *FFmpegEncoder) Close()

Close releases the encoder resources. Safe to call multiple times.

func (*FFmpegEncoder) Encode

func (e *FFmpegEncoder) Encode(yuv []byte, pts int64, forceIDR bool) ([]byte, bool, error)

Encode encodes a packed YUV420 planar frame to Annex B H.264 data. pts is the presentation timestamp in 90 kHz MPEG-TS units, passed through to the encoded bitstream for A/V sync. If forceIDR is true, the encoder forces an IDR keyframe. Returns the encoded bitstream, whether the frame is a keyframe, and any error.

func (*FFmpegEncoder) Extradata

func (e *FFmpegEncoder) Extradata() []byte

Extradata returns the encoder's extradata (SPS/PPS) when AV_CODEC_FLAG_GLOBAL_HEADER is set. Returns nil if no extradata is available.

type FFmpegHWFramesEncoder

type FFmpegHWFramesEncoder struct{}

FFmpegHWFramesEncoder is a stub for non-CUDA builds with FFmpeg. CUDA hw_frames_ctx is only available on NVIDIA GPUs.

func NewFFmpegHWFramesEncoder

func NewFFmpegHWFramesEncoder(cudaCtx unsafe.Pointer, width, height, bitrate, fpsNum, fpsDen, gopSecs int, preview ...bool) (*FFmpegHWFramesEncoder, error)

NewFFmpegHWFramesEncoder returns an error on non-CUDA builds.

func NewFFmpegHWFramesEncoderHEVC

func NewFFmpegHWFramesEncoderHEVC(cudaCtx unsafe.Pointer, width, height, bitrate, fpsNum, fpsDen, gopSecs int,
	is10bit bool) (*FFmpegHWFramesEncoder, error)

NewFFmpegHWFramesEncoderHEVC returns an error on non-CUDA builds.

func (*FFmpegHWFramesEncoder) Close

func (e *FFmpegHWFramesEncoder) Close()

Close is a no-op stub.

func (*FFmpegHWFramesEncoder) EncodeNV12CUDA

func (e *FFmpegHWFramesEncoder) EncodeNV12CUDA(yDevPtr, uvDevPtr unsafe.Pointer, pitch int, pts int64, forceIDR bool, cudaStream unsafe.Pointer) ([]byte, bool, error)

EncodeNV12CUDA is a stub that always returns an error.

type FileProbeResult

type FileProbeResult struct {
	VideoCodecID int
	AudioCodecID int
	Width        int
	Height       int
	HasVideo     bool
	HasAudio     bool
}

FileProbeResult holds the result of probing a media file with avformat.

func ProbeFile

func ProbeFile(path string) (*FileProbeResult, error)

ProbeFile opens a media file with FFmpeg's avformat and extracts stream information including codec IDs and dimensions.

func (*FileProbeResult) IsH264

func (r *FileProbeResult) IsH264() bool

IsH264 returns true if the probed video codec is H.264.

func (*FileProbeResult) IsHEVC

func (r *FileProbeResult) IsHEVC() bool

IsHEVC returns true if the probed video codec is HEVC (H.265).

type OpenH264Decoder

type OpenH264Decoder struct{}

OpenH264Decoder is a stub for builds without the openh264 build tag.

func NewOpenH264Decoder

func NewOpenH264Decoder() (*OpenH264Decoder, error)

NewOpenH264Decoder returns an error when the openh264 build tag is not set.

func (*OpenH264Decoder) Close

func (d *OpenH264Decoder) Close()

Close is a no-op stub.

func (*OpenH264Decoder) Decode

func (d *OpenH264Decoder) Decode(data []byte) ([]byte, int, int, error)

Decode is a stub that always returns an error.

type OpenH264Encoder

type OpenH264Encoder struct{}

OpenH264Encoder is a stub for builds without the openh264 build tag.

func NewOpenH264Encoder

func NewOpenH264Encoder(width, height, bitrate, fpsNum, fpsDen int) (*OpenH264Encoder, error)

NewOpenH264Encoder returns an error when the openh264 build tag is not set.

func (*OpenH264Encoder) Close

func (e *OpenH264Encoder) Close()

Close is a no-op stub.

func (*OpenH264Encoder) Encode

func (e *OpenH264Encoder) Encode(yuv []byte, pts int64, forceIDR bool) ([]byte, bool, error)

Encode is a stub that always returns an error.

type OutputPreset

type OutputPreset string

OutputPreset controls the encoder speed/quality tradeoff.

const (
	OutputPresetLowLatency OutputPreset = "low-latency"
	OutputPresetBalanced   OutputPreset = "balanced"
	OutputPresetQuality    OutputPreset = "quality"
)

func (OutputPreset) NVENCPreset

func (p OutputPreset) NVENCPreset() string

NVENCPreset returns the NVENC preset string for this output preset.

func (OutputPreset) Valid

func (p OutputPreset) Valid() bool

Valid returns true for the three named presets or the empty string (meaning "use default").

func (OutputPreset) X264Preset

func (p OutputPreset) X264Preset() string

X264Preset returns the x264 preset string for this output preset.

func (OutputPreset) X265Preset

func (p OutputPreset) X265Preset() string

X265Preset returns the x265 preset string for this output preset.

type TranscodeResult

type TranscodeResult struct {
	Width       int
	Height      int
	DurationMs  int64
	FPS         float64
	SampleRate  int
	Channels    int
	VideoFrames int
	// AudioSamples is the number of audio samples (at the output sample rate)
	// accepted by the audio encoder.
	AudioSamples int64
}

TranscodeResult holds metadata from the transcode operation.

func TranscodeFile

func TranscodeFile(inputPath, outputPath, encoderName string, bitrate int) (*TranscodeResult, error)

TranscodeFile transcodes any media file supported by FFmpeg's avformat into H.264+AAC MPEG-TS. The encoderName should come from ProbeEncoders() (e.g., "libx264", "h264_videotoolbox"). Pass "" to auto-select H.264 encoder. Bitrate of 0 auto-selects based on resolution.

func TranscodeFileWithProgress

func TranscodeFileWithProgress(inputPath, outputPath, encoderName string, bitrate int, progressPct *int32) (*TranscodeResult, error)

TranscodeFileWithProgress is like TranscodeFile but accepts an optional progress pointer. When non-nil, the C transcode loop atomically writes 0-100 into *progressPct as packets are processed.

Jump to

Keyboard shortcuts

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