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