/src/aom/aom/src/aom_encoder.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016, Alliance for Open Media. All rights reserved. |
3 | | * |
4 | | * This source code is subject to the terms of the BSD 2 Clause License and |
5 | | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
6 | | * was not distributed with this source code in the LICENSE file, you can |
7 | | * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
8 | | * Media Patent License 1.0 was not distributed with this source code in the |
9 | | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
10 | | */ |
11 | | |
12 | | /*!\file |
13 | | * \brief Provides the high level interface to wrap encoder algorithms. |
14 | | * |
15 | | */ |
16 | | #include "config/aom_config.h" |
17 | | |
18 | | #if HAVE_FEXCEPT |
19 | | #ifndef _GNU_SOURCE |
20 | | #define _GNU_SOURCE |
21 | | #endif |
22 | | #include <fenv.h> |
23 | | #endif |
24 | | |
25 | | #include <limits.h> |
26 | | #include <stdint.h> |
27 | | #include <string.h> |
28 | | |
29 | | #include "aom/aom_encoder.h" |
30 | | #include "aom/internal/aom_codec_internal.h" |
31 | | |
32 | 0 | #define SAVE_STATUS(ctx, var) (ctx ? (ctx->err = var) : var) |
33 | | |
34 | 0 | static aom_codec_alg_priv_t *get_alg_priv(aom_codec_ctx_t *ctx) { |
35 | 0 | return (aom_codec_alg_priv_t *)ctx->priv; |
36 | 0 | } |
37 | | |
38 | | aom_codec_err_t aom_codec_enc_init_ver(aom_codec_ctx_t *ctx, |
39 | | aom_codec_iface_t *iface, |
40 | | const aom_codec_enc_cfg_t *cfg, |
41 | 0 | aom_codec_flags_t flags, int ver) { |
42 | 0 | aom_codec_err_t res; |
43 | | // The value of AOM_ENCODER_ABI_VERSION in libaom v3.0.0 and v3.1.0 - v3.1.3. |
44 | | // |
45 | | // We are compatible with these older libaom releases. AOM_ENCODER_ABI_VERSION |
46 | | // was incremented after these releases for two reasons: |
47 | | // 1. AOM_ENCODER_ABI_VERSION takes contribution from |
48 | | // AOM_EXT_PART_ABI_VERSION. The external partition API is still |
49 | | // experimental, so it should not be considered as part of the stable ABI. |
50 | | // fd9ed8366 External partition: Define APIs |
51 | | // https://aomedia-review.googlesource.com/c/aom/+/135663 |
52 | | // 2. As a way to detect the presence of speeds 7-9 in all-intra mode. I (wtc) |
53 | | // suggested this change because I misunderstood how |
54 | | // AOM_ENCODER_ABI_VERSION was used. |
55 | | // bbdfa68d1 AllIntra: Redefine all-intra mode speed features for speed 7+ |
56 | | // https://aomedia-review.googlesource.com/c/aom/+/140624 |
57 | 0 | const int aom_encoder_abi_version_25 = 25; |
58 | | |
59 | | // TODO(bug aomedia:3228): Remove the check for aom_encoder_abi_version_25 in |
60 | | // libaom v4.0.0. |
61 | 0 | if (ver != AOM_ENCODER_ABI_VERSION && ver != aom_encoder_abi_version_25) |
62 | 0 | res = AOM_CODEC_ABI_MISMATCH; |
63 | 0 | else if (!ctx || !iface || !cfg) |
64 | 0 | res = AOM_CODEC_INVALID_PARAM; |
65 | 0 | else if (iface->abi_version != AOM_CODEC_INTERNAL_ABI_VERSION) |
66 | 0 | res = AOM_CODEC_ABI_MISMATCH; |
67 | 0 | else if (!(iface->caps & AOM_CODEC_CAP_ENCODER)) |
68 | 0 | res = AOM_CODEC_INCAPABLE; |
69 | 0 | else if ((flags & AOM_CODEC_USE_PSNR) && !(iface->caps & AOM_CODEC_CAP_PSNR)) |
70 | 0 | res = AOM_CODEC_INCAPABLE; |
71 | 0 | else if ((flags & AOM_CODEC_USE_HIGHBITDEPTH) && |
72 | 0 | !(iface->caps & AOM_CODEC_CAP_HIGHBITDEPTH)) { |
73 | 0 | res = AOM_CODEC_INCAPABLE; |
74 | 0 | } else if (cfg->g_bit_depth > 8 && |
75 | 0 | (flags & AOM_CODEC_USE_HIGHBITDEPTH) == 0) { |
76 | 0 | res = AOM_CODEC_INVALID_PARAM; |
77 | 0 | ctx->err_detail = |
78 | 0 | "High bit-depth used without the AOM_CODEC_USE_HIGHBITDEPTH flag."; |
79 | 0 | } else { |
80 | 0 | ctx->iface = iface; |
81 | 0 | ctx->name = iface->name; |
82 | 0 | ctx->priv = NULL; |
83 | 0 | ctx->init_flags = flags; |
84 | 0 | ctx->config.enc = cfg; |
85 | 0 | res = ctx->iface->init(ctx); |
86 | |
|
87 | 0 | if (res) { |
88 | | // IMPORTANT: ctx->priv->err_detail must be null or point to a string |
89 | | // that remains valid after ctx->priv is destroyed, such as a C string |
90 | | // literal. This makes it safe to call aom_codec_error_detail() after |
91 | | // aom_codec_enc_init_ver() failed. |
92 | 0 | ctx->err_detail = ctx->priv ? ctx->priv->err_detail : NULL; |
93 | 0 | aom_codec_destroy(ctx); |
94 | 0 | } |
95 | 0 | } |
96 | |
|
97 | 0 | return SAVE_STATUS(ctx, res); |
98 | 0 | } |
99 | | |
100 | | aom_codec_err_t aom_codec_enc_config_default(aom_codec_iface_t *iface, |
101 | | aom_codec_enc_cfg_t *cfg, |
102 | 0 | unsigned int usage) { |
103 | 0 | aom_codec_err_t res; |
104 | |
|
105 | 0 | if (!iface || !cfg) |
106 | 0 | res = AOM_CODEC_INVALID_PARAM; |
107 | 0 | else if (!(iface->caps & AOM_CODEC_CAP_ENCODER)) |
108 | 0 | res = AOM_CODEC_INCAPABLE; |
109 | 0 | else { |
110 | 0 | res = AOM_CODEC_INVALID_PARAM; |
111 | |
|
112 | 0 | for (int i = 0; i < iface->enc.cfg_count; ++i) { |
113 | 0 | if (iface->enc.cfgs[i].g_usage == usage) { |
114 | 0 | *cfg = iface->enc.cfgs[i]; |
115 | 0 | res = AOM_CODEC_OK; |
116 | | /* default values */ |
117 | 0 | memset(&cfg->encoder_cfg, 0, sizeof(cfg->encoder_cfg)); |
118 | 0 | cfg->encoder_cfg.super_block_size = 0; // Dynamic |
119 | 0 | cfg->encoder_cfg.max_partition_size = 128; |
120 | 0 | cfg->encoder_cfg.min_partition_size = 4; |
121 | 0 | cfg->encoder_cfg.disable_trellis_quant = 3; |
122 | 0 | break; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | } |
126 | 0 | return res; |
127 | 0 | } |
128 | | |
129 | | #if AOM_ARCH_X86 || AOM_ARCH_X86_64 |
130 | | /* On X86, disable the x87 unit's internal 80 bit precision for better |
131 | | * consistency with the SSE unit's 64 bit precision. |
132 | | */ |
133 | | #include "aom_ports/x86.h" |
134 | | #define FLOATING_POINT_SET_PRECISION \ |
135 | | unsigned short x87_orig_mode = x87_set_double_precision(); |
136 | | #define FLOATING_POINT_RESTORE_PRECISION x87_set_control_word(x87_orig_mode); |
137 | | #else |
138 | | #define FLOATING_POINT_SET_PRECISION |
139 | | #define FLOATING_POINT_RESTORE_PRECISION |
140 | | #endif // AOM_ARCH_X86 || AOM_ARCH_X86_64 |
141 | | |
142 | | #if HAVE_FEXCEPT && CONFIG_DEBUG |
143 | | #define FLOATING_POINT_SET_EXCEPTIONS \ |
144 | 0 | const int float_excepts = \ |
145 | 0 | feenableexcept(FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW); |
146 | | #define FLOATING_POINT_RESTORE_EXCEPTIONS \ |
147 | 0 | if (float_excepts != -1) { \ |
148 | 0 | fedisableexcept(FE_ALL_EXCEPT); \ |
149 | 0 | feenableexcept(float_excepts); \ |
150 | 0 | } |
151 | | #else |
152 | | #define FLOATING_POINT_SET_EXCEPTIONS |
153 | | #define FLOATING_POINT_RESTORE_EXCEPTIONS |
154 | | #endif // HAVE_FEXCEPT && CONFIG_DEBUG |
155 | | |
156 | | /* clang-format off */ |
157 | | #define FLOATING_POINT_INIT \ |
158 | 0 | do { \ |
159 | 0 | FLOATING_POINT_SET_PRECISION \ |
160 | 0 | FLOATING_POINT_SET_EXCEPTIONS |
161 | | |
162 | | #define FLOATING_POINT_RESTORE \ |
163 | 0 | FLOATING_POINT_RESTORE_EXCEPTIONS \ |
164 | 0 | FLOATING_POINT_RESTORE_PRECISION \ |
165 | 0 | } while (0); |
166 | | /* clang-format on */ |
167 | | |
168 | | aom_codec_err_t aom_codec_encode(aom_codec_ctx_t *ctx, const aom_image_t *img, |
169 | | aom_codec_pts_t pts, unsigned long duration, |
170 | 0 | aom_enc_frame_flags_t flags) { |
171 | 0 | aom_codec_err_t res = AOM_CODEC_OK; |
172 | |
|
173 | 0 | if (!ctx || (img && !duration)) |
174 | 0 | res = AOM_CODEC_INVALID_PARAM; |
175 | 0 | else if (!ctx->iface || !ctx->priv) |
176 | 0 | res = AOM_CODEC_ERROR; |
177 | 0 | else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) |
178 | 0 | res = AOM_CODEC_INCAPABLE; |
179 | 0 | else if (img && ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) != 0) != |
180 | 0 | ((ctx->init_flags & AOM_CODEC_USE_HIGHBITDEPTH) != 0)) { |
181 | 0 | res = AOM_CODEC_INVALID_PARAM; |
182 | 0 | #if ULONG_MAX > UINT32_MAX |
183 | 0 | } else if (duration > UINT32_MAX) { |
184 | 0 | res = AOM_CODEC_INVALID_PARAM; |
185 | 0 | #endif |
186 | 0 | } else { |
187 | | /* Execute in a normalized floating point environment, if the platform |
188 | | * requires it. |
189 | | */ |
190 | 0 | FLOATING_POINT_INIT |
191 | 0 | res = ctx->iface->enc.encode(get_alg_priv(ctx), img, pts, duration, flags); |
192 | 0 | FLOATING_POINT_RESTORE |
193 | 0 | } |
194 | |
|
195 | 0 | return SAVE_STATUS(ctx, res); |
196 | 0 | } |
197 | | |
198 | | const aom_codec_cx_pkt_t *aom_codec_get_cx_data(aom_codec_ctx_t *ctx, |
199 | 0 | aom_codec_iter_t *iter) { |
200 | 0 | const aom_codec_cx_pkt_t *pkt = NULL; |
201 | |
|
202 | 0 | if (ctx) { |
203 | 0 | if (!iter) |
204 | 0 | ctx->err = AOM_CODEC_INVALID_PARAM; |
205 | 0 | else if (!ctx->iface || !ctx->priv) |
206 | 0 | ctx->err = AOM_CODEC_ERROR; |
207 | 0 | else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) |
208 | 0 | ctx->err = AOM_CODEC_INCAPABLE; |
209 | 0 | else |
210 | 0 | pkt = ctx->iface->enc.get_cx_data(get_alg_priv(ctx), iter); |
211 | 0 | } |
212 | |
|
213 | 0 | if (pkt && pkt->kind == AOM_CODEC_CX_FRAME_PKT) { |
214 | | // If the application has specified a destination area for the |
215 | | // compressed data, and the codec has not placed the data there, |
216 | | // and it fits, copy it. |
217 | 0 | aom_codec_priv_t *const priv = ctx->priv; |
218 | 0 | char *const dst_buf = (char *)priv->enc.cx_data_dst_buf.buf; |
219 | |
|
220 | 0 | if (dst_buf && pkt->data.raw.buf != dst_buf && |
221 | 0 | pkt->data.raw.sz + priv->enc.cx_data_pad_before + |
222 | 0 | priv->enc.cx_data_pad_after <= |
223 | 0 | priv->enc.cx_data_dst_buf.sz) { |
224 | 0 | aom_codec_cx_pkt_t *modified_pkt = &priv->enc.cx_data_pkt; |
225 | |
|
226 | 0 | memcpy(dst_buf + priv->enc.cx_data_pad_before, pkt->data.raw.buf, |
227 | 0 | pkt->data.raw.sz); |
228 | 0 | *modified_pkt = *pkt; |
229 | 0 | modified_pkt->data.raw.buf = dst_buf; |
230 | 0 | modified_pkt->data.raw.sz += |
231 | 0 | priv->enc.cx_data_pad_before + priv->enc.cx_data_pad_after; |
232 | 0 | pkt = modified_pkt; |
233 | 0 | } |
234 | |
|
235 | 0 | if (dst_buf == pkt->data.raw.buf) { |
236 | 0 | priv->enc.cx_data_dst_buf.buf = dst_buf + pkt->data.raw.sz; |
237 | 0 | priv->enc.cx_data_dst_buf.sz -= pkt->data.raw.sz; |
238 | 0 | } |
239 | 0 | } |
240 | |
|
241 | 0 | return pkt; |
242 | 0 | } |
243 | | |
244 | | aom_codec_err_t aom_codec_set_cx_data_buf(aom_codec_ctx_t *ctx, |
245 | | const aom_fixed_buf_t *buf, |
246 | | unsigned int pad_before, |
247 | 0 | unsigned int pad_after) { |
248 | 0 | if (!ctx || !ctx->priv) return AOM_CODEC_INVALID_PARAM; |
249 | | |
250 | 0 | if (buf) { |
251 | 0 | ctx->priv->enc.cx_data_dst_buf = *buf; |
252 | 0 | ctx->priv->enc.cx_data_pad_before = pad_before; |
253 | 0 | ctx->priv->enc.cx_data_pad_after = pad_after; |
254 | 0 | } else { |
255 | 0 | ctx->priv->enc.cx_data_dst_buf.buf = NULL; |
256 | 0 | ctx->priv->enc.cx_data_dst_buf.sz = 0; |
257 | 0 | ctx->priv->enc.cx_data_pad_before = 0; |
258 | 0 | ctx->priv->enc.cx_data_pad_after = 0; |
259 | 0 | } |
260 | |
|
261 | 0 | return AOM_CODEC_OK; |
262 | 0 | } |
263 | | |
264 | 0 | const aom_image_t *aom_codec_get_preview_frame(aom_codec_ctx_t *ctx) { |
265 | 0 | aom_image_t *img = NULL; |
266 | |
|
267 | 0 | if (ctx) { |
268 | 0 | if (!ctx->iface || !ctx->priv) |
269 | 0 | ctx->err = AOM_CODEC_ERROR; |
270 | 0 | else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) |
271 | 0 | ctx->err = AOM_CODEC_INCAPABLE; |
272 | 0 | else if (!ctx->iface->enc.get_preview) |
273 | 0 | ctx->err = AOM_CODEC_INCAPABLE; |
274 | 0 | else |
275 | 0 | img = ctx->iface->enc.get_preview(get_alg_priv(ctx)); |
276 | 0 | } |
277 | |
|
278 | 0 | return img; |
279 | 0 | } |
280 | | |
281 | 0 | aom_fixed_buf_t *aom_codec_get_global_headers(aom_codec_ctx_t *ctx) { |
282 | 0 | aom_fixed_buf_t *buf = NULL; |
283 | |
|
284 | 0 | if (ctx) { |
285 | 0 | if (!ctx->iface || !ctx->priv) |
286 | 0 | ctx->err = AOM_CODEC_ERROR; |
287 | 0 | else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) |
288 | 0 | ctx->err = AOM_CODEC_INCAPABLE; |
289 | 0 | else if (!ctx->iface->enc.get_glob_hdrs) |
290 | 0 | ctx->err = AOM_CODEC_INCAPABLE; |
291 | 0 | else |
292 | 0 | buf = ctx->iface->enc.get_glob_hdrs(get_alg_priv(ctx)); |
293 | 0 | } |
294 | |
|
295 | 0 | return buf; |
296 | 0 | } |
297 | | |
298 | | aom_codec_err_t aom_codec_enc_config_set(aom_codec_ctx_t *ctx, |
299 | 0 | const aom_codec_enc_cfg_t *cfg) { |
300 | 0 | aom_codec_err_t res; |
301 | |
|
302 | 0 | if (!ctx || !ctx->iface || !ctx->priv || !cfg) |
303 | 0 | res = AOM_CODEC_INVALID_PARAM; |
304 | 0 | else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) |
305 | 0 | res = AOM_CODEC_INCAPABLE; |
306 | 0 | else |
307 | 0 | res = ctx->iface->enc.cfg_set(get_alg_priv(ctx), cfg); |
308 | |
|
309 | 0 | return SAVE_STATUS(ctx, res); |
310 | 0 | } |
311 | | |
312 | | int aom_codec_pkt_list_add(struct aom_codec_pkt_list *list, |
313 | 0 | const struct aom_codec_cx_pkt *pkt) { |
314 | 0 | if (list->cnt < list->max) { |
315 | 0 | list->pkts[list->cnt++] = *pkt; |
316 | 0 | return 0; |
317 | 0 | } |
318 | | |
319 | 0 | return 1; |
320 | 0 | } |
321 | | |
322 | | const aom_codec_cx_pkt_t *aom_codec_pkt_list_get( |
323 | 0 | struct aom_codec_pkt_list *list, aom_codec_iter_t *iter) { |
324 | 0 | const aom_codec_cx_pkt_t *pkt; |
325 | |
|
326 | 0 | if (!(*iter)) { |
327 | 0 | *iter = list->pkts; |
328 | 0 | } |
329 | |
|
330 | 0 | pkt = (const aom_codec_cx_pkt_t *)*iter; |
331 | |
|
332 | 0 | if ((size_t)(pkt - list->pkts) < list->cnt) |
333 | 0 | *iter = pkt + 1; |
334 | 0 | else |
335 | 0 | pkt = NULL; |
336 | |
|
337 | 0 | return pkt; |
338 | 0 | } |