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