1 /**
2  * @file h265/encode.c H.265 Encode
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 
7 #include <string.h>
8 #include <re.h>
9 #include <rem.h>
10 #include <baresip.h>
11 #include <x265.h>
12 #include "h265.h"
13 
14 
15 struct videnc_state {
16 	struct vidsz size;
17 	x265_param *param;
18 	x265_encoder *x265;
19 	int64_t pts;
20 	unsigned fps;
21 	unsigned bitrate;
22 	unsigned pktsize;
23 	videnc_packet_h *pkth;
24 	void *arg;
25 };
26 
27 
destructor(void * arg)28 static void destructor(void *arg)
29 {
30 	struct videnc_state *st = arg;
31 
32 	if (st->x265)
33 		x265_encoder_close(st->x265);
34 	if (st->param)
35 		x265_param_free(st->param);
36 }
37 
38 
set_params(struct videnc_state * st,unsigned fps,unsigned bitrate)39 static int set_params(struct videnc_state *st, unsigned fps, unsigned bitrate)
40 {
41 	st->param = x265_param_alloc();
42 	if (!st->param) {
43 		warning("h265: x265_param_alloc failed\n");
44 		return ENOMEM;
45 	}
46 
47 	x265_param_default(st->param);
48 
49 	if (0 != x265_param_apply_profile(st->param, "main")) {
50 		warning("h265: x265_param_apply_profile failed\n");
51 		return EINVAL;
52 	}
53 
54 	if (0 != x265_param_default_preset(st->param,
55 					   "ultrafast", "zerolatency")) {
56 
57 		warning("h265: x265_param_default_preset error\n");
58 		return EINVAL;
59 	}
60 
61 	st->param->fpsNum = fps;
62 	st->param->fpsDenom = 1;
63 
64 	/* VPS, SPS and PPS headers should be output with each keyframe */
65 	st->param->bRepeatHeaders = 1;
66 
67 	/* Rate Control */
68 	st->param->rc.rateControlMode = X265_RC_CRF;
69 	st->param->rc.bitrate = bitrate / 1000;
70 	st->param->rc.vbvMaxBitrate = bitrate / 1000;
71 	st->param->rc.vbvBufferSize = 2 * bitrate / fps;
72 
73 	return 0;
74 }
75 
76 
h265_encode_update(struct videnc_state ** vesp,const struct vidcodec * vc,struct videnc_param * prm,const char * fmtp,videnc_packet_h * pkth,void * arg)77 int h265_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
78 		       struct videnc_param *prm, const char *fmtp,
79 		       videnc_packet_h *pkth, void *arg)
80 {
81 	struct videnc_state *ves;
82 	int err = 0;
83 	(void)fmtp;
84 
85 	if (!vesp || !vc || !prm || prm->pktsize < 3 || !pkth)
86 		return EINVAL;
87 
88 	ves = *vesp;
89 
90 	if (!ves) {
91 
92 		ves = mem_zalloc(sizeof(*ves), destructor);
93 		if (!ves)
94 			return ENOMEM;
95 
96 		*vesp = ves;
97 	}
98 	else {
99 		if (ves->x265 && (ves->bitrate != prm->bitrate ||
100 				  ves->pktsize != prm->pktsize ||
101 				  ves->fps     != prm->fps)) {
102 
103 			x265_encoder_close(ves->x265);
104 			ves->x265 = NULL;
105 		}
106 	}
107 
108 	ves->bitrate = prm->bitrate;
109 	ves->pktsize = prm->pktsize;
110 	ves->fps     = prm->fps;
111 	ves->pkth    = pkth;
112 	ves->arg     = arg;
113 
114 	err = set_params(ves, prm->fps, prm->bitrate);
115 	if (err)
116 		return err;
117 
118 	return 0;
119 }
120 
121 
open_encoder(struct videnc_state * st,const struct vidsz * size)122 static int open_encoder(struct videnc_state *st, const struct vidsz *size)
123 {
124 	if (st->x265) {
125 		debug("h265: re-opening encoder\n");
126 		x265_encoder_close(st->x265);
127 	}
128 
129 	st->param->sourceWidth  = size->w;
130 	st->param->sourceHeight = size->h;
131 
132 	st->x265 = x265_encoder_open(st->param);
133 	if (!st->x265) {
134 		warning("h265: x265_encoder_open failed\n");
135 		return ENOMEM;
136 	}
137 
138 	return 0;
139 }
140 
141 
packetize(bool marker,const uint8_t * buf,size_t len,size_t maxlen,uint32_t rtp_ts,videnc_packet_h * pkth,void * arg)142 static inline int packetize(bool marker, const uint8_t *buf, size_t len,
143 			    size_t maxlen, uint32_t rtp_ts,
144 			    videnc_packet_h *pkth, void *arg)
145 {
146 	int err = 0;
147 
148 	if (len <= maxlen) {
149 		err = pkth(marker, rtp_ts, NULL, 0, buf, len, arg);
150 	}
151 	else {
152 		struct h265_nal nal;
153 		uint8_t fu_hdr[3];
154 		const size_t flen = maxlen - sizeof(fu_hdr);
155 
156 		err = h265_nal_decode(&nal, buf);
157 		if (err) {
158 			warning("h265: encode: could not decode"
159 				" NAL of %zu bytes (%m)\n", len, err);
160 			return err;
161 		}
162 
163 		h265_nal_encode(fu_hdr, H265_NAL_FU,
164 				nal.nuh_temporal_id_plus1);
165 
166 		fu_hdr[2] = 1<<7 | nal.nal_unit_type;
167 
168 		buf+=2;
169 		len-=2;
170 
171 		while (len > flen) {
172 			err |= pkth(false, rtp_ts, fu_hdr, 3, buf, flen,
173 				    arg);
174 
175 			buf += flen;
176 			len -= flen;
177 			fu_hdr[2] &= ~(1 << 7); /* clear Start bit */
178 		}
179 
180 		fu_hdr[2] |= 1<<6;  /* set END bit */
181 
182 		err |= pkth(marker, rtp_ts, fu_hdr, 3, buf, len,
183 			    arg);
184 	}
185 
186 	return err;
187 }
188 
189 
h265_encode(struct videnc_state * st,bool update,const struct vidframe * frame)190 int h265_encode(struct videnc_state *st, bool update,
191 		const struct vidframe *frame)
192 {
193 	x265_picture *pic_in = NULL, pic_out;
194 	x265_nal *nalv;
195 	uint32_t i, nalc = 0;
196 	int colorspace;
197 	int n, err = 0;
198 	uint32_t ts;
199 
200 	if (!st || !frame)
201 		return EINVAL;
202 
203 	switch (frame->fmt) {
204 
205 	case VID_FMT_YUV420P:
206 		colorspace = X265_CSP_I420;
207 		break;
208 
209 	case VID_FMT_YUV444P:
210 		colorspace = X265_CSP_I444;
211 		break;
212 
213 	default:
214 		warning("h265: encode: pixel format not supported (%s)\n",
215 			vidfmt_name(frame->fmt));
216 		return EINVAL;
217 	}
218 
219 	if (!st->x265 || !vidsz_cmp(&st->size, &frame->size) ||
220 	    st->param->internalCsp != colorspace) {
221 
222 		debug("h265: encoder: reset %u x %u (%s)\n",
223 		      frame->size.w, frame->size.h, vidfmt_name(frame->fmt));
224 
225 		st->param->internalCsp = colorspace;
226 
227 		err = open_encoder(st, &frame->size);
228 		if (err)
229 			return err;
230 
231 		st->size = frame->size;
232 	}
233 
234 	if (update) {
235 		debug("h265: encode: picture update was requested\n");
236 	}
237 
238 	pic_in = x265_picture_alloc();
239 	if (!pic_in) {
240 		warning("h265: x265_picture_alloc failed\n");
241 		return ENOMEM;
242 	}
243 
244 	x265_picture_init(st->param, pic_in);
245 
246 	pic_in->sliceType  = update ? X265_TYPE_IDR : X265_TYPE_AUTO;
247 	pic_in->pts        = ++st->pts;      /* XXX: add PTS to API */
248 	pic_in->colorSpace = colorspace;
249 
250 	for (i=0; i<3; i++) {
251 		pic_in->planes[i] = frame->data[i];
252 		pic_in->stride[i] = frame->linesize[i];
253 	}
254 
255 	/* NOTE: important to get the PTS of the "out" picture */
256 	n = x265_encoder_encode(st->x265, &nalv, &nalc, pic_in, &pic_out);
257 	if (n <= 0)
258 		goto out;
259 
260 	ts = video_calc_rtp_timestamp(pic_out.pts, st->fps);
261 
262 	for (i=0; i<nalc; i++) {
263 
264 		x265_nal *nal = &nalv[i];
265 		uint8_t *p = nal->payload;
266 		size_t len = nal->sizeBytes;
267 		bool marker;
268 
269 #if 0
270 		debug("h265: encode: %s type=%2d  %s\n",
271 			  h265_is_keyframe(nal->type) ? "<KEY>" : "     ",
272 			  nal->type, h265_nalunit_name(nal->type));
273 #endif
274 
275 		h265_skip_startcode(&p, &len);
276 
277 		/* XXX: use pic_out.pts */
278 
279 		marker = (i+1)==nalc;  /* last NAL */
280 
281 		err = packetize(marker, p, len, st->pktsize,
282 				ts, st->pkth, st->arg);
283 		if (err)
284 			goto out;
285 	}
286 
287  out:
288 	if (pic_in)
289 		x265_picture_free(pic_in);
290 
291 	return err;
292 }
293