-
-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathav1.c
215 lines (190 loc) · 6.42 KB
/
av1.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <aom/aom_encoder.h>
#include <aom/aomcx.h>
#include "av1.h"
#define SET_CODEC_CONTROL(ctrl, val) \
{if (aom_codec_control(ctx, ctrl, val)) return AVIF_ERROR_CODEC_INIT;}
typedef struct {
aom_img_fmt_t fmt;
int dst_c_dec_h;
int dst_c_dec_v;
int bps;
int bytes_per_sample;
} avif_format;
static avif_format convert_subsampling(const avif_subsampling subsampling) {
avif_format fmt = { 0 };
switch (subsampling) {
case AVIF_SUBSAMPLING_I420:
fmt.fmt = AOM_IMG_FMT_I420;
fmt.dst_c_dec_h = 2;
fmt.dst_c_dec_v = 2;
fmt.bps = 12;
fmt.bytes_per_sample = 1;
break;
default:
assert(0);
}
return fmt;
}
// We don't use aom_img_wrap() because it forces padding for odd picture
// sizes (c) libaom/common/y4minput.c
static void convert_frame(const avif_frame *frame, aom_image_t *aom_frame) {
memset(aom_frame, 0, sizeof(*aom_frame));
avif_format fmt = convert_subsampling(frame->subsampling);
aom_frame->fmt = fmt.fmt;
aom_frame->w = aom_frame->d_w = frame->width;
aom_frame->h = aom_frame->d_h = frame->height;
aom_frame->x_chroma_shift = fmt.dst_c_dec_h >> 1;
aom_frame->y_chroma_shift = fmt.dst_c_dec_v >> 1;
aom_frame->bps = fmt.bps;
int pic_sz = frame->width * frame->height * fmt.bytes_per_sample;
int c_w = (frame->width + fmt.dst_c_dec_h - 1) / fmt.dst_c_dec_h;
c_w *= fmt.bytes_per_sample;
int c_h = (frame->height + fmt.dst_c_dec_v - 1) / fmt.dst_c_dec_v;
int c_sz = c_w * c_h;
aom_frame->stride[AOM_PLANE_Y] = frame->width * fmt.bytes_per_sample;
aom_frame->stride[AOM_PLANE_U] = aom_frame->stride[AOM_PLANE_V] = c_w;
aom_frame->planes[AOM_PLANE_Y] = frame->data;
aom_frame->planes[AOM_PLANE_U] = frame->data + pic_sz;
aom_frame->planes[AOM_PLANE_V] = frame->data + pic_sz + c_sz;
}
static int get_frame_stats(aom_codec_ctx_t *ctx,
const aom_image_t *frame,
aom_fixed_buf_t *stats) {
if (aom_codec_encode(ctx, frame, 1/*pts*/, 1/*duration*/, 0/*flags*/))
return AVIF_ERROR_FRAME_ENCODE;
const aom_codec_cx_pkt_t *pkt = NULL;
aom_codec_iter_t iter = NULL;
int got_pkts = 0;
while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
got_pkts = 1;
if (pkt->kind == AOM_CODEC_STATS_PKT) {
const uint8_t *const pkt_buf = pkt->data.twopass_stats.buf;
const size_t pkt_size = pkt->data.twopass_stats.sz;
stats->buf = realloc(stats->buf, stats->sz + pkt_size);
memcpy((uint8_t *)stats->buf + stats->sz, pkt_buf, pkt_size);
stats->sz += pkt_size;
}
}
return got_pkts;
}
static int encode_frame(aom_codec_ctx_t *ctx,
const aom_image_t *frame,
avif_buffer *obu) {
if (aom_codec_encode(ctx, frame, 1/*pts*/, 1/*duration*/, 0/*flags*/))
return AVIF_ERROR_FRAME_ENCODE;
const aom_codec_cx_pkt_t *pkt = NULL;
aom_codec_iter_t iter = NULL;
int got_pkts = 0;
while ((pkt = aom_codec_get_cx_data(ctx, &iter)) != NULL) {
got_pkts = 1;
if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
const uint8_t *const pkt_buf = pkt->data.frame.buf;
const size_t pkt_size = pkt->data.frame.sz;
obu->buf = realloc(obu->buf, obu->sz + pkt_size);
memcpy((uint8_t *)obu->buf + obu->sz, pkt_buf, pkt_size);
obu->sz += pkt_size;
}
}
return got_pkts;
}
static avif_error init_codec(aom_codec_iface_t *iface,
aom_codec_ctx_t *ctx,
const aom_codec_enc_cfg_t *aom_cfg,
const avif_config *cfg) {
if (aom_codec_enc_init(ctx, iface, aom_cfg, 0))
return AVIF_ERROR_CODEC_INIT;
SET_CODEC_CONTROL(AOME_SET_CPUUSED, cfg->speed)
SET_CODEC_CONTROL(AOME_SET_CQ_LEVEL, cfg->quality)
if (cfg->quality == 0) {
SET_CODEC_CONTROL(AV1E_SET_LOSSLESS, 1)
}
SET_CODEC_CONTROL(AV1E_SET_FRAME_PARALLEL_DECODING, 0)
SET_CODEC_CONTROL(AV1E_SET_TILE_COLUMNS, 1)
SET_CODEC_CONTROL(AV1E_SET_TILE_ROWS, 1)
#ifdef AOM_CTRL_AV1E_SET_ROW_MT
SET_CODEC_CONTROL(AV1E_SET_ROW_MT, 1)
#endif
return AVIF_OK;
}
static avif_error do_pass1(aom_codec_ctx_t *ctx,
const aom_image_t *frame,
aom_fixed_buf_t *stats) {
avif_error res = AVIF_OK;
// Calculate frame statistics.
if ((res = get_frame_stats(ctx, frame, stats)) < 0)
goto fail;
// Flush encoder.
while ((res = get_frame_stats(ctx, NULL, stats)) > 0)
continue;
fail:
return res < 0 ? res : AVIF_OK;
}
static avif_error do_pass2(aom_codec_ctx_t *ctx,
const aom_image_t *frame,
avif_buffer *obu) {
avif_error res = AVIF_OK;
// Encode frame.
if ((res = encode_frame(ctx, frame, obu)) < 0)
goto fail;
// Flush encoder.
while ((res = encode_frame(ctx, NULL, obu)) > 0)
continue;
fail:
return res < 0 ? res : AVIF_OK;
}
avif_error avif_encode_frame(const avif_config *cfg,
const avif_frame *frame,
avif_buffer *obu) {
// Validation.
assert(cfg->threads >= 1);
assert(cfg->speed >= AVIF_MIN_SPEED && cfg->speed <= AVIF_MAX_SPEED);
assert(cfg->quality >= AVIF_MIN_QUALITY && cfg->quality <= AVIF_MAX_QUALITY);
assert(frame->width && frame->height);
// Prepare image.
aom_image_t aom_frame;
convert_frame(frame, &aom_frame);
// Setup codec.
avif_error res = AVIF_OK;
aom_codec_ctx_t codec;
aom_fixed_buf_t stats = { NULL, 0 };
aom_codec_iface_t *iface = aom_codec_av1_cx();
aom_codec_enc_cfg_t aom_cfg;
if (aom_codec_enc_config_default(iface, &aom_cfg, 0)) {
res = AVIF_ERROR_CODEC_INIT;
goto fail;
}
aom_cfg.g_limit = 1;
aom_cfg.g_w = frame->width;
aom_cfg.g_h = frame->height;
aom_cfg.g_timebase.num = 1;
aom_cfg.g_timebase.den = 24;
aom_cfg.rc_end_usage = AOM_Q;
aom_cfg.g_threads = cfg->threads;
// Pass 1.
aom_cfg.g_pass = AOM_RC_FIRST_PASS;
if ((res = init_codec(iface, &codec, &aom_cfg, cfg)))
goto fail;
if ((res = do_pass1(&codec, &aom_frame, &stats)))
goto fail;
if (aom_codec_destroy(&codec)) {
res = AVIF_ERROR_CODEC_DESTROY;
goto fail;
}
// Pass 2.
aom_cfg.g_pass = AOM_RC_LAST_PASS;
aom_cfg.rc_twopass_stats_in = stats;
if ((res = init_codec(iface, &codec, &aom_cfg, cfg)))
goto fail;
if ((res = do_pass2(&codec, &aom_frame, obu)))
goto fail;
if (aom_codec_destroy(&codec)) {
res = AVIF_ERROR_CODEC_DESTROY;
goto fail;
}
fail:
free(stats.buf);
return res;
}