From ede15c4fe5236176a26890381b2d48a3cf76f6b9 Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Sun, 19 May 2024 18:55:37 +0300 Subject: [PATCH] Make x264 glue code not mem-pinned --- pkg/encoder/h264/x264.go | 116 +++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/pkg/encoder/h264/x264.go b/pkg/encoder/h264/x264.go index 8e75f3ef2..5b792487c 100644 --- a/pkg/encoder/h264/x264.go +++ b/pkg/encoder/h264/x264.go @@ -8,25 +8,72 @@ package h264 #include "stdint.h" #include "x264.h" #include + +typedef struct +{ + x264_t *h; + x264_nal_t *nal; // array of NALs + int i_nal; // number of NALs + int y; // Y size + int uv; // U or V size + x264_picture_t pic; + x264_picture_t pic_out; +} h264; + +h264 *h264_new(x264_param_t *param) +{ + h264 tmp; + x264_picture_t pic; + + tmp.h = x264_encoder_open(param); + if (!tmp.h) + return NULL; + + x264_picture_init(&pic); + pic.img.i_csp = param->i_csp; + pic.img.i_plane = 3; + pic.img.i_stride[0] = param->i_width; + pic.img.i_stride[1] = param->i_width >> 1; + pic.img.i_stride[2] = param->i_width >> 1; + tmp.pic = pic; + + // crashes during x264_picture_clean :/ + //if (x264_picture_alloc(&pic, param->i_csp, param->i_width, param->i_height) < 0) + // return NULL; + + tmp.y = param->i_width * param->i_height; + tmp.uv = tmp.y >> 2; + + h264 *h = malloc(sizeof(h264)); + *h = tmp; + return h; +} + +int h264_encode(h264 *h, uint8_t *yuv) +{ + h->pic.img.plane[0] = yuv; + h->pic.img.plane[1] = h->pic.img.plane[0] + h->y; + h->pic.img.plane[2] = h->pic.img.plane[1] + h->uv; + h->pic.i_pts += 1; + return x264_encoder_encode(h->h, &h->nal, &h->i_nal, &h->pic, &h->pic_out); +} + +void h264_destroy(h264 *h) +{ + if (h == NULL) return; + x264_encoder_close(h->h); + free(h); +} */ import "C" import ( "fmt" - "runtime" "unsafe" ) type H264 struct { - ref *C.x264_t - - nal *C.x264_nal_t // array of NALs - cNal *C.int // number of NALs - y int // Y size - uv int // U or V size - in, out *C.x264_picture_t - - p runtime.Pinner + h *C.h264 } type Options struct { @@ -100,46 +147,18 @@ func NewEncoder(w, h int, th int, opts *Options) (encoder *H264, err error) { param.rc.i_rc_method = C.X264_RC_CRF param.rc.f_rf_constant = C.float(opts.Crf) - encoder = &H264{ - y: w * h, - uv: w * h / 4, - cNal: new(C.int), - nal: new(C.x264_nal_t), - out: new(C.x264_picture_t), - in: &C.x264_picture_t{ - img: C.x264_image_t{ - i_csp: param.i_csp, - i_plane: 3, - i_stride: [4]C.int{0: C.int(w), 1: C.int(w >> 1), 2: C.int(w >> 1)}, - }, - }, - ref: C.x264_encoder_open(¶m), + h264 := C.h264_new(¶m) + if h264 == nil { + return nil, fmt.Errorf("x264: cannot open the encoder") } - - if encoder.ref == nil { - err = fmt.Errorf("x264: cannot open the encoder") - } - return + return &H264{h264}, nil } func (e *H264) Encode(yuv []byte) []byte { - e.in.img.plane[0] = (*C.uchar)(unsafe.Pointer(&yuv[0])) - e.in.img.plane[1] = (*C.uchar)(unsafe.Pointer(&yuv[e.y])) - e.in.img.plane[2] = (*C.uchar)(unsafe.Pointer(&yuv[e.y+e.uv])) - - e.in.i_pts += 1 - - e.p.Pin(e.in.img.plane[0]) - e.p.Pin(e.in.img.plane[1]) - e.p.Pin(e.in.img.plane[2]) - - e.p.Pin(e.nal) - bytes := C.x264_encoder_encode(e.ref, &e.nal, e.cNal, e.in, e.out) - e.p.Unpin() - + bytes := C.h264_encode(e.h, (*C.uchar)(unsafe.SliceData(yuv))) // we merge multiple NALs stored in **nal into a single byte stream // ret contains the total size of NALs in bytes, i.e. each e.nal[...].p_payload * i_payload - return unsafe.Slice((*byte)(e.nal.p_payload), bytes) + return unsafe.Slice((*byte)(e.h.nal.p_payload), bytes) } func (e *H264) IntraRefresh() { @@ -150,15 +169,16 @@ func (e *H264) Info() string { return fmt.Sprintf("x264: v%v", Version()) } func (e *H264) SetFlip(b bool) { if b { - e.in.img.i_csp |= C.X264_CSP_VFLIP + (*e.h).pic.img.i_csp |= C.X264_CSP_VFLIP } else { - e.in.img.i_csp &= ^C.X264_CSP_VFLIP + (*e.h).pic.img.i_csp &= ^C.X264_CSP_VFLIP } } func (e *H264) Shutdown() error { - C.x264_encoder_close(e.ref) - e.p.Unpin() + if e.h != nil { + C.h264_destroy(e.h) + } return nil }