SkillAgentSearch skills...

Edge264

Simple H.264 decoder

Install / Use

npx skills add tvlabs/edge264

Installs into whichever agent you are using.

About this skill

Quality Score

0/100

Supported Platforms

Universal

README

edge264

edge264 is an H.264/AVC cross-platform open-source decoder, focused on speed and ease-of-use.

It grew up as a research effort on new software engineering practices, most notably the use of C vector extensions to replace hand-crafted assembly. As such it is slowly but steadily progressing towards production-readiness, with a target release and API-freeze in 2027.

Benchmark computed by a median of 5 runs of Big Buck Bunny test video.

Features

edge264 supports Progressive High and MVC 3D profiles, up to level 6.2

Below is an overview of optional features versus Baseline (BP), Extended (XP), Main (MP), High (HP) and Stereo High (SHP) profiles. 💡 marks planned improvements.

| Feature | BP | XP | MP | HP | SHP | edge264 | | --- | --- | --- | --- | --- | --- | --- | | Bit depth | 8 | 8 | 8 | 8 | 8 | 8 💡 | | Chroma formats | 4:2:0 | 4:2:0 | 4:2:0 | 4:0:0<br/>4:2:0 | 4:0:0<br/>4:2:0 | 4:2:0 💡 | | Flexible macroblock ordering | ✓ | ✓ | | | | | | Arbitrary slice ordering | ✓ | ✓ | | | | ✓ | | Redundant slices | ✓ | ✓ | | | | | | Data partitioning | | ✓ | | | | | | SI/SP slices | | ✓ | | | | | | Interlaced coding (PAFF, MBAFF) | | ✓ | ✓ | ✓ | ✓ | 💡 | | B slices | | ✓ | ✓ | ✓ | ✓ | ✓ | | CABAC entropy coding | | | ✓ | ✓ | ✓ | ✓ | | 8x8 IDCT transforms | | | | ✓ | ✓ | ✓ | | Custom quantization matrices | | | | ✓ | ✓ | ✓ | | Separate Cb/Cr QP control | | | | ✓ | ✓ | ✓ | | Separate color planes | | | | | | 💡 | | Lossless coding | | | | | | 💡 | | Max. number of views | 1 | 1 | 1 | 1 | 2 | 2 |

Platforms

Target system support currently includes macOS, Linux, Windows and WebAssembly.

Processor support depends on the compiler used (GNU GCC or LLVM Clang). edge264 can choose among 4 backends, the last one supporting every other little-endian CPU by relying on Clang vector extensions.

| Compiler | Intel x86/x64 | ARM32/64+NEON | WASM32/64 v2+ | Other ISAs | |-|-|-|-|-| | Clang | ✓ | ✓ | ✓ | ✓ (v15+) | | GCC | ✓ | ✓ | | |

Building

For native builds:

make

For WebAssembly builds:

emmake make # add CFLAGS=-mrelaxed-simd to target WASM v3

You can find lists of targets and options and what they do in the Makefile.

The VARIANTS option allows shipping multiple builds inside a single library file. It is intended for distribution packages that must run efficiently across a wide range of x86 CPUs: the library detects the host ISA level at runtime and dispatches to the fastest available implementation. They are not needed for a native single-machine build, where -march=native already picks the best code path at compile time. For example:

make CFLAGS="-march=x86-64" VARIANTS=x86-64-v2,x86-64-v3 BUILDTEST=no

CMake integration

edge264 ships a CMakeLists.txt that wraps its Makefile, so you can integrate it into a CMake project without writing any custom build logic. It exposes a single imported target edge264::edge264 for use with target_link_libraries.

cmake_minimum_required(VERSION 3.14)
project(my_app C)

include(FetchContent)
FetchContent_Declare(edge264
  GIT_REPOSITORY https://github.com/tvlabs/edge264.git
  GIT_TAG        v1.0  # always pin to a tag or commit hash
)
FetchContent_MakeAvailable(edge264)

add_executable(my_app main.c)
target_link_libraries(my_app PRIVATE edge264::edge264)

Testing

A custom test suite is included and regularly updated, to run it:

make check

For more advanced testing and display, the program edge264_test can browse files in a given directory, decoding each <video>.264 file and comparing its output with each sibling file <video>.yuv if found. On the set of AVCv1, FRExt and MVC conformance bitstreams, 109/224 files are decoded without errors, the rest using yet unsupported features.

make
./edge264_test --help # prints all options available
ffmpeg -i vid.mp4 -vcodec copy -bsf h264_mp4toannexb -an vid.264 # optional, converts from MP4 format
./edge264_test -d vid.264 # replace -d with -b to benchmark instead of display

Example code

Here is a complete example that opens an input file in Annex B byte stream format from command line, and dumps its decoded frames in planar YUV order to standard output. See edge264_test.c for a more complete example which can also display frames.

#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "edge264.h"

int main(int argc, char *argv[]) {
	int fd = open(argv[1], O_RDONLY);
	struct stat st;
	fstat(fd, &st);
	uint8_t *buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
	const uint8_t *nal = buf + 3 + (buf[2] == 0); // skip the [0]001 delimiter
	const uint8_t *end = buf + st.st_size;
	// auto threads, no logs, auto allocs
	Edge264Decoder *dec = edge264_alloc(-1, NULL, NULL, 0, NULL, NULL, NULL);
	Edge264Frame frm;
	int res;
	do {
		const uint8_t *start_code = edge264_find_start_code(nal, end, 0);
		res = edge264_decode_NAL(dec, nal, start_code, NULL, NULL);
		while (!edge264_get_frame(dec, &frm, 0)) {
			for (int y = 0; y < frm.height_Y; y++)
				write(1, frm.samples[0] + y * frm.stride_Y, frm.width_Y);
			for (int y = 0; y < frm.height_C; y++)
				write(1, frm.samples[1] + y * frm.stride_C, frm.width_C);
			for (int y = 0; y < frm.height_C; y++)
				write(1, frm.samples[2] + y * frm.stride_C, frm.width_C);
		}
		if (res != ENOBUFS)
			nal = start_code + 3;
	} while (res == 0 || res == ENOBUFS);
	edge264_free(&dec);
	munmap(buf, st.st_size);
	close(fd);
	return 0;
}

API reference

<code>const uint8_t * <b>edge264_find_start_code</b>(buf, end, four_byte)</code>

Return a pointer to the next three or four byte (0)001 start code prefix, or end if not found.

  • const uint8_t * buf - first byte of buffer to search into
  • const uint8_t * end - first invalid byte past the buffer that stops the search
  • int four_byte - if 0 seek a 001 prefix, otherwise seek a 0001

<code>Edge264Decoder * <b>edge264_alloc</b>(n_threads, log_cb, log_arg, log_mbs, alloc_cb, free_cb, alloc_arg)</code>

Allocate and initialize a decoding context.

  • int n_threads - number of background worker threads, with 0 to disable multithreading and -1 to detect the number of logical cores at runtime
  • void (* log_cb)(const char * str, void * log_arg) - if not NULL, a fputs-compatible function pointer that edge264_decode_NAL will call to log every header, SEI or macroblock, requiring the logs variant (otherwise it fails at runtime), and called from the same thread except for macroblocks in multithreaded decoding
  • void * log_arg - custom value passed to log_cb
  • int log_mbs - set to 1 to enable the logging of macroblocks
  • void (* alloc_cb)(void ** samples, unsigned samples_size, void ** mbs, unsigned mbs_size, int errno_on_fail, void * alloc_arg) - if not NULL, a function pointer that edge264_decode_NAL will call (on the same thread) instead of malloc to request allocation of samples and macroblock buffers for a frame (errno_on_fail is ENOMEM for mandatory allocations, or ENOBUFS for allocations that may be skipped to save memory but reduce playback smoothness)
  • void (* free_cb)(void * samples, void * mbs, void * alloc_arg) - if not NULL, a function pointer that edge264_decode_NAL and edge264_free will call (on the same thread) to free buffers allocated through alloc_cb
  • void * alloc_arg - custom value passed to alloc_cb and free_cb

<code>int <b>edge264_decode_NAL</b>(dec, buf, end, free_cb, free_arg)</code>

Decode a single NAL unit of any type.

  • Edge264Decoder * dec - initialized decoding context
  • const uint8_t * buf - first byte of NAL unit (containing nal_unit_type)
  • const uint8_t * end - first byte past the buffer (passing buf >= end will make all buffered frames ready for output with edge264_get_frame)
  • void (* free_cb)(void * free_arg, int ret) - function that may be called from another thread to signal the end of parsing and release the NAL buffer (only when returning 0)
  • void * free_arg - custom value that will be passed to free_cb

Return codes:

  • 0 - success
  • ENOBUFS - more frames should be consumed with edge264_get_frame before calling the function again with the same NAL
  • ENOTSUP - unsupported stream (decoding may proceed but could return zero frames)
  • EBADMSG - invalid stream (decoding may proceed but could show visual artefacts, if you can check with another decoder that the stream is actually flawless, please consider filling a bug report 🙏)
  • EINVAL - the function was called with dec == NULL or buf == NULL
  • ENODATA - the function was called with buf >= end and there are no frames left to output
  • ENOMEM - malloc failed to allocate memory

Note that MVC is enabled after receiving a "Subset sequence parameter set" NAL. Once enabled it can only be reverted to single-frame pictures by passing a "End of sequence" ("\x6a") NAL to edge264_decode_NAL.

<code>int <b>edge264_get_frame</b>(dec, out, borrow)</code>

Fetch the next frame ready for output.

  • Edge264Decoder * dec - initialized decoding context
  • Edge264Frame *out - a structure that will be filled with data for the frame returned
  • int borrow - if 0 the frame may be accessed until the next call to edge264_decode_NAL, otherwise the frame should be explicitly returned with edge264_return_frame. Note that access is not exclusive, it may be used concurrently as reference for other frames.

Return codes are:

  • 0 on success (one frame is returned)
  • EINVAL if the function was called with dec == NULL

Related Skills

View on GitHub
GitHub Stars425
CategoryDevelopment
Updated3d ago
Forks19

Languages

C

Security Score

95/100

Audited on Aug 5, 2026

No findings