Skip to content

Commit

Permalink
Make x264 glue code not mem-pinned
Browse files Browse the repository at this point in the history
  • Loading branch information
sergystepanov committed May 19, 2024
1 parent 99976dd commit ede15c4
Showing 1 changed file with 68 additions and 48 deletions.
116 changes: 68 additions & 48 deletions pkg/encoder/h264/x264.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,72 @@ package h264
#include "stdint.h"
#include "x264.h"
#include <stdlib.h>
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 {
Expand Down Expand Up @@ -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(&param),
h264 := C.h264_new(&param)
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() {
Expand All @@ -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
}

Expand Down

0 comments on commit ede15c4

Please sign in to comment.