Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ea65bfa
Adding windows compatibility
djthorpe Oct 7, 2026
5c4ee0a
Add temporary push trigger to test_ffmpeg workflow for WIP branch
djthorpe Oct 7, 2026
5cebfe6
Fix ubuntu-latest ffmpeg configure: install liblzma-dev etc. to match…
djthorpe Oct 7, 2026
d8b91d0
Also build libraw for test-ffmpeg: reader needs it transitively
djthorpe Oct 7, 2026
9523512
Rename workflow to test.yaml, since it will grow to cover all tests
djthorpe Oct 7, 2026
5392dbe
Rename workflow display name to "Test"
djthorpe Oct 7, 2026
a4340e2
Declare real build prerequisites on each test-* Makefile target
djthorpe Oct 7, 2026
72f0793
Drop Go 1.25 from merge test matrix, minimum is now 1.26
djthorpe Oct 7, 2026
b8f6d03
Add dependabot.yml, grouping noisy otel/grpc module updates
djthorpe Oct 7, 2026
f6528c7
Skip Test_avdevice_device_info_string when the first device is nil
djthorpe Oct 7, 2026
b325623
Drop sys/ffmpeg61 and sys/ffmpeg71, fix allocator mismatch in sys/ffm…
djthorpe Oct 7, 2026
100dff9
Expand CI matrix from make test-ffmpeg to the full make test
djthorpe Oct 7, 2026
20f205f
Add -lws2_32 to libraw.pc on Windows, fixing undefined __imp_htonl
djthorpe Oct 7, 2026
37dccde
Fix chromaprint build on generator-agnostic cmake, add HEIF decoder libs
djthorpe Oct 7, 2026
a4002c0
Install Docker on macOS/Windows runners for profile/manager's tests
djthorpe Oct 7, 2026
84cb08b
Make libraw.pc patching robust to upstream Libs: line format
djthorpe Oct 7, 2026
c3bcee9
Use native Postgres instead of Docker testcontainers on macOS/Windows CI
djthorpe Oct 7, 2026
4dd4ecb
Added go 1.27 for tests
djthorpe Oct 7, 2026
3442d95
ContentType: return immediately for curated extensions, skip the read
djthorpe Oct 7, 2026
66245fd
Fix the real reason -lws2_32 never applied: OS=Windows_NT env collision
djthorpe Oct 7, 2026
1418fc0
Fix encoder not handling EAGAIN from avcodec_send_frame
djthorpe Oct 7, 2026
cd38f41
Fix IsErrno comparing against a numeric value with no C-side meaning
djthorpe Oct 7, 2026
161fb7a
Bump actions/cache to v6, which natively targets node24
djthorpe Oct 7, 2026
094b3d9
writer.Create: use url.Path directly for local files, not url.String()
djthorpe Oct 7, 2026
d044571
Fix reader file handle leak: AVFormat_free_context vs close_input
djthorpe Oct 7, 2026
3765b6f
Define CHROMAPRINT_NODLL, fixing undefined __imp_chromaprint_* on Win…
djthorpe Oct 7, 2026
fbb325b
Disable NLS in libexif, fixing undefined libintl_* on Windows
djthorpe Oct 7, 2026
99c31a0
Fix flaky TestManager_StartCompletes: guarantee measurable duration
djthorpe Oct 7, 2026
8d258a3
Fix the same AVFormat_free_context leak in three more test helpers
djthorpe Oct 7, 2026
40c0cf4
Remove redundant -lstdc++ cgo directive; fix gomedia/task duration flake
djthorpe Oct 7, 2026
e9f25d6
Group-wrap -lchromaprint/-lstdc++ for GNU ld, retry at the libstdc++ bug
djthorpe Oct 7, 2026
e77feef
Fix TestProbeSource_File: build a real file:// URL, not string-pasted
djthorpe Oct 7, 2026
0957d57
Normalize RFC 8089 file:// URLs for FFmpeg's unusual protocol parsing
djthorpe Oct 8, 2026
e647a1a
Fixed style
djthorpe Oct 8, 2026
e691a67
Promote test.yaml to the real CI gate, retire on_pull_request_merge.yaml
djthorpe Oct 8, 2026
a89d57f
Add docker.yaml with a Windows image, mirroring go-omodie's docker.yml
djthorpe Oct 8, 2026
b777e4e
Remove Docker mechanics from the Makefile
djthorpe Oct 8, 2026
a13fdca
Address Copilot PR review: UNC paths, .jpg sniffing, run concurrency
djthorpe Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
This Go module provides CGO bindings to several media C libraries, layered consistently:

1. **sys/\*** - low-level CGO bindings, mirroring each C library's API 1:1
- `sys/ffmpeg80/` (default), plus `sys/ffmpeg71/`, `sys/ffmpeg61/` - FFmpeg bindings, selected via `SYS_VERSION` in the Makefile
- `sys/ffmpeg80/` - FFmpeg bindings, selected via `SYS_VERSION` in the Makefile
- `sys/libheif/`, `sys/libraw/`, `sys/libexif/`, `sys/chromaprint/`, `sys/dvb/`
2. **pkg/\*** - high-level, idiomatic Go APIs per library
- `pkg/ffmpeg/` - Reader, Decoder, Encoder, Resampler, Frame abstractions
Expand Down
26 changes: 26 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 2
updates:
# Go modules, checked monthly. OpenTelemetry and gRPC both release updates
# across many modules at once, so each is grouped into a single pull request.
- package-ecosystem: gomod
directory: /
schedule:
interval: monthly
groups:
opentelemetry:
patterns:
- "go.opentelemetry.io/*"
grpc:
patterns:
- "google.golang.org/grpc*"
- "github.com/grpc-ecosystem/*"

# Actions used by the workflows, checked monthly in a single pull request
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
groups:
github-actions:
patterns:
- "*"
24 changes: 0 additions & 24 deletions .github/workflows/on_pull_request_merge.yaml

This file was deleted.

128 changes: 128 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: "Test"

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
# Report results for every platform, even if one fails
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Configure git line endings
shell: bash
run: git config --global core.autocrlf input

- name: Checkout
uses: actions/checkout@v5

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.26.x"

- name: Install build tools (Linux)
if: runner.os == 'Linux'
run: sudo apt -y update && sudo apt -y install nasm cmake libde265-dev libaom-dev libdav1d-dev libxml2-dev liblzma-dev

- name: Install build tools (macOS)
if: runner.os == 'macOS'
# libde265/aom/dav1d give libheif an actual decoder to test against -
# without one, every heif test fails with "Decoder plugin generated
# an error: Unspecified". Mirrors the Linux apt list.
run: brew install pkg-config nasm cmake libde265 aom dav1d

- name: Start PostgreSQL (macOS)
# profile/manager's tests default to a Docker testcontainer, which
# ubuntu-latest's built-in daemon handles fine, but installing a
# working Docker daemon on macOS/Windows runners (e.g. via
# docker/setup-docker-action) took 7+ minutes. A native Postgres,
# pointed at via GOMEDIA_TEST_DATABASE_URL (see profile/test/test.go),
# is far faster - same approach as go-omodie's test.yml.
if: runner.os == 'macOS'
shell: bash
run: |
brew install postgresql@17
pgdata="$(brew --prefix)/var/postgresql@17"
if [[ ! -f "$pgdata/PG_VERSION" ]]; then
"$(brew --prefix postgresql@17)/bin/initdb" -D "$pgdata" --username "$(id -un)" --auth-local=trust --auth-host=scram-sha-256
fi
brew services start postgresql@17
pg_bin="$(brew --prefix postgresql@17)/bin"
echo "$pg_bin" >> "$GITHUB_PATH"
export PATH="$pg_bin:$PATH"
for attempt in {1..60}; do
if pg_isready -h 127.0.0.1 -p 5432; then
break
fi
sleep 1
done
psql -d postgres -c "ALTER USER \"$(id -un)\" WITH PASSWORD 'runner'"
echo "GOMEDIA_TEST_DATABASE_URL=postgres://$(id -un):runner@127.0.0.1:5432/postgres?sslmode=disable" >> "$GITHUB_ENV"

- name: Start PostgreSQL (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
Set-Service -Name postgresql-x64-17 -StartupType Manual
Start-Service -Name postgresql-x64-17
$pgIsReady = 'C:\Program Files\PostgreSQL\17\bin\pg_isready.exe'
for ($attempt = 0; $attempt -lt 60; $attempt++) {
& $pgIsReady -h 127.0.0.1 -p 5432 -U postgres
if ($LASTEXITCODE -eq 0) { break }
Start-Sleep -Seconds 1
}
if ($LASTEXITCODE -ne 0) { throw 'PostgreSQL did not become ready' }
Add-Content -Path $env:GITHUB_ENV -Value 'GOMEDIA_TEST_DATABASE_URL=postgres://postgres:root@127.0.0.1:5432/postgres?sslmode=disable'

- name: Cache build directory
uses: actions/cache@v6
with:
path: build
key: ${{ runner.os }}-build-${{ hashFiles('Makefile') }}
restore-keys: |
${{ runner.os }}-build-

- name: Setup MSYS2 (Windows)
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
# 'inherit' keeps the Go toolchain (installed above by actions/setup-go,
# outside MSYS2) visible inside the msys2 shell alongside gcc/make/pkg-config
path-type: inherit
install: >-
base-devel
make
curl
diffutils
nasm
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-zlib
mingw-w64-x86_64-cmake
mingw-w64-x86_64-libde265
mingw-w64-x86_64-aom
mingw-w64-x86_64-dav1d

- name: Run tests (Unix)
if: runner.os != 'Windows'
shell: bash
run: make test

- name: Run tests (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: make test
68 changes: 55 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,19 @@ LIBHEIF_VERSION ?= 1.23.1

# Set OS and Architecture (must be before CGO configuration)
ARCH ?= $(shell arch | tr A-Z a-z | sed 's/x86_64/amd64/' | sed 's/i386/amd64/' | sed 's/armv7l/arm/' | sed 's/aarch64/arm64/')
OS ?= $(shell uname | tr A-Z a-z)
# Windows always pre-sets an OS=Windows_NT environment variable (a classic
# DOS/Windows convention), which make's ?= would otherwise respect as an
# "already set" value - silently skipping the uname-based detection below
# and breaking every ifeq/filter against $(OS) further down. That's not a
# value anyone would intentionally pass to this Makefile, so compute OS
# directly in that case (note: OS := here, not OS ?= - a variable assigned
# to empty is still "already defined" as far as a later ?= is concerned, so
# clearing it first and relying on ?= afterwards would leave it empty).
ifeq ($(OS),Windows_NT)
OS := $(shell uname | tr A-Z a-z | sed -E 's/^(msys|mingw|cygwin).*/windows/')
else
OS ?= $(shell uname | tr A-Z a-z | sed -E 's/^(msys|mingw|cygwin).*/windows/')
endif
VERSION ?= $(shell git describe --tags --always | sed 's/^v//')
DOCKER_REGISTRY ?= ghcr.io/mutablelogic

Expand Down Expand Up @@ -152,17 +164,20 @@ chromaprint-configure: mkdir ${BUILD_DIR}/${CHROMAPRINT_VERSION} ffmpeg
-B ${BUILD_DIR}

# Build chromaprint
# cmake --build is generator-agnostic: a bare `make` here would fail whenever
# cmake picks Ninja instead of Unix Makefiles (e.g. MSYS2, where ninja is on
# PATH alongside the mingw-w64 toolchain), since no Makefile gets generated.
.PHONY: chromaprint-build
chromaprint-build: chromaprint-configure
@echo "Building ${CHROMAPRINT_VERSION} with ${JOBS} jobs"
@cd $(BUILD_DIR) && make -j$(JOBS)
@cmake --build ${BUILD_DIR} -j$(JOBS)

# Install chromaprint
# Create a modified pkg-config file that ensures correct linking order for C++
.PHONY: chromaprint
chromaprint: chromaprint-build
@echo "Installing ${CHROMAPRINT_VERSION} => ${PREFIX}"
@cd $(BUILD_DIR) && make install
@cmake --install ${BUILD_DIR}
@sed -i.bak 's/Libs: -L\${libdir} -lchromaprint/Libs: -L\${libdir} -lchromaprint -lstdc++ -lavutil/g' "${PREFIX}/lib/pkgconfig/libchromaprint.pc"
@rm -f "${PREFIX}/lib/pkgconfig/libchromaprint.pc.bak"

Expand Down Expand Up @@ -194,12 +209,19 @@ libraw-build: libraw-configure
@cd $(BUILD_DIR)/libraw-$(LIBRAW_VERSION) && make -j$(JOBS) lib/libraw.la lib/libraw_r.la

# Install libraw
# Patch pkg-config to add -lz (required for DNG deflate support) and -lm (math functions)
# Patch pkg-config to add -lz (required for DNG deflate support) and -lm (math
# functions). On Windows, htonl/ntohl (used for byte-swapping in RAW parsing)
# live in Winsock rather than libc, so -lws2_32 is needed too - appended here,
# rather than relying on link-line order, since a static lib can only resolve
# symbols from libraries listed after it. Appended to the end of the Libs:
# line (rather than matched/inserted after "-lraw -lstdc++") since libraw's
# own ./configure doesn't generate that substring consistently across
# platforms - e.g. not on Windows, where this previously silently no-op'd.
.PHONY: libraw
libraw: libraw-build
@echo "Installing ${LIBRAW_VERSION} => ${PREFIX}"
@cd $(BUILD_DIR)/libraw-$(LIBRAW_VERSION) && make install
@sed -i.bak 's|-lraw -lstdc++|-lraw -lstdc++ -lz -lm|' "${PREFIX}/lib/pkgconfig/libraw.pc"
@sed -i.bak '/^Libs:/ s/$$/ -lz -lm$(if $(filter windows,${OS}), -lws2_32)/' "${PREFIX}/lib/pkgconfig/libraw.pc"
@rm -f "${PREFIX}/lib/pkgconfig/libraw.pc.bak"
@${GO} clean -cache

Expand All @@ -217,10 +239,18 @@ ${BUILD_DIR}/libexif-${LIBEXIF_VERSION}:
fi

.PHONY: libexif-configure
# --disable-nls: go-media reads EXIF tags as structured data, not translated
# human-readable strings, so there's no need for libexif's gettext-based
# National Language Support. Without this, configure enables it whenever it
# finds gettext/libintl available (as MSYS2 does), requiring -lintl at link
# time, which nothing here provides - undefined reference to libintl_*.
# Doesn't affect Linux (glibc has gettext built in, no separate libintl) or
# macOS (Homebrew's gettext is keg-only, off configure's default search
# path, so it was already silently detected as absent there).
libexif-configure: mkdir ${BUILD_DIR}/libexif-${LIBEXIF_VERSION}
@echo "Configuring libexif-${LIBEXIF_VERSION} => ${PREFIX}"
@cd ${BUILD_DIR}/libexif-${LIBEXIF_VERSION} && ./configure \
--disable-docs --enable-year2038 \
--disable-docs --disable-nls --enable-year2038 \
--prefix="$(shell realpath ${PREFIX})" \
--enable-static --disable-shared

Expand Down Expand Up @@ -326,44 +356,56 @@ ts: npm-dep mkdir
test: ffmpeg chromaprint libexif libraw libheif test-ffmpeg test-chromaprint test-exif test-raw test-heif test-metadata test-gomedia

.PHONY: test-chromaprint
test-chromaprint:
# pkg/chromaprint -> pkg/ffmpeg -> sys/ffmpeg80, so chromaprint (which itself
# depends on ffmpeg via chromaprint-configure) covers both.
test-chromaprint: chromaprint
@echo ... test pkg/segmenter pkg/chromaprint
@${CGO_ENV} ${GO} test ${ARGS} ./pkg/segmenter
@${CGO_ENV} ${GO} test ${ARGS} ./pkg/chromaprint

.PHONY: test-exif
test-exif:
# pkg/exif imports sys/libheif directly, not just sys/libexif.
test-exif: libexif libheif
@echo ... test sys/libexif pkg/exif
@${CGO_ENV} ${GO} test ${ARGS} ./sys/libexif
@${CGO_ENV} ${GO} test ${ARGS} ./pkg/exif

.PHONY: test-raw
test-raw:
test-raw: libraw
@echo ... test sys/libraw pkg/raw
@${CGO_ENV} ${GO} test ${ARGS} ./sys/libraw
@${CGO_ENV} ${GO} test ${ARGS} ./pkg/raw

.PHONY: test-heif
test-heif:
# pkg/heif imports pkg/exif, which needs libexif as well as libheif.
test-heif: libheif libexif
@echo ... test sys/libheif pkg/heif
@${CGO_ENV} ${GO} test ${ARGS} ./sys/libheif
@${CGO_ENV} ${GO} test ${ARGS} ./pkg/heif

.PHONY: test-ffmpeg
test-ffmpeg: go-dep go-tidy
# reader -> metadata -> pkg/raw (blank import, for MIME type registration)
# -> sys/libraw, so libraw is needed even though this only tests ffmpeg
# bindings. writer and frame don't have this dependency.
test-ffmpeg: go-dep go-tidy ffmpeg libraw
@echo ... test sys/${SYS_VERSION} reader writer frame
@${CGO_ENV} ${GO} test ${ARGS} ./sys/${SYS_VERSION}
@${CGO_ENV} ${GO} test ${ARGS} ./reader/...
@${CGO_ENV} ${GO} test ${ARGS} ./writer/...
@${CGO_ENV} ${GO} test ${ARGS} ./frame/...

.PHONY: test-metadata
test-metadata:
# metadata/image imports pkg/exif, pkg/heif and pkg/raw; metadata/audio and
# metadata/video import sys/ffmpeg80 directly.
test-metadata: ffmpeg libexif libheif libraw
@echo ... test metadata
@${CGO_ENV} ${GO} test ${ARGS} ./metadata/...

.PHONY: test-gomedia
test-gomedia:
# task blank-imports all metadata/* subpackages (-> ffmpeg, libexif, libheif,
# libraw); gomedia imports pkg/chromaprint (-> chromaprint); profile only
# needs ffmpeg, already covered.
test-gomedia: ffmpeg libexif libheif libraw chromaprint
@echo ... test task profile gomedia
@${CGO_ENV} ${GO} test ${ARGS} ./task/...
@${CGO_ENV} ${GO} test ${ARGS} ./profile/...
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,7 @@ func main() {

### Low-Level FFmpeg Bindings

For direct FFmpeg access, use `sys/ffmpeg80` (the default; `sys/ffmpeg71` and `sys/ffmpeg61`
are also available, selected via the Makefile's `SYS_VERSION`):
For direct FFmpeg access, use `sys/ffmpeg80`, selected via the Makefile's `SYS_VERSION`:

```go
import (
Expand All @@ -248,7 +247,7 @@ graph := ff.AVFilterGraph_alloc()
## Project Structure

```text
sys/ffmpeg80/, sys/ffmpeg71/, sys/ffmpeg61/ # Low-level CGO FFmpeg bindings
sys/ffmpeg80/ # Low-level CGO FFmpeg bindings
sys/libheif/, sys/libraw/, sys/libexif/ # Low-level CGO bindings for image metadata/codecs
sys/chromaprint/, sys/dvb/ # Other low-level bindings

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading