1 /**
2  * @file av1/encode.c AV1 Encode
3  *
4  * Copyright (C) 2010 - 2016 Creytiv.com
5  */
6 
7 #include <string.h>
8 #include <re.h>
9 #include <rem.h>
10 #include <baresip.h>
11 #include <aom/aom.h>
12 #include <aom/aom_encoder.h>
13 #include <aom/aomcx.h>
14 #include "av1.h"
15 
16 
17 enum {
18 	HDR_SIZE = 4,
19 };
20 
21 
22 struct videnc_state {
23 	aom_codec_ctx_t ctx;
24 	struct vidsz size;
25 	aom_codec_pts_t pts;
26 	unsigned fps;
27 	unsigned bitrate;
28 	unsigned pktsize;
29 	bool ctxup;
30 	uint16_t picid;
31 	videnc_packet_h *pkth;
32 	void *arg;
33 };
34 
35 
destructor(void * arg)36 static void destructor(void *arg)
37 {
38 	struct videnc_state *ves = arg;
39 
40 	if (ves->ctxup)
41 		aom_codec_destroy(&ves->ctx);
42 }
43 
44 
av1_encode_update(struct videnc_state ** vesp,const struct vidcodec * vc,struct videnc_param * prm,const char * fmtp,videnc_packet_h * pkth,void * arg)45 int av1_encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
46 		      struct videnc_param *prm, const char *fmtp,
47 		      videnc_packet_h *pkth, void *arg)
48 {
49 	struct videnc_state *ves;
50 
51 	if (!vesp || !vc || !prm || prm->pktsize < (HDR_SIZE + 1))
52 		return EINVAL;
53 
54 	ves = *vesp;
55 
56 	if (!ves) {
57 
58 		ves = mem_zalloc(sizeof(*ves), destructor);
59 		if (!ves)
60 			return ENOMEM;
61 
62 		ves->picid = rand_u16();
63 
64 		*vesp = ves;
65 	}
66 	else {
67 		if (ves->ctxup && (ves->bitrate != prm->bitrate ||
68 				   ves->fps     != prm->fps)) {
69 
70 			aom_codec_destroy(&ves->ctx);
71 			ves->ctxup = false;
72 		}
73 	}
74 
75 	ves->bitrate = prm->bitrate;
76 	ves->pktsize = prm->pktsize;
77 	ves->fps     = prm->fps;
78 	ves->pkth    = pkth;
79 	ves->arg     = arg;
80 
81 	return 0;
82 }
83 
84 
open_encoder(struct videnc_state * ves,const struct vidsz * size)85 static int open_encoder(struct videnc_state *ves, const struct vidsz *size)
86 {
87 	aom_codec_enc_cfg_t cfg;
88 	aom_codec_err_t res;
89 
90 	res = aom_codec_enc_config_default(&aom_codec_av1_cx_algo, &cfg, 0);
91 	if (res)
92 		return EPROTO;
93 
94 	cfg.g_w               = size->w;
95 	cfg.g_h               = size->h;
96 	cfg.g_timebase.num    = 1;
97 	cfg.g_timebase.den    = ves->fps;
98 	cfg.g_error_resilient = AOM_ERROR_RESILIENT_DEFAULT;
99 	cfg.g_pass            = AOM_RC_ONE_PASS;
100 	cfg.g_lag_in_frames   = 0;
101 	cfg.rc_end_usage      = AOM_VBR;
102 	cfg.rc_target_bitrate = ves->bitrate;
103 	cfg.kf_mode           = AOM_KF_AUTO;
104 
105 	if (ves->ctxup) {
106 		debug("av1: re-opening encoder\n");
107 		aom_codec_destroy(&ves->ctx);
108 		ves->ctxup = false;
109 	}
110 
111 	res = aom_codec_enc_init(&ves->ctx, &aom_codec_av1_cx_algo, &cfg,
112 				 0);
113 	if (res) {
114 		warning("av1: enc init: %s\n", aom_codec_err_to_string(res));
115 		return EPROTO;
116 	}
117 
118 	ves->ctxup = true;
119 
120 	res = aom_codec_control(&ves->ctx, AOME_SET_CPUUSED, 8);
121 	if (res) {
122 		warning("av1: codec ctrl C: %s\n",
123 			aom_codec_err_to_string(res));
124 	}
125 
126 	return 0;
127 }
128 
129 
hdr_encode(uint8_t hdr[HDR_SIZE],bool noref,bool start,uint8_t partid,uint16_t picid)130 static inline void hdr_encode(uint8_t hdr[HDR_SIZE], bool noref, bool start,
131 			      uint8_t partid, uint16_t picid)
132 {
133 	hdr[0] = 1<<7 | noref<<5 | start<<4 | (partid & 0x7);
134 	hdr[1] = 1<<7;
135 	hdr[2] = 1<<7 | (picid>>8 & 0x7f);
136 	hdr[3] = picid & 0xff;
137 }
138 
139 
packetize(bool marker,uint32_t rtp_ts,const uint8_t * buf,size_t len,size_t maxlen,bool noref,uint8_t partid,uint16_t picid,videnc_packet_h * pkth,void * arg)140 static inline int packetize(bool marker, uint32_t rtp_ts,
141 			    const uint8_t *buf, size_t len,
142 			    size_t maxlen, bool noref, uint8_t partid,
143 			    uint16_t picid, videnc_packet_h *pkth, void *arg)
144 {
145 	uint8_t hdr[HDR_SIZE];
146 	bool start = true;
147 	int err = 0;
148 
149 	maxlen -= sizeof(hdr);
150 
151 	while (len > maxlen) {
152 
153 		hdr_encode(hdr, noref, start, partid, picid);
154 
155 		err |= pkth(false, rtp_ts, hdr, sizeof(hdr), buf, maxlen, arg);
156 
157 		buf  += maxlen;
158 		len  -= maxlen;
159 		start = false;
160 	}
161 
162 	hdr_encode(hdr, noref, start, partid, picid);
163 
164 	err |= pkth(marker, rtp_ts, hdr, sizeof(hdr), buf, len, arg);
165 
166 	return err;
167 }
168 
169 
av1_encode(struct videnc_state * ves,bool update,const struct vidframe * frame)170 int av1_encode(struct videnc_state *ves, bool update,
171 		const struct vidframe *frame)
172 {
173 	aom_enc_frame_flags_t flags = 0;
174 	aom_codec_iter_t iter = NULL;
175 	aom_codec_err_t res;
176 	aom_image_t *img;
177 	aom_img_fmt_t img_fmt;
178 	int err = 0, i;
179 
180 	if (!ves || !frame || frame->fmt != VID_FMT_YUV420P)
181 		return EINVAL;
182 
183 	if (!ves->ctxup || !vidsz_cmp(&ves->size, &frame->size)) {
184 
185 		err = open_encoder(ves, &frame->size);
186 		if (err)
187 			return err;
188 
189 		ves->size = frame->size;
190 	}
191 
192 	if (update) {
193 		/* debug("av1: picture update\n"); */
194 		flags |= AOM_EFLAG_FORCE_KF;
195 	}
196 
197 	img_fmt = AOM_IMG_FMT_I420;
198 
199 	img = aom_img_wrap(NULL, img_fmt, frame->size.w, frame->size.h,
200 			   16, NULL);
201 	if (!img) {
202 		warning("vp9: encoder: could not allocate image\n");
203 		err = ENOMEM;
204 		goto out;
205 	}
206 
207 	for (i=0; i<4; i++) {
208 		img->stride[i] = frame->linesize[i];
209 		img->planes[i] = frame->data[i];
210 	}
211 
212 	res = aom_codec_encode(&ves->ctx, img, ves->pts++, 1,
213 			       flags, AOM_DL_REALTIME);
214 	if (res) {
215 		warning("av1: enc error: %s\n", aom_codec_err_to_string(res));
216 		return ENOMEM;
217 	}
218 
219 	++ves->picid;
220 
221 	for (;;) {
222 		bool keyframe = false, marker = true;
223 		const aom_codec_cx_pkt_t *pkt;
224 		uint8_t partid = 0;
225 		uint32_t ts;
226 
227 		pkt = aom_codec_get_cx_data(&ves->ctx, &iter);
228 		if (!pkt)
229 			break;
230 
231 		if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
232 			continue;
233 
234 		if (pkt->data.frame.flags & AOM_FRAME_IS_KEY)
235 			keyframe = true;
236 
237 		if (pkt->data.frame.flags & AOM_FRAME_IS_FRAGMENT)
238 			marker = false;
239 
240 		if (pkt->data.frame.partition_id >= 0)
241 			partid = pkt->data.frame.partition_id;
242 
243 		ts = video_calc_rtp_timestamp(pkt->data.frame.pts, ves->fps);
244 
245 		err = packetize(marker, ts,
246 				pkt->data.frame.buf,
247 				pkt->data.frame.sz,
248 				ves->pktsize, !keyframe, partid, ves->picid,
249 				ves->pkth, ves->arg);
250 		if (err)
251 			return err;
252 	}
253 
254  out:
255 	if (img)
256 		aom_img_free(img);
257 
258 	return err;
259 }
260