M avif.go +40 -44
@@ 1,12 1,10 @@
-//go:build notyet
package main
-// #cgo pkg-config: libavif
/*
#include <stdlib.h>
#include <string.h>
-#include "avif/avif.h"
+#include "lazif.h"
*/
import "C"
import (
@@ 14,10 12,26 @@ import (
"image"
"image/jpeg"
"runtime"
+ "sync"
"unsafe"
)
+var avifOnce sync.Once
+var avifLoaded bool
+
+func avifInit() {
+ rv := C.lazifInit()
+ if rv == 0 {
+ avifLoaded = true
+ }
+}
+
func avifEncode(data []byte) []byte {
+ avifOnce.Do(avifInit)
+ if !avifLoaded {
+ return nil
+ }
+
img, err := jpeg.Decode(bytes.NewReader(data))
if err != nil {
ilog.Printf("failed to decode")
@@ 28,57 42,39 @@ func avifEncode(data []byte) []byte {
ilog.Printf("not jpeg")
return nil
}
- var fmt C.avifPixelFormat
switch jpg.SubsampleRatio {
case image.YCbCrSubsampleRatio420:
- fmt = C.AVIF_PIXEL_FORMAT_YUV420
default:
ilog.Printf("bad sample ratio: %d", jpg.SubsampleRatio)
return nil
}
- w := C.uint(jpg.Rect.Max.X)
- h := C.uint(jpg.Rect.Max.Y)
- frame := C.avifImageCreate(w, h, 8, fmt)
- if frame == nil {
- ilog.Printf("no image")
- return nil
- }
- defer C.avifImageDestroy(frame)
+ var args C.struct_lazifArgs
+
+ args.width = C.uint(jpg.Rect.Max.X)
+ args.height = C.uint(jpg.Rect.Max.Y)
+
+ var pinner runtime.Pinner
+ defer pinner.Unpin()
- frame.yuvPlanes[C.AVIF_CHAN_Y] = (*C.uchar)(&jpg.Y[0])
- frame.yuvRowBytes[C.AVIF_CHAN_Y] = C.uint(jpg.YStride)
- frame.yuvPlanes[C.AVIF_CHAN_U] = (*C.uchar)(&jpg.Cb[0])
- frame.yuvRowBytes[C.AVIF_CHAN_U] = C.uint(jpg.CStride)
- frame.yuvPlanes[C.AVIF_CHAN_V] = (*C.uchar)(&jpg.Cr[0])
- frame.yuvRowBytes[C.AVIF_CHAN_V] = C.uint(jpg.CStride)
+ args.planes[0] = (*C.uchar)(&jpg.Y[0])
+ pinner.Pin(args.planes[0])
+ args.strides[0] = C.uint(jpg.YStride)
+ args.planes[1] = (*C.uchar)(&jpg.Cb[0])
+ pinner.Pin(args.planes[1])
+ args.strides[1] = C.uint(jpg.CStride)
+ args.planes[2] = (*C.uchar)(&jpg.Cr[0])
+ pinner.Pin(args.planes[2])
+ args.strides[2] = C.uint(jpg.CStride)
- runtime.KeepAlive(jpg)
-
- enc := C.avifEncoderCreate()
- if enc == nil {
- ilog.Printf("failed to create encoder")
+ rv := C.lazifEncode(&args)
+ if rv != 0 {
+ ilog.Printf("failed to encode")
return nil
}
- defer C.avifEncoderDestroy(enc)
- enc.codecChoice = C.AVIF_CODEC_CHOICE_AOM
- enc.maxThreads = 2
- enc.speed = 9
-
- res := C.avifEncoderAddImage(enc, frame, 1, C.AVIF_ADD_IMAGE_FLAG_SINGLE)
- if res != 0 {
- ilog.Printf("failed to add image: %d", res)
- return nil
- }
+ res := make([]byte, int(args.outlen))
+ C.memcpy(unsafe.Pointer(&res[0]), unsafe.Pointer(args.out), args.outlen)
+ C.lazifFree(&args)
- var out C.avifRWData
- res = C.avifEncoderFinish(enc, &out)
- if res != 0 {
- ilog.Printf("failed to finish encode: %d", res)
- return nil
- }
-
- rv := make([]byte, int(out.size))
- C.memcpy(unsafe.Pointer(&rv[0]), unsafe.Pointer(out.data), out.size)
- return rv
+ return res
}
A => lazif.c +137 -0
@@ 0,0 1,137 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <string.h>
+
+#include "lazif.h"
+
+struct frame {
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+
+ int yuvFormat;
+ int yuvRange;
+ int yuvChromaSamplePosition;
+ uint8_t * yuvPlanes[3];
+ uint32_t yuvRowBytes[3];
+ int imageOwnsYUVPlanes;
+
+ uint8_t * alphaPlane;
+ uint32_t alphaRowBytes;
+ int imageOwnsAlphaPlane;
+ int alphaPremultiplied;
+};
+
+struct encoder {
+ int codecChoice;
+ int maxThreads;
+ int speed;
+ int keyframeInterval;
+ uint64_t timescale;
+ int repetitionCount;
+ uint32_t extraLayerCount;
+ int quality;
+ int qualityAlpha;
+ int minQuantizer;
+ int maxQuantizer;
+ int minQuantizerAlpha;
+ int maxQuantizerAlpha;
+};
+
+struct rwdata {
+ unsigned char *data;
+ size_t size;
+};
+
+static struct frame *(*imgCreate)(unsigned int, unsigned int, int, int);
+static struct encoder *(*encCreate)(void);
+static int (*encWrite)(struct encoder *, struct frame *, struct rwdata *);
+static void (*encDestroy)(struct encoder *);
+static void (*imgDestroy)(struct frame *);
+static void (*dataFree)(struct rwdata *);
+
+int
+lazifInit(void)
+{
+ void *lib = dlopen("libavif.so", RTLD_LAZY);
+ if (!lib) {
+ printf("no libavif\n");
+ return -1;
+ }
+ if (!(imgCreate = dlsym(lib, "avifImageCreate"))) {
+ printf("no imgCreate\n");
+ return -1;
+ }
+ if (!(encCreate = dlsym(lib, "avifEncoderCreate"))) {
+ printf("no encCreate\n");
+ return -1;
+ }
+ if (!(encWrite = dlsym(lib, "avifEncoderWrite"))) {
+ printf("no encWrite\n");
+ return -1;
+ }
+ if (!(encDestroy = dlsym(lib, "avifEncoderDestroy"))) {
+ printf("no encDestroy\n");
+ return -1;
+ }
+ if (!(imgDestroy = dlsym(lib, "avifImageDestroy"))) {
+ printf("no imgDestroy\n");
+ return -1;
+ }
+ if (!(dataFree = dlsym(lib, "avifRWDataFree"))) {
+ printf("no dataFree\n");
+ return -1;
+ }
+ return 0;
+}
+
+int
+lazifEncode(struct lazifArgs *args)
+{
+ int rv = -1;
+ const int yuv420 = 3;
+ struct frame *frame = imgCreate(args->width, args->height, 8, yuv420);
+ if (!frame)
+ goto out;
+
+ printf("create frame\n");
+ for (int i = 0; i < 3; i++) {
+ frame->yuvPlanes[i] = args->planes[i];
+ frame->yuvRowBytes[i] = args->strides[i];
+ }
+
+ struct encoder *enc = encCreate();
+ // avifEncoder *enc = avifEncoderCreate();
+ if (!enc)
+ goto out;
+ printf("create enc\n");
+ enc->maxThreads = 2;
+ enc->speed = 10;
+
+ printf("writing image\n");
+ struct rwdata out;
+ memset(&out, 0, sizeof(out));
+ int err = encWrite(enc, frame, &out);
+ if (err)
+ goto out;
+ printf("wrote image\n");
+ args->out = out.data;
+ args->outlen = out.size;
+ rv = 0;
+out:
+ if (enc)
+ encDestroy(enc);
+ if (frame)
+ imgDestroy(frame);
+ return rv;
+}
+
+void
+lazifFree(struct lazifArgs *args)
+{
+ struct rwdata out;
+ out.data = args->out;
+ out.size = args->outlen;
+ dataFree(&out);
+}
A => lazif.h +13 -0
@@ 0,0 1,13 @@
+struct lazifArgs {
+ unsigned int width;
+ unsigned int height;
+ unsigned char *planes[3];
+ unsigned int strides[3];
+ unsigned char *out;
+ size_t outlen;
+};
+
+int lazifInit(void);
+int lazifEncode(struct lazifArgs *args);
+void lazifFree(struct lazifArgs *args);
+
M main.go +3 -0
@@ 55,6 55,9 @@ func serverURL(u string, args ...interfa
}
func ElaborateUnitTests() {
+ data, _ := os.ReadFile("input.jpg")
+ d2 := avifEncode(data)
+ os.WriteFile("output.avif", d2, 0600)
}
func unplugserver(hostname string) {