Skip to content

Commit c53d54c

Browse files
committed
feat: upgrade FFmpeg to 8.0.1 and restore upstream headers
- Update FFmpeg version references to 8.0.1 in builder, fetch, and documentation - Replace patched `libavformat/avformat.h` with original upstream header (removes manual struct patch) - Update static library download URL to n8.0.1 - Regenerate Go bindings with improved generator: skip anonymous structs and fields CGO cannot reference - Add manual Go binding for AVStreamGroupTileGrid offsets in custom.go using a typedef for the anonymous struct - Apply upstream bugfixes to `libavutil/common.h` macros - All tests pass and generator logs/documentation updated
1 parent 475efb5 commit c53d54c

11 files changed

Lines changed: 233 additions & 179 deletions

File tree

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Build once, deploy anywhere. No hunting for system FFmpeg. No version mismatches
1212

1313
## Features
1414

15-
- **FFmpeg 8.0** - Latest release with AV1, H.265, H.264, VP8/9
15+
- **FFmpeg 8.0.1** - Latest release with AV1, H.265, H.264, VP8/9
1616
- **Truly static** - Builds into your binary (just needs system `m` and `stdc++` libraries)
1717
- **Cross-platform** - Linux and macOS (arm64, amd64)
1818
- **Hardware acceleration** - NVENC/NVDEC, VideoToolbox, Vulkan and QuickSync support
@@ -83,7 +83,7 @@ If you need complete FFmpeg with all filters, use the official FFmpeg distributi
8383

8484
| Library | Version | Description |
8585
|------------------|-------------|------------------------------------------------------------------------------------|
86-
| FFmpeg | 8.0 | A complete, cross-platform solution to record, convert and stream audio and video |
86+
| FFmpeg | 8.0.1 | A complete, cross-platform solution to record, convert and stream audio and video |
8787
| dav1d | 1.5.2 | AV1 cross-platform decoder, open-source, and focused on speed, size and correctness|
8888
| glslang | 15.4.0 | Khronos-reference front end for GLSL/ESSL and a SPIR-V generator |
8989
| libsrt | 1.5.5-rc.0a | A transport protocol for ultra low latency live video and audio streaming |
@@ -93,17 +93,18 @@ If you need complete FFmpeg with all filters, use the official FFmpeg distributi
9393
| libxml2 | 2.15.1 | An XML parser and toolkit implemented in C |
9494
| libiconv | 1.18 | A character set conversion library (*macOS only*) |
9595
| mp3lame | 3.100 | A high quality MPEG Audio Layer III (MP3) encoder |
96-
| nv-codec-headers | 11.1.5.3 | Headers required to interface with Nvidias codec APIs (*Linux only*) |
96+
| nv-codec-headers | 12.2.72.0 | Headers required to interface with Nvidias codec APIs (*Linux only*) |
9797
| openssl | 3.6.0 | Open Source Toolkit for the TLS, DTLS, and QUIC protocols. |
9898
| opus | 1.5.2 | A totally open, royalty-free, highly versatile audio codec |
99-
| rav1e | 0.8.2 | The fastest and safest AV1 encoder. |
100-
| Vulkan-Headers | 1.4.332 | Vulkan header files and API registry |
101-
| VVenC | 1.13.1 | Fraunhofer Versatile Video Encoder, a fast & efficient software H.266/VVC encoder |
99+
| rav1e | 0.8.1 | The fastest and safest AV1 encoder. |
100+
| Vulkan-Headers | 1.4.335 | Vulkan header files and API registry |
102101
| x264 | head | H.264/MPEG-4 AVC compression format library for encoding video streams |
103102
| x265 | head | H.265/MPEG-H HEVC compression format library for encoding video streams |
104103
| zimg | 3.0.6 | Scaling, colorspace conversion, and dithering library |
105104
| zlib | 1.3.1 | A Massively Spiffy Yet Delicately Unobtrusive Compression Library |
106105

106+
VVenC 1.13.1 (Fraunhofer Versatile Video Encoder, a fast & efficient software H.266/VVC encoder) is in the build configuration, **but currently disabled**, as it adds ~25MB to the static ffmpeg library and is too slow for practical use.
107+
107108
### Enabled Codecs
108109

109110
Details of codecs, muxers and parsers available in enable in the static ffmpeg library that ffmpeg-statigo ships are documented in [`docs/CODECS.md`](docs/CODECS.md).

custom.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ extern const AVOutputFormat *av_muxer_iterate(void **opaque);
1616
extern const AVInputFormat *av_demuxer_iterate(void **opaque);
1717
extern const AVFilter *av_filter_iterate(void **opaque);
1818
extern const AVBitStreamFilter *av_bsf_iterate(void **opaque);
19+
20+
// Typedef for anonymous struct inside AVStreamGroupTileGrid.
21+
// The 'offsets' field is an array of these structs containing tile position info.
22+
// We create a typedef so CGO can reference it by a stable name.
23+
typedef struct {
24+
unsigned int idx;
25+
int horizontal;
26+
int vertical;
27+
} AVStreamGroupTileGridOffset;
1928
*/
2029
import "C"
2130
import (
@@ -264,3 +273,94 @@ func AVUuidCopy(dest *AVUUID, src *AVUUID) {
264273
func AVUuidNil(uu *AVUUID) {
265274
C.av_uuid_nil((*C.uint8_t)(unsafe.Pointer(&uu[0])))
266275
}
276+
277+
// --- Manual binding for anonymous struct in AVStreamGroupTileGrid ---
278+
279+
// AVStreamGroupTileGridOffset wraps the anonymous struct used in AVStreamGroupTileGrid.offsets.
280+
// This is a tile grid offset structure containing the stream index and pixel offsets.
281+
type AVStreamGroupTileGridOffset struct {
282+
ptr *C.AVStreamGroupTileGridOffset
283+
}
284+
285+
// RawPtr returns the underlying C pointer.
286+
func (s *AVStreamGroupTileGridOffset) RawPtr() unsafe.Pointer {
287+
return unsafe.Pointer(s.ptr)
288+
}
289+
290+
// ToAVStreamGroupTileGridOffsetArray creates an Array accessor for a slice of offsets.
291+
func ToAVStreamGroupTileGridOffsetArray(ptr unsafe.Pointer) *Array[*AVStreamGroupTileGridOffset] {
292+
if ptr == nil {
293+
return nil
294+
}
295+
296+
return &Array[*AVStreamGroupTileGridOffset]{
297+
elemSize: unsafe.Sizeof(C.AVStreamGroupTileGridOffset{}),
298+
loadPtr: func(pointer unsafe.Pointer) *AVStreamGroupTileGridOffset {
299+
cValue := (*C.AVStreamGroupTileGridOffset)(pointer)
300+
return &AVStreamGroupTileGridOffset{ptr: cValue}
301+
},
302+
ptr: ptr,
303+
storePtr: func(pointer unsafe.Pointer, value *AVStreamGroupTileGridOffset) {
304+
dest := (*C.AVStreamGroupTileGridOffset)(pointer)
305+
if value != nil {
306+
*dest = *value.ptr
307+
}
308+
},
309+
}
310+
}
311+
312+
// Idx gets the idx field.
313+
// Index of the stream in the group this tile references.
314+
// Must be < AVStreamGroup.nb_streams.
315+
func (s *AVStreamGroupTileGridOffset) Idx() uint {
316+
return uint(s.ptr.idx)
317+
}
318+
319+
// SetIdx sets the idx field.
320+
func (s *AVStreamGroupTileGridOffset) SetIdx(value uint) {
321+
s.ptr.idx = (C.uint)(value)
322+
}
323+
324+
// Horizontal gets the horizontal field.
325+
// Offset in pixels from the left edge of the canvas where the tile should be placed.
326+
func (s *AVStreamGroupTileGridOffset) Horizontal() int {
327+
return int(s.ptr.horizontal)
328+
}
329+
330+
// SetHorizontal sets the horizontal field.
331+
func (s *AVStreamGroupTileGridOffset) SetHorizontal(value int) {
332+
s.ptr.horizontal = (C.int)(value)
333+
}
334+
335+
// Vertical gets the vertical field.
336+
// Offset in pixels from the top edge of the canvas where the tile should be placed.
337+
func (s *AVStreamGroupTileGridOffset) Vertical() int {
338+
return int(s.ptr.vertical)
339+
}
340+
341+
// SetVertical sets the vertical field.
342+
func (s *AVStreamGroupTileGridOffset) SetVertical(value int) {
343+
s.ptr.vertical = (C.int)(value)
344+
}
345+
346+
// Offsets gets the offsets field from AVStreamGroupTileGrid.
347+
// Returns an array of tile grid offsets containing stream index and pixel positions.
348+
// The offsets field is an anonymous struct in C, so we cast to our compatible typedef.
349+
func (s *AVStreamGroupTileGrid) Offsets() *AVStreamGroupTileGridOffset {
350+
// The C field is an anonymous struct pointer; cast to our compatible typedef
351+
value := unsafe.Pointer(s.ptr.offsets)
352+
if value == nil {
353+
return nil
354+
}
355+
return &AVStreamGroupTileGridOffset{ptr: (*C.AVStreamGroupTileGridOffset)(value)}
356+
}
357+
358+
// SetOffsets sets the offsets field on AVStreamGroupTileGrid.
359+
func (s *AVStreamGroupTileGrid) SetOffsets(value *AVStreamGroupTileGridOffset) {
360+
if value != nil {
361+
// Use unsafe pointer to bypass CGO type checking between compatible struct types
362+
*(*unsafe.Pointer)(unsafe.Pointer(&s.ptr.offsets)) = unsafe.Pointer(value.ptr)
363+
} else {
364+
*(*unsafe.Pointer)(unsafe.Pointer(&s.ptr.offsets)) = nil
365+
}
366+
}

docs/DEVELOPMENT.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ Go modules don't handle 100MB+ binary files. The `go get` command won't fetch st
1414

1515
Projects using ffmpeg-statigo follow this pattern. Reference implementations with justfiles and GitHub workflows:
1616

17-
- [jivedrop](https://github.com/linuxmatters/jivedrop)*Drop your podcast .wav into a neat MP3, ship the show metadata, cover art, and all 🪩*
18-
- [jivefire](https://github.com/linuxmatters/jivefire)*Spin your podcast .wav into a groovy MP4 visualiser. Cava-inspired real-time audio frequencies 🔥*
17+
- [jivedrop](https://github.com/linuxmatters/jivedrop)*Drop your podcast .wav into a shiny MP3 with metadata, cover art, and all 🪩*
18+
- [jivefire](https://github.com/linuxmatters/jivefire)*Spin your podcast .wav into a groovy MP4 visualiser with Cava-inspired real-time audio frequencies 🔥*
1919

2020
### Project Structure
2121

ffmpeg_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func TestVersions(t *testing.T) {
11-
// FFmpeg 8.0: version 62.3.100 = 0x3E0364 = 4066148
11+
// FFmpeg 8.0.x: libavcodec version 62.11.100 (0x3E0B64 = 4066148)
1212
assert.Equal(t, 4066148, int(ffmpeg.AVCodecVersion()), "AVCodec version should match expected")
1313
assert.Equal(t, ffmpeg.LIBAVCodecVersionInt, int(ffmpeg.AVCodecVersion()), "AVCodec version func and const should match")
1414
}

include/libavformat/avformat.h

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -950,29 +950,6 @@ typedef struct AVStream {
950950
* sizeof(AVStreamGroupTileGrid) is not a part of the ABI and may only be
951951
* allocated by avformat_stream_group_create().
952952
*/
953-
954-
/**
955-
* Tile grid offset structure.
956-
*/
957-
struct UnnamedStruct_avformat_986_5 {
958-
/**
959-
* Index of the stream in the group this tile references.
960-
*
961-
* Must be < @ref AVStreamGroup.nb_streams "nb_streams".
962-
*/
963-
unsigned int idx;
964-
/**
965-
* Offset in pixels from the left edge of the canvas where the tile
966-
* should be placed.
967-
*/
968-
int horizontal;
969-
/**
970-
* Offset in pixels from the top edge of the canvas where the tile
971-
* should be placed.
972-
*/
973-
int vertical;
974-
};
975-
976953
typedef struct AVStreamGroupTileGrid {
977954
const AVClass *av_class;
978955

@@ -1006,7 +983,24 @@ typedef struct AVStreamGroupTileGrid {
1006983
*
1007984
* Freed by libavformat in avformat_free_context().
1008985
*/
1009-
struct UnnamedStruct_avformat_986_5 *offsets;
986+
struct {
987+
/**
988+
* Index of the stream in the group this tile references.
989+
*
990+
* Must be < @ref AVStreamGroup.nb_streams "nb_streams".
991+
*/
992+
unsigned int idx;
993+
/**
994+
* Offset in pixels from the left edge of the canvas where the tile
995+
* should be placed.
996+
*/
997+
int horizontal;
998+
/**
999+
* Offset in pixels from the top edge of the canvas where the tile
1000+
* should be placed.
1001+
*/
1002+
int vertical;
1003+
} *offsets;
10101004

10111005
/**
10121006
* The pixel value per channel in RGBA format used if no pixel of any tile

include/libavutil/common.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -486,13 +486,13 @@ static av_always_inline av_const int av_parity_c(uint32_t v)
486486
* to prevent undefined results.
487487
*/
488488
#define GET_UTF8(val, GET_BYTE, ERROR)\
489-
val= (GET_BYTE);\
489+
val= (uint8_t)(GET_BYTE);\
490490
{\
491491
uint32_t top = (val & 128) >> 1;\
492492
if ((val & 0xc0) == 0x80 || val >= 0xFE)\
493493
{ERROR}\
494494
while (val & top) {\
495-
unsigned int tmp = (GET_BYTE) - 128;\
495+
unsigned int tmp = (uint8_t)(GET_BYTE) - 128;\
496496
if(tmp>>6)\
497497
{ERROR}\
498498
val= (val<<6) + tmp;\
@@ -511,11 +511,11 @@ static av_always_inline av_const int av_parity_c(uint32_t v)
511511
* typically a goto statement.
512512
*/
513513
#define GET_UTF16(val, GET_16BIT, ERROR)\
514-
val = (GET_16BIT);\
514+
val = (uint16_t)(GET_16BIT);\
515515
{\
516516
unsigned int hi = val - 0xD800;\
517517
if (hi < 0x800) {\
518-
val = (GET_16BIT) - 0xDC00;\
518+
val = (uint16_t)(GET_16BIT) - 0xDC00;\
519519
if (val > 0x3FFU || hi > 0x3FFU)\
520520
{ERROR}\
521521
val += (hi<<10) + 0x10000;\

include/libavutil/ffversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* Automatically generated by version.sh, do not manually edit! */
22
#ifndef AVUTIL_FFVERSION_H
33
#define AVUTIL_FFVERSION_H
4-
#define FFMPEG_VERSION "731bbe9"
4+
#define FFMPEG_VERSION "n8.0.1"
55
#endif /* AVUTIL_FFVERSION_H */

internal/builder/libraries.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ var libsrt = &Library{
671671
// ffmpeg - FFmpeg multimedia framework
672672
var ffmpeg = &Library{
673673
Name: "ffmpeg",
674-
URL: "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n8.0.tar.gz",
674+
URL: "https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n8.0.1.tar.gz",
675675
BuildSystem: &AutoconfBuild{},
676676
SkipAutoFlags: true, // FFmpeg uses --extra-cflags and --extra-ldflags instead
677677
PostExtract: func(srcPath string) error {

internal/generator/generator.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,26 @@ var fieldCTypeOverrides = map[string]map[string]string{
4646
},
4747
}
4848

49+
// skippedStructs contains struct names that should not be generated.
50+
// These require manual bindings in custom.go, typically because they are
51+
// anonymous C structs that CGO cannot directly reference by name.
52+
var skippedStructs = map[string]bool{
53+
// Anonymous struct inside AVStreamGroupTileGrid - CGO generates an internal
54+
// name like "struct___12" which is unstable across compilations.
55+
"UnnamedStruct_avformat_986_5": true,
56+
}
57+
58+
// skippedFields contains struct.field combinations that should not be generated.
59+
// These require manual bindings in custom.go, typically because they reference
60+
// types that CGO cannot directly access (like anonymous structs).
61+
var skippedFields = map[string]map[string]bool{
62+
"AVStreamGroupTileGrid": {
63+
// The offsets field is a pointer to an anonymous struct that CGO
64+
// assigns an internal name. Manual binding in custom.go handles this.
65+
"offsets": true,
66+
},
67+
}
68+
4969
// isOutputParameter checks if a parameter name suggests it's an output parameter
5070
// isOutputParameter detects function parameters that are output parameters.
5171
// Priority 2 Enhancement: Recovers ~30 functions with output parameters.
@@ -333,6 +353,12 @@ func (g *Generator) generateStructs() {
333353
for _, stName := range i.structOrder {
334354
st := i.structs[stName]
335355

356+
// Skip structs that require manual bindings
357+
if skippedStructs[st.Name] {
358+
log.Println("Skipping struct", st.Name, "(manual binding in custom.go)")
359+
continue
360+
}
361+
336362
log.Println("Generating struct", st.Name)
337363
o.Commentf("--- Struct %v ---", st.Name)
338364
o.Line()
@@ -464,6 +490,15 @@ func (g *Generator) generateStructs() {
464490

465491
fieldLoop:
466492
for _, field := range st.Fields {
493+
// Check if this field should be skipped (manual binding in custom.go)
494+
if fields, ok := skippedFields[st.Name]; ok {
495+
if fields[field.Name] {
496+
o.Commentf("%v skipped (manual binding in custom.go)", field.Name)
497+
o.Line()
498+
continue fieldLoop
499+
}
500+
}
501+
467502
fName := strcase.ToCamel(field.Name)
468503

469504
cName := field.Name

lib/fetch.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import (
2020
)
2121

2222
// Version is the FFmpeg library version (major.minor.patch)
23-
// The downloader will find the latest internal release (e.g., lib-8.0.0.3)
24-
const Version = "8.0.0"
23+
// The downloader will find the latest internal release (e.g., lib-8.0.1.0)
24+
const Version = "8.0.1"
2525

2626
// DownloadLibs downloads the FFmpeg static libraries for the current platform
2727
func DownloadLibs() error {

0 commit comments

Comments
 (0)