also looking at this
feat(video): add native AVC/HEVC and synchronized AAC MP4 streaming
#913
14 files
+1119
-0
| @@ -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> | |
| 8 | struct 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 | }; | |
| 19 | void 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 | } | |
| 28 | int 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; | |
| 79 | fail: | |
| 80 | home_encoder_close(e); return -3; | |
| 81 | } | |
| 82 | static 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 | } | |
| 101 | int 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 | } | |
| 112 | int 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 | ||
| 137 | int 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 | ||
| 157 | int home_encoder_request_keyframe(home_encoder *e) { if(!e)return -1;e->force_key=1;return 0; } | |