Edge264
Simple H.264 decoder
Install / Use
npx skills add tvlabs/edge264Installs into whichever agent you are using.
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
endif not found.
const uint8_t * buf- first byte of buffer to search intoconst uint8_t * end- first invalid byte past the buffer that stops the searchint 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 runtimevoid (* log_cb)(const char * str, void * log_arg)- if not NULL, afputs-compatible function pointer thatedge264_decode_NALwill call to log every header, SEI or macroblock, requiring thelogsvariant (otherwise it fails at runtime), and called from the same thread except for macroblocks in multithreaded decodingvoid * log_arg- custom value passed tolog_cbint log_mbs- set to 1 to enable the logging of macroblocksvoid (* 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 thatedge264_decode_NALwill call (on the same thread) instead of malloc to request allocation of samples and macroblock buffers for a frame (errno_on_failis 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 thatedge264_decode_NALandedge264_freewill call (on the same thread) to free buffers allocated throughalloc_cbvoid * alloc_arg- custom value passed toalloc_cbandfree_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 contextconst uint8_t * buf- first byte of NAL unit (containingnal_unit_type)const uint8_t * end- first byte past the buffer (passingbuf >= endwill make all buffered frames ready for output withedge264_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 returning0)void * free_arg- custom value that will be passed tofree_cb
Return codes:
0- successENOBUFS- more frames should be consumed withedge264_get_framebefore calling the function again with the same NALENOTSUP- 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 withdec == NULLorbuf == NULLENODATA- the function was called withbuf >= endand there are no frames left to outputENOMEM-mallocfailed 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 toedge264_decode_NAL.
<code>int <b>edge264_get_frame</b>(dec, out, borrow)</code>
Fetch the next frame ready for output.
Edge264Decoder * dec- initialized decoding contextEdge264Frame *out- a structure that will be filled with data for the frame returnedint borrow- if 0 the frame may be accessed until the next call toedge264_decode_NAL, otherwise the frame should be explicitly returned withedge264_return_frame. Note that access is not exclusive, it may be used concurrently as reference for other frames.
Return codes are:
0on success (one frame is returned)EINVALif the function was called withdec == NULL
Related Skills
node-connect
385.6kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
commit-push-pr
140.7kCommit, push, and open a PR
