ReviewOS

also looking at this

home-lang/home

feat(video): add native AVC/HEVC and synchronized AAC MP4 streaming

#913
Merged chris wants to merge feat/avc-hevc-mp4 into main
14 files +1119 -0
packages/video/src/codecs/video/native_encoder.cadded+157-0
Changes to packages/video/src/codecs/video/native_encoder.c
@@ -0,0 +1,157 @@
1/* Home's header-checked x264/x265 adapter. No hand-declared vendor structures. */
2#include "native_encoder.h"
3#include <stdlib.h>
4#include <string.h>
5#include <limits.h>
6#include <x264.h>
7#include <x265.h>
8struct home_encoder {
9 uint32_t codec;
10 x264_t *avc;
11 x265_encoder *hevc;
12 x265_param *param;
13 x265_picture *in, *out;
14 uint8_t *buffer;
15 size_t capacity;
16 uint32_t width, height;
17 int force_key;
18};
19void home_encoder_close(home_encoder *e) {
20 if (!e) return;
21 if (e->avc) x264_encoder_close(e->avc);
22 if (e->hevc) x265_encoder_close(e->hevc);
23 if (e->in) x265_picture_free(e->in);
24 if (e->out) x265_picture_free(e->out);
25 if (e->param) x265_param_free(e->param);
26 free(e->buffer); free(e);
27}
28int home_encoder_open(const home_encoder_config *c, home_encoder **out) {
29 if (!out) return -1;
30 *out=NULL;
31 if (!c || (c->codec!=1 && c->codec!=2) || c->width<16 || c->height<16 || c->width>8192 || c->height>8192 || (c->width|c->height)&1 || !c->fps_num || !c->fps_den || c->fps_num>1000000 || c->fps_den>1000000 || c->quality<1 || c->quality>100 || c->gop>INT_MAX || (c->bitrate && c->bitrate<1000)) return -1;
32 home_encoder *e=calloc(1,sizeof(*e));
33 if (!e) return -2;
34 e->codec=c->codec; e->width=c->width; e->height=c->height;
35 /* Quality 1..100 -> CRF 51..0; AVC clamps at 1 to keep High-profile
36 * compatibility (CRF0 requires High444 lossless). bitrate>0 selects ABR. */
37 const double crf=51.0*(100-c->quality)/99.0;
38 if (c->codec==1) {
39 x264_param_t p;
40 if (x264_param_default_preset(&p,"veryfast","zerolatency")<0) goto fail;
41 p.i_width=(int)c->width; p.i_height=(int)c->height; p.i_csp=X264_CSP_I420;
42 p.i_fps_num=c->fps_num; p.i_fps_den=c->fps_den;
43 p.i_timebase_num=1; p.i_timebase_den=1000000; p.b_vfr_input=1;
44 p.i_threads=1; p.i_bframe=0; p.rc.i_lookahead=0;
45 p.i_keyint_max=c->gop ? (int)c->gop : 60; p.i_keyint_min=1; p.i_scenecut_threshold=0;
46 p.b_open_gop=0; p.b_annexb=0; p.b_repeat_headers=0; p.i_log_level=X264_LOG_ERROR;
47 p.vui.b_fullrange=0; p.vui.i_colorprim=1; p.vui.i_transfer=13; p.vui.i_colmatrix=6;
48 p.rc.i_rc_method=c->bitrate ? X264_RC_ABR : X264_RC_CRF;
49 p.rc.i_bitrate=(int)(c->bitrate/1000); p.rc.f_rf_constant=(float)(crf<1.0 ? 1.0 : crf);
50 if (x264_param_apply_profile(&p,"high")<0) { x264_param_cleanup(&p); goto fail; }
51 e->avc=x264_encoder_open(&p);
52 x264_param_cleanup(&p);
53 if (!e->avc) goto fail;
54 } else {
55 e->param=x265_param_alloc(); e->in=x265_picture_alloc(); e->out=x265_picture_alloc();
56 if (!e->param || !e->in || !e->out) goto fail;
57 x265_param *p=e->param;
58 if (x265_param_default_preset(p,"veryfast","zerolatency")<0) goto fail;
59 p->sourceWidth=(int)c->width; p->sourceHeight=(int)c->height;
60 p->fpsNum=c->fps_num; p->fpsDenom=c->fps_den; p->internalCsp=X265_CSP_I420; p->internalBitDepth=8;
61 p->bframes=0; p->lookaheadDepth=0; p->frameNumThreads=1;
62 p->numaPools="none"; p->bEnableWavefront=0;
63 p->keyframeMax=c->gop ? (int)c->gop : 60; p->keyframeMin=1; p->scenecutThreshold=0; p->bOpenGOP=0;
64 p->bAnnexB=0; p->bRepeatHeaders=0; p->bEmitInfoSEI=0; p->logLevel=X265_LOG_ERROR;
65 /* Fixed CTU sizes across instances avoid x265's process-global CTU conflict. */
66 p->maxCUSize=16; p->minCUSize=8;
67 p->vui.bEnableVideoSignalTypePresentFlag=1; p->vui.bEnableVideoFullRangeFlag=0;
68 p->vui.bEnableColorDescriptionPresentFlag=1; p->vui.colorPrimaries=1;
69 p->vui.transferCharacteristics=13; p->vui.matrixCoeffs=6;
70 p->rc.rateControlMode=c->bitrate ? X265_RC_ABR : X265_RC_CRF;
71 p->rc.bitrate=(int)(c->bitrate/1000); p->rc.rfConstant=crf;
72 if (x265_param_apply_profile(p,"main")<0) goto fail;
73 e->hevc=x265_encoder_open(p);
74 if (!e->hevc) goto fail;
75 x265_picture_init(p,e->in); x265_picture_init(p,e->out);
76 e->in->bitDepth=8;
77 }
78 *out=e; return 0;
79fail:
80 home_encoder_close(e); return -3;
81}
82static int collect(home_encoder *e, x264_nal_t *a, x265_nal *h, uint32_t count, home_packet *out) {
83 size_t total=0;
84 for (uint32_t i=0;i<count;i++) {
85 size_t n=a ? (size_t)a[i].i_payload : h[i].sizeBytes;
86 if (n>SIZE_MAX-total) return -2;
87 total+=n;
88 }
89 if (total>e->capacity) {
90 uint8_t *p=realloc(e->buffer,total);
91 if (!p) return -2;
92 e->buffer=p; e->capacity=total;
93 }
94 size_t offset=0;
95 for (uint32_t i=0;i<count;i++) {
96 size_t n=a ? (size_t)a[i].i_payload : h[i].sizeBytes;
97 memcpy(e->buffer+offset,a ? a[i].p_payload : h[i].payload,n); offset+=n;
98 }
99 out->data=e->buffer; out->size=total; return 0;
100}
101int home_encoder_headers(home_encoder *e, home_packet *out) {
102 memset(out,0,sizeof(*out));
103 if (e->avc) {
104 x264_nal_t *nal=NULL; int n=0;
105 if (x264_encoder_headers(e->avc,&nal,&n)<0 || n<=0) return -3;
106 return collect(e,nal,NULL,(uint32_t)n,out);
107 }
108 x265_nal *nal=NULL; uint32_t n=0;
109 if (x265_encoder_headers(e->hevc,&nal,&n)<0 || !n) return -3;
110 return collect(e,NULL,nal,n,out);
111}
112int home_encoder_encode(home_encoder *e, const uint8_t *const planes[3], const int strides[3], int64_t pts, home_packet *out) {
113 memset(out,0,sizeof(*out));
114 if (!planes[0] || !planes[1] || !planes[2] || strides[0]<(int)e->width || strides[1]<(int)e->width/2 || strides[2]<(int)e->width/2) return -1;
115 if (e->avc) {
116 x264_picture_t in, decoded; x264_picture_init(&in); x264_picture_init(&decoded);
117 in.i_type=e->force_key ? X264_TYPE_IDR : X264_TYPE_AUTO;
118 e->force_key=0;
119 in.img.i_csp=X264_CSP_I420; in.img.i_plane=3; in.i_pts=pts;
120 for (int i=0;i<3;i++) { in.img.plane[i]=(uint8_t *)planes[i]; in.img.i_stride[i]=strides[i]; }
121 x264_nal_t *nal=NULL; int n=0;
122 int ret=x264_encoder_encode(e->avc,&nal,&n,&in,&decoded);
123 if(ret<0) return -3;
124 if(ret==0) return 0;
125 out->pts=decoded.i_pts; out->key=decoded.b_keyframe;
126 return collect(e,nal,NULL,(uint32_t)n,out);
127 }
128 for (int i=0;i<3;i++) { e->in->planes[i]=(void *)planes[i]; e->in->stride[i]=strides[i]; }
129 e->in->pts=pts;
130 e->in->sliceType=e->force_key ? X265_TYPE_IDR : X265_TYPE_AUTO;e->force_key=0;
131 x265_nal *nal=NULL; uint32_t n=0;
132 if (x265_encoder_encode(e->hevc,&nal,&n,e->in,e->out)<=0 || !n) return -3;
133 out->pts=e->out->pts; out->key=IS_X265_TYPE_I(e->out->sliceType);
134 return collect(e,NULL,nal,n,out);
135}
136
137int home_encoder_flush(home_encoder *e, home_packet *out) {
138 memset(out,0,sizeof(*out));
139 if(e->avc) {
140 if(!x264_encoder_delayed_frames(e->avc)) return 0;
141 x264_picture_t picture; x264_picture_init(&picture);
142 x264_nal_t *nal=NULL; int n=0;
143 int ret=x264_encoder_encode(e->avc,&nal,&n,NULL,&picture);
144 if(ret<0) return -3;
145 if(ret==0) return 0;
146 out->pts=picture.i_pts;out->key=picture.b_keyframe;
147 return collect(e,nal,NULL,(uint32_t)n,out);
148 }
149 x265_nal *nal=NULL;uint32_t n=0;
150 int ret=x265_encoder_encode(e->hevc,&nal,&n,NULL,e->out);
151 if(ret<0) return -3;
152 if(ret==0) return 0;
153 out->pts=e->out->pts;out->key=IS_X265_TYPE_I(e->out->sliceType);
154 return collect(e,NULL,nal,n,out);
155}
156
157int home_encoder_request_keyframe(home_encoder *e) { if(!e)return -1;e->force_key=1;return 0; }