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 #include "aom/internal/aom_codec_internal.h" 28 29 #define SAVE_STATUS(ctx, var) (ctx ? (ctx->err = var) : var) 30 31 static aom_codec_alg_priv_t *get_alg_priv(aom_codec_ctx_t *ctx) { 32 return (aom_codec_alg_priv_t *)ctx->priv; 33 } 34 35 aom_codec_err_t aom_codec_enc_init_ver(aom_codec_ctx_t *ctx, 36 aom_codec_iface_t *iface, 37 const aom_codec_enc_cfg_t *cfg, 38 aom_codec_flags_t flags, int ver) { 39 aom_codec_err_t res; 40 41 if (ver != AOM_ENCODER_ABI_VERSION) 42 res = AOM_CODEC_ABI_MISMATCH; 43 else if (!ctx || !iface || !cfg) 44 res = AOM_CODEC_INVALID_PARAM; 45 else if (iface->abi_version != AOM_CODEC_INTERNAL_ABI_VERSION) 46 res = AOM_CODEC_ABI_MISMATCH; 47 else if (!(iface->caps & AOM_CODEC_CAP_ENCODER)) 48 res = AOM_CODEC_INCAPABLE; 49 else if ((flags & AOM_CODEC_USE_PSNR) && !(iface->caps & AOM_CODEC_CAP_PSNR)) 50 res = AOM_CODEC_INCAPABLE; 51 else { 52 ctx->iface = iface; 53 ctx->name = iface->name; 54 ctx->priv = NULL; 55 ctx->init_flags = flags; 56 ctx->config.enc = cfg; 57 res = ctx->iface->init(ctx, NULL); 58 59 if (res) { 60 ctx->err_detail = ctx->priv ? ctx->priv->err_detail : NULL; 61 aom_codec_destroy(ctx); 62 } 63 } 64 65 return SAVE_STATUS(ctx, res); 66 } 67 68 aom_codec_err_t aom_codec_enc_init_multi_ver( 69 aom_codec_ctx_t *ctx, aom_codec_iface_t *iface, aom_codec_enc_cfg_t *cfg, 70 int num_enc, aom_codec_flags_t flags, aom_rational_t *dsf, int ver) { 71 aom_codec_err_t res = AOM_CODEC_OK; 72 73 if (ver != AOM_ENCODER_ABI_VERSION) 74 res = AOM_CODEC_ABI_MISMATCH; 75 else if (!ctx || !iface || !cfg || (num_enc > 16 || num_enc < 1)) 76 res = AOM_CODEC_INVALID_PARAM; 77 else if (iface->abi_version != AOM_CODEC_INTERNAL_ABI_VERSION) 78 res = AOM_CODEC_ABI_MISMATCH; 79 else if (!(iface->caps & AOM_CODEC_CAP_ENCODER)) 80 res = AOM_CODEC_INCAPABLE; 81 else if ((flags & AOM_CODEC_USE_PSNR) && !(iface->caps & AOM_CODEC_CAP_PSNR)) 82 res = AOM_CODEC_INCAPABLE; 83 else { 84 int i; 85 void *mem_loc = NULL; 86 87 if (!(res = iface->enc.mr_get_mem_loc(cfg, &mem_loc))) { 88 for (i = 0; i < num_enc; i++) { 89 aom_codec_priv_enc_mr_cfg_t mr_cfg; 90 91 /* Validate down-sampling factor. */ 92 if (dsf->num < 1 || dsf->num > 4096 || dsf->den < 1 || 93 dsf->den > dsf->num) { 94 res = AOM_CODEC_INVALID_PARAM; 95 break; 96 } 97 98 mr_cfg.mr_low_res_mode_info = mem_loc; 99 mr_cfg.mr_total_resolutions = num_enc; 100 mr_cfg.mr_encoder_id = num_enc - 1 - i; 101 mr_cfg.mr_down_sampling_factor.num = dsf->num; 102 mr_cfg.mr_down_sampling_factor.den = dsf->den; 103 104 /* Force Key-frame synchronization. Namely, encoder at higher 105 * resolution always use the same frame_type chosen by the 106 * lowest-resolution encoder. 107 */ 108 if (mr_cfg.mr_encoder_id) cfg->kf_mode = AOM_KF_DISABLED; 109 110 ctx->iface = iface; 111 ctx->name = iface->name; 112 ctx->priv = NULL; 113 ctx->init_flags = flags; 114 ctx->config.enc = cfg; 115 res = ctx->iface->init(ctx, &mr_cfg); 116 117 if (res) { 118 const char *error_detail = ctx->priv ? ctx->priv->err_detail : NULL; 119 /* Destroy current ctx */ 120 ctx->err_detail = error_detail; 121 aom_codec_destroy(ctx); 122 123 /* Destroy already allocated high-level ctx */ 124 while (i) { 125 ctx--; 126 ctx->err_detail = error_detail; 127 aom_codec_destroy(ctx); 128 i--; 129 } 130 } 131 132 if (res) break; 133 134 ctx++; 135 cfg++; 136 dsf++; 137 } 138 ctx--; 139 } 140 } 141 142 return SAVE_STATUS(ctx, res); 143 } 144 145 aom_codec_err_t aom_codec_enc_config_default(aom_codec_iface_t *iface, 146 aom_codec_enc_cfg_t *cfg, 147 unsigned int usage) { 148 aom_codec_err_t res; 149 aom_codec_enc_cfg_map_t *map; 150 int i; 151 152 if (!iface || !cfg || usage > INT_MAX) 153 res = AOM_CODEC_INVALID_PARAM; 154 else if (!(iface->caps & AOM_CODEC_CAP_ENCODER)) 155 res = AOM_CODEC_INCAPABLE; 156 else { 157 res = AOM_CODEC_INVALID_PARAM; 158 159 for (i = 0; i < iface->enc.cfg_map_count; ++i) { 160 map = iface->enc.cfg_maps + i; 161 if (map->usage == (int)usage) { 162 *cfg = map->cfg; 163 cfg->g_usage = usage; 164 res = AOM_CODEC_OK; 165 break; 166 } 167 } 168 } 169 170 /* default values */ 171 if (cfg) { 172 cfg->cfg.ext_partition = 1; 173 } 174 175 return res; 176 } 177 178 #if ARCH_X86 || ARCH_X86_64 179 /* On X86, disable the x87 unit's internal 80 bit precision for better 180 * consistency with the SSE unit's 64 bit precision. 181 */ 182 #include "aom_ports/x86.h" 183 #define FLOATING_POINT_SET_PRECISION \ 184 unsigned short x87_orig_mode = x87_set_double_precision(); 185 #define FLOATING_POINT_RESTORE_PRECISION x87_set_control_word(x87_orig_mode); 186 #else 187 #define FLOATING_POINT_SET_PRECISION 188 #define FLOATING_POINT_RESTORE_PRECISION 189 #endif // ARCH_X86 || ARCH_X86_64 190 191 #if HAVE_FEXCEPT && CONFIG_DEBUG 192 #define FLOATING_POINT_SET_EXCEPTIONS \ 193 const int float_excepts = feenableexcept(FE_DIVBYZERO); 194 #define FLOATING_POINT_RESTORE_EXCEPTIONS feenableexcept(float_excepts); 195 #else 196 #define FLOATING_POINT_SET_EXCEPTIONS 197 #define FLOATING_POINT_RESTORE_EXCEPTIONS 198 #endif // HAVE_FEXCEPT && CONFIG_DEBUG 199 200 /* clang-format off */ 201 #define FLOATING_POINT_INIT \ 202 do { \ 203 FLOATING_POINT_SET_PRECISION \ 204 FLOATING_POINT_SET_EXCEPTIONS 205 206 #define FLOATING_POINT_RESTORE \ 207 FLOATING_POINT_RESTORE_EXCEPTIONS \ 208 FLOATING_POINT_RESTORE_PRECISION \ 209 } while (0); 210 /* clang-format on */ 211 212 aom_codec_err_t aom_codec_encode(aom_codec_ctx_t *ctx, const aom_image_t *img, 213 aom_codec_pts_t pts, unsigned long duration, 214 aom_enc_frame_flags_t flags) { 215 aom_codec_err_t res = AOM_CODEC_OK; 216 217 if (!ctx || (img && !duration)) 218 res = AOM_CODEC_INVALID_PARAM; 219 else if (!ctx->iface || !ctx->priv) 220 res = AOM_CODEC_ERROR; 221 else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) 222 res = AOM_CODEC_INCAPABLE; 223 else { 224 unsigned int num_enc = ctx->priv->enc.total_encoders; 225 226 /* Execute in a normalized floating point environment, if the platform 227 * requires it. 228 */ 229 FLOATING_POINT_INIT 230 231 if (num_enc == 1) 232 res = 233 ctx->iface->enc.encode(get_alg_priv(ctx), img, pts, duration, flags); 234 else { 235 /* Multi-resolution encoding: 236 * Encode multi-levels in reverse order. For example, 237 * if mr_total_resolutions = 3, first encode level 2, 238 * then encode level 1, and finally encode level 0. 239 */ 240 int i; 241 242 ctx += num_enc - 1; 243 if (img) img += num_enc - 1; 244 245 for (i = num_enc - 1; i >= 0; i--) { 246 if ((res = ctx->iface->enc.encode(get_alg_priv(ctx), img, pts, duration, 247 flags))) 248 break; 249 250 ctx--; 251 if (img) img--; 252 } 253 ctx++; 254 } 255 256 FLOATING_POINT_RESTORE 257 } 258 259 return SAVE_STATUS(ctx, res); 260 } 261 262 const aom_codec_cx_pkt_t *aom_codec_get_cx_data(aom_codec_ctx_t *ctx, 263 aom_codec_iter_t *iter) { 264 const aom_codec_cx_pkt_t *pkt = NULL; 265 266 if (ctx) { 267 if (!iter) 268 ctx->err = AOM_CODEC_INVALID_PARAM; 269 else if (!ctx->iface || !ctx->priv) 270 ctx->err = AOM_CODEC_ERROR; 271 else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) 272 ctx->err = AOM_CODEC_INCAPABLE; 273 else 274 pkt = ctx->iface->enc.get_cx_data(get_alg_priv(ctx), iter); 275 } 276 277 if (pkt && pkt->kind == AOM_CODEC_CX_FRAME_PKT) { 278 // If the application has specified a destination area for the 279 // compressed data, and the codec has not placed the data there, 280 // and it fits, copy it. 281 aom_codec_priv_t *const priv = ctx->priv; 282 char *const dst_buf = (char *)priv->enc.cx_data_dst_buf.buf; 283 284 if (dst_buf && pkt->data.raw.buf != dst_buf && 285 pkt->data.raw.sz + priv->enc.cx_data_pad_before + 286 priv->enc.cx_data_pad_after <= 287 priv->enc.cx_data_dst_buf.sz) { 288 aom_codec_cx_pkt_t *modified_pkt = &priv->enc.cx_data_pkt; 289 290 memcpy(dst_buf + priv->enc.cx_data_pad_before, pkt->data.raw.buf, 291 pkt->data.raw.sz); 292 *modified_pkt = *pkt; 293 modified_pkt->data.raw.buf = dst_buf; 294 modified_pkt->data.raw.sz += 295 priv->enc.cx_data_pad_before + priv->enc.cx_data_pad_after; 296 pkt = modified_pkt; 297 } 298 299 if (dst_buf == pkt->data.raw.buf) { 300 priv->enc.cx_data_dst_buf.buf = dst_buf + pkt->data.raw.sz; 301 priv->enc.cx_data_dst_buf.sz -= pkt->data.raw.sz; 302 } 303 } 304 305 return pkt; 306 } 307 308 aom_codec_err_t aom_codec_set_cx_data_buf(aom_codec_ctx_t *ctx, 309 const aom_fixed_buf_t *buf, 310 unsigned int pad_before, 311 unsigned int pad_after) { 312 if (!ctx || !ctx->priv) return AOM_CODEC_INVALID_PARAM; 313 314 if (buf) { 315 ctx->priv->enc.cx_data_dst_buf = *buf; 316 ctx->priv->enc.cx_data_pad_before = pad_before; 317 ctx->priv->enc.cx_data_pad_after = pad_after; 318 } else { 319 ctx->priv->enc.cx_data_dst_buf.buf = NULL; 320 ctx->priv->enc.cx_data_dst_buf.sz = 0; 321 ctx->priv->enc.cx_data_pad_before = 0; 322 ctx->priv->enc.cx_data_pad_after = 0; 323 } 324 325 return AOM_CODEC_OK; 326 } 327 328 const aom_image_t *aom_codec_get_preview_frame(aom_codec_ctx_t *ctx) { 329 aom_image_t *img = NULL; 330 331 if (ctx) { 332 if (!ctx->iface || !ctx->priv) 333 ctx->err = AOM_CODEC_ERROR; 334 else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) 335 ctx->err = AOM_CODEC_INCAPABLE; 336 else if (!ctx->iface->enc.get_preview) 337 ctx->err = AOM_CODEC_INCAPABLE; 338 else 339 img = ctx->iface->enc.get_preview(get_alg_priv(ctx)); 340 } 341 342 return img; 343 } 344 345 aom_fixed_buf_t *aom_codec_get_global_headers(aom_codec_ctx_t *ctx) { 346 aom_fixed_buf_t *buf = NULL; 347 348 if (ctx) { 349 if (!ctx->iface || !ctx->priv) 350 ctx->err = AOM_CODEC_ERROR; 351 else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) 352 ctx->err = AOM_CODEC_INCAPABLE; 353 else if (!ctx->iface->enc.get_glob_hdrs) 354 ctx->err = AOM_CODEC_INCAPABLE; 355 else 356 buf = ctx->iface->enc.get_glob_hdrs(get_alg_priv(ctx)); 357 } 358 359 return buf; 360 } 361 362 aom_codec_err_t aom_codec_enc_config_set(aom_codec_ctx_t *ctx, 363 const aom_codec_enc_cfg_t *cfg) { 364 aom_codec_err_t res; 365 366 if (!ctx || !ctx->iface || !ctx->priv || !cfg) 367 res = AOM_CODEC_INVALID_PARAM; 368 else if (!(ctx->iface->caps & AOM_CODEC_CAP_ENCODER)) 369 res = AOM_CODEC_INCAPABLE; 370 else 371 res = ctx->iface->enc.cfg_set(get_alg_priv(ctx), cfg); 372 373 return SAVE_STATUS(ctx, res); 374 } 375 376 int aom_codec_pkt_list_add(struct aom_codec_pkt_list *list, 377 const struct aom_codec_cx_pkt *pkt) { 378 if (list->cnt < list->max) { 379 list->pkts[list->cnt++] = *pkt; 380 return 0; 381 } 382 383 return 1; 384 } 385 386 const aom_codec_cx_pkt_t *aom_codec_pkt_list_get( 387 struct aom_codec_pkt_list *list, aom_codec_iter_t *iter) { 388 const aom_codec_cx_pkt_t *pkt; 389 390 if (!(*iter)) { 391 *iter = list->pkts; 392 } 393 394 pkt = (const aom_codec_cx_pkt_t *)*iter; 395 396 if ((size_t)(pkt - list->pkts) < list->cnt) 397 *iter = pkt + 1; 398 else 399 pkt = NULL; 400 401 return pkt; 402 } 403