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