svc: Remove non working svc code tests and examples.
This code is calling unimplemented code in av1, remove. If necessary we can port from vp9 when implemented in av1. Change-Id: I49a12820201eda9c7ccd46224fda26a75288b85d
This commit is contained in:
Родитель
c0bf1dd1b2
Коммит
2c82d97b5c
|
@ -1,651 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* AV1 SVC encoding support via libaom
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define AOM_DISABLE_CTRL_TYPECHECKS 1
|
||||
#include "./aom_config.h"
|
||||
#include "aom/svc_context.h"
|
||||
#include "aom/aomcx.h"
|
||||
#include "aom/aom_encoder.h"
|
||||
#include "aom_mem/aom_mem.h"
|
||||
#include "av1/common/onyxc_int.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define strtok_r strtok_s
|
||||
#ifndef MINGW_HAS_SECURE_API
|
||||
// proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
|
||||
_CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
|
||||
#endif /* MINGW_HAS_SECURE_API */
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strdup _strdup
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
|
||||
#define SVC_REFERENCE_FRAMES 8
|
||||
#define SUPERFRAME_SLOTS (8)
|
||||
#define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
|
||||
|
||||
#define MAX_QUANTIZER 63
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_NUM[AOM_SS_MAX_LAYERS] = { 4, 5, 7, 11,
|
||||
16 };
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_DEN[AOM_SS_MAX_LAYERS] = { 16, 16, 16,
|
||||
16, 16 };
|
||||
|
||||
typedef enum {
|
||||
QUANTIZER = 0,
|
||||
BITRATE,
|
||||
SCALE_FACTOR,
|
||||
AUTO_ALT_REF,
|
||||
ALL_OPTION_TYPES
|
||||
} LAYER_OPTION_TYPE;
|
||||
|
||||
static const int option_max_values[ALL_OPTION_TYPES] = { 63, INT_MAX, INT_MAX,
|
||||
1 };
|
||||
|
||||
static const int option_min_values[ALL_OPTION_TYPES] = { 0, 0, 1, 0 };
|
||||
|
||||
// One encoded frame
|
||||
typedef struct FrameData {
|
||||
void *buf; // compressed data buffer
|
||||
size_t size; // length of compressed data
|
||||
aom_codec_frame_flags_t flags; /**< flags for this frame */
|
||||
struct FrameData *next;
|
||||
} FrameData;
|
||||
|
||||
static SvcInternal_t *get_svc_internal(SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
if (svc_ctx->internal == NULL) {
|
||||
SvcInternal_t *const si = (SvcInternal_t *)malloc(sizeof(*si));
|
||||
if (si != NULL) {
|
||||
memset(si, 0, sizeof(*si));
|
||||
}
|
||||
svc_ctx->internal = si;
|
||||
}
|
||||
return (SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static const SvcInternal_t *get_const_svc_internal(const SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
return (const SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static void svc_log_reset(SvcContext *svc_ctx) {
|
||||
SvcInternal_t *const si = (SvcInternal_t *)svc_ctx->internal;
|
||||
si->message_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level, const char *fmt,
|
||||
...) {
|
||||
char buf[512];
|
||||
int retval = 0;
|
||||
va_list ap;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
|
||||
if (level > svc_ctx->log_level) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
retval = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (svc_ctx->log_print) {
|
||||
printf("%s", buf);
|
||||
} else {
|
||||
strncat(si->message_buffer, buf,
|
||||
sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
|
||||
}
|
||||
|
||||
if (level == SVC_LOG_ERROR) {
|
||||
si->codec_ctx->err_detail = si->message_buffer;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static aom_codec_err_t extract_option(LAYER_OPTION_TYPE type, char *input,
|
||||
int *value0, int *value1) {
|
||||
if (type == SCALE_FACTOR) {
|
||||
*value0 = strtol(input, &input, 10);
|
||||
if (*input++ != '/') return AOM_CODEC_INVALID_PARAM;
|
||||
*value1 = strtol(input, &input, 10);
|
||||
|
||||
if (*value0 < option_min_values[SCALE_FACTOR] ||
|
||||
*value1 < option_min_values[SCALE_FACTOR] ||
|
||||
*value0 > option_max_values[SCALE_FACTOR] ||
|
||||
*value1 > option_max_values[SCALE_FACTOR] ||
|
||||
*value0 > *value1) // num shouldn't be greater than den
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
} else {
|
||||
*value0 = atoi(input);
|
||||
if (*value0 < option_min_values[type] || *value0 > option_max_values[type])
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
return AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
static aom_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
|
||||
LAYER_OPTION_TYPE type,
|
||||
const char *input,
|
||||
int *option0,
|
||||
int *option1) {
|
||||
int i;
|
||||
aom_codec_err_t res = AOM_CODEC_OK;
|
||||
char *input_string;
|
||||
char *token;
|
||||
const char *delim = ",";
|
||||
char *save_ptr;
|
||||
|
||||
if (input == NULL || option0 == NULL ||
|
||||
(option1 == NULL && type == SCALE_FACTOR))
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
|
||||
input_string = strdup(input);
|
||||
token = strtok_r(input_string, delim, &save_ptr);
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (token != NULL) {
|
||||
res = extract_option(type, token, option0 + i, option1 + i);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
token = strtok_r(NULL, delim, &save_ptr);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res == AOM_CODEC_OK && i != svc_ctx->spatial_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: layer params type: %d %d values required, "
|
||||
"but only %d specified\n",
|
||||
type, svc_ctx->spatial_layers, i);
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
free(input_string);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SVC encoding options
|
||||
* Format: encoding-mode=<svc_mode>,layers=<layer_count>
|
||||
* scale-factors=<n1>/<d1>,<n2>/<d2>,...
|
||||
* quantizers=<q1>,<q2>,...
|
||||
* svc_mode = [i|ip|alt_ip|gf]
|
||||
*/
|
||||
static aom_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
|
||||
char *input_string;
|
||||
char *option_name;
|
||||
char *option_value;
|
||||
char *input_ptr;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
aom_codec_err_t res = AOM_CODEC_OK;
|
||||
int i, alt_ref_enabled = 0;
|
||||
|
||||
if (options == NULL) return AOM_CODEC_OK;
|
||||
input_string = strdup(options);
|
||||
|
||||
// parse option name
|
||||
option_name = strtok_r(input_string, "=", &input_ptr);
|
||||
while (option_name != NULL) {
|
||||
// parse option value
|
||||
option_value = strtok_r(NULL, " ", &input_ptr);
|
||||
if (option_value == NULL) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
|
||||
option_name);
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
if (strcmp("spatial-layers", option_name) == 0) {
|
||||
svc_ctx->spatial_layers = atoi(option_value);
|
||||
} else if (strcmp("temporal-layers", option_name) == 0) {
|
||||
svc_ctx->temporal_layers = atoi(option_value);
|
||||
} else if (strcmp("scale-factors", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
|
||||
si->svc_params.scaling_factor_num,
|
||||
si->svc_params.scaling_factor_den);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
} else if (strcmp("max-quantizers", option_name) == 0) {
|
||||
res =
|
||||
parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.max_quantizers, NULL);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
} else if (strcmp("min-quantizers", option_name) == 0) {
|
||||
res =
|
||||
parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.min_quantizers, NULL);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
} else if (strcmp("auto-alt-refs", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
|
||||
si->enable_auto_alt_ref, NULL);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
} else if (strcmp("bitrates", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
|
||||
si->bitrates, NULL);
|
||||
if (res != AOM_CODEC_OK) break;
|
||||
} else if (strcmp("multi-frame-contexts", option_name) == 0) {
|
||||
si->use_multiple_frame_contexts = atoi(option_value);
|
||||
} else {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
option_name = strtok_r(NULL, "=", &input_ptr);
|
||||
}
|
||||
free(input_string);
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER ||
|
||||
si->svc_params.max_quantizers[i] < 0 ||
|
||||
si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] ||
|
||||
si->svc_params.min_quantizers[i] < 0)
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (si->use_multiple_frame_contexts &&
|
||||
(svc_ctx->spatial_layers > 3 ||
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
alt_ref_enabled += si->enable_auto_alt_ref[i];
|
||||
if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
|
||||
"enabled auto alt reference frame, but % layers are enabled\n",
|
||||
REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
|
||||
res = AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
aom_codec_err_t aom_svc_set_options(SvcContext *svc_ctx, const char *options) {
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || options == NULL || si == NULL) {
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
strncpy(si->options, options, sizeof(si->options));
|
||||
si->options[sizeof(si->options) - 1] = '\0';
|
||||
return AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
void assign_layer_bitrates(const SvcContext *svc_ctx,
|
||||
aom_codec_enc_cfg_t *const enc_cfg) {
|
||||
int i;
|
||||
const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
|
||||
int sl, tl, spatial_layer_target;
|
||||
|
||||
if (svc_ctx->temporal_layering_mode != 0) {
|
||||
if (si->bitrates[0] != 0) {
|
||||
enc_cfg->rc_target_bitrate = 0;
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
enc_cfg->ss_target_bitrate[sl * svc_ctx->temporal_layers] = 0;
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
enc_cfg->ss_target_bitrate[sl * svc_ctx->temporal_layers] +=
|
||||
(unsigned int)si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + tl] =
|
||||
si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[AOM_MAX_LAYERS] = { 0 };
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
if (si->svc_params.scaling_factor_den[sl] > 0) {
|
||||
alloc_ratio[sl] =
|
||||
(float)(si->svc_params.scaling_factor_num[sl] * 1.0 /
|
||||
si->svc_params.scaling_factor_den[sl]);
|
||||
total += alloc_ratio[sl];
|
||||
}
|
||||
}
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
enc_cfg->ss_target_bitrate[sl] = spatial_layer_target =
|
||||
(unsigned int)(enc_cfg->rc_target_bitrate * alloc_ratio[sl] /
|
||||
total);
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
spatial_layer_target >> 1;
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
(spatial_layer_target >> 1) + (spatial_layer_target >> 2);
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 2] =
|
||||
spatial_layer_target;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
spatial_layer_target * 2 / 3;
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
spatial_layer_target;
|
||||
} else {
|
||||
// User should explicitly assign bitrates in this case.
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (si->bitrates[0] != 0) {
|
||||
enc_cfg->rc_target_bitrate = 0;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
|
||||
enc_cfg->rc_target_bitrate += si->bitrates[i];
|
||||
}
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[AOM_MAX_LAYERS] = { 0 };
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.scaling_factor_den[i] > 0) {
|
||||
alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 /
|
||||
si->svc_params.scaling_factor_den[i]);
|
||||
|
||||
alloc_ratio[i] *= alloc_ratio[i];
|
||||
total += alloc_ratio[i];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AOM_SS_MAX_LAYERS; ++i) {
|
||||
if (total > 0) {
|
||||
enc_cfg->layer_target_bitrate[i] =
|
||||
(unsigned int)(enc_cfg->rc_target_bitrate * alloc_ratio[i] /
|
||||
total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aom_codec_err_t aom_svc_init(SvcContext *svc_ctx, aom_codec_ctx_t *codec_ctx,
|
||||
aom_codec_iface_t *iface,
|
||||
aom_codec_enc_cfg_t *enc_cfg) {
|
||||
aom_codec_err_t res;
|
||||
int i, sl, tl;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
|
||||
enc_cfg == NULL) {
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
if (si == NULL) return AOM_CODEC_MEM_ERROR;
|
||||
|
||||
si->codec_ctx = codec_ctx;
|
||||
|
||||
si->width = enc_cfg->g_w;
|
||||
si->height = enc_cfg->g_h;
|
||||
|
||||
if (enc_cfg->kf_max_dist < 2) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
|
||||
enc_cfg->kf_max_dist);
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
si->kf_dist = enc_cfg->kf_max_dist;
|
||||
|
||||
if (svc_ctx->spatial_layers == 0)
|
||||
svc_ctx->spatial_layers = AOM_SS_DEFAULT_LAYERS;
|
||||
if (svc_ctx->spatial_layers < 1 ||
|
||||
svc_ctx->spatial_layers > AOM_SS_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
|
||||
svc_ctx->spatial_layers);
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Note: temporal_layering_mode only applies to one-pass CBR
|
||||
// si->svc_params.temporal_layering_mode = svc_ctx->temporal_layering_mode;
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
svc_ctx->temporal_layers = 3;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
svc_ctx->temporal_layers = 2;
|
||||
}
|
||||
|
||||
for (sl = 0; sl < AOM_SS_MAX_LAYERS; ++sl) {
|
||||
si->svc_params.scaling_factor_num[sl] = DEFAULT_SCALE_FACTORS_NUM[sl];
|
||||
si->svc_params.scaling_factor_den[sl] = DEFAULT_SCALE_FACTORS_DEN[sl];
|
||||
}
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
i = sl * svc_ctx->temporal_layers + tl;
|
||||
si->svc_params.max_quantizers[i] = MAX_QUANTIZER;
|
||||
si->svc_params.min_quantizers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse aggregate command line options. Options must start with
|
||||
// "layers=xx" then followed by other options
|
||||
res = parse_options(svc_ctx, si->options);
|
||||
if (res != AOM_CODEC_OK) return res;
|
||||
|
||||
if (svc_ctx->spatial_layers < 1) svc_ctx->spatial_layers = 1;
|
||||
if (svc_ctx->spatial_layers > AOM_SS_MAX_LAYERS)
|
||||
svc_ctx->spatial_layers = AOM_SS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers < 1) svc_ctx->temporal_layers = 1;
|
||||
if (svc_ctx->temporal_layers > AOM_TS_MAX_LAYERS)
|
||||
svc_ctx->temporal_layers = AOM_TS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers * svc_ctx->spatial_layers > AOM_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"spatial layers * temporal layers exceeds the maximum number of "
|
||||
"allowed layers of %d\n",
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers,
|
||||
(int)AOM_MAX_LAYERS);
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
assign_layer_bitrates(svc_ctx, enc_cfg);
|
||||
|
||||
#if CONFIG_SPATIAL_SVC
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
|
||||
#endif
|
||||
|
||||
if (svc_ctx->temporal_layers > 1) {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->temporal_layers; ++i) {
|
||||
enc_cfg->ts_target_bitrate[i] =
|
||||
enc_cfg->rc_target_bitrate / svc_ctx->temporal_layers;
|
||||
enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
|
||||
}
|
||||
}
|
||||
|
||||
if (svc_ctx->threads) enc_cfg->g_threads = svc_ctx->threads;
|
||||
|
||||
// Modify encoder configuration
|
||||
enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
|
||||
enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
|
||||
|
||||
if (enc_cfg->rc_end_usage == AOM_CBR) {
|
||||
enc_cfg->rc_resize_allowed = 0;
|
||||
enc_cfg->rc_min_quantizer = 2;
|
||||
enc_cfg->rc_max_quantizer = 56;
|
||||
enc_cfg->rc_undershoot_pct = 50;
|
||||
enc_cfg->rc_overshoot_pct = 50;
|
||||
enc_cfg->rc_buf_initial_sz = 500;
|
||||
enc_cfg->rc_buf_optimal_sz = 600;
|
||||
enc_cfg->rc_buf_sz = 1000;
|
||||
enc_cfg->rc_dropframe_thresh = 0;
|
||||
}
|
||||
|
||||
if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
|
||||
enc_cfg->g_error_resilient = 1;
|
||||
|
||||
// Initialize codec
|
||||
res = aom_codec_enc_init(codec_ctx, iface, enc_cfg, AOM_CODEC_USE_PSNR);
|
||||
if (res != AOM_CODEC_OK) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
|
||||
return res;
|
||||
}
|
||||
if (svc_ctx->spatial_layers > 1 || svc_ctx->temporal_layers > 1) {
|
||||
aom_codec_control(codec_ctx, AV1E_SET_SVC, 1);
|
||||
aom_codec_control(codec_ctx, AV1E_SET_SVC_PARAMETERS, &si->svc_params);
|
||||
}
|
||||
return AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a frame into multiple layers
|
||||
* Create a superframe containing the individual layers
|
||||
*/
|
||||
aom_codec_err_t aom_svc_encode(SvcContext *svc_ctx, aom_codec_ctx_t *codec_ctx,
|
||||
struct aom_image *rawimg, aom_codec_pts_t pts,
|
||||
int64_t duration, int deadline) {
|
||||
aom_codec_err_t res;
|
||||
aom_codec_iter_t iter;
|
||||
const aom_codec_cx_pkt_t *cx_pkt;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
|
||||
return AOM_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
svc_log_reset(svc_ctx);
|
||||
|
||||
res =
|
||||
aom_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0, deadline);
|
||||
if (res != AOM_CODEC_OK) {
|
||||
return res;
|
||||
}
|
||||
// save compressed data
|
||||
iter = NULL;
|
||||
while ((cx_pkt = aom_codec_get_cx_data(codec_ctx, &iter))) {
|
||||
switch (cx_pkt->kind) {
|
||||
#if AOM_ENCODER_ABI_VERSION > (5 + AOM_CODEC_ABI_VERSION)
|
||||
#if CONFIG_SPATIAL_SVC
|
||||
case AOM_CODEC_SPATIAL_SVC_LAYER_PSNR: {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
int j;
|
||||
svc_log(svc_ctx, SVC_LOG_DEBUG,
|
||||
"SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
|
||||
"%2.3f %2.3f %2.3f %2.3f \n",
|
||||
si->psnr_pkt_received, i, cx_pkt->data.layer_psnr[i].psnr[0],
|
||||
cx_pkt->data.layer_psnr[i].psnr[1],
|
||||
cx_pkt->data.layer_psnr[i].psnr[2],
|
||||
cx_pkt->data.layer_psnr[i].psnr[3]);
|
||||
svc_log(svc_ctx, SVC_LOG_DEBUG,
|
||||
"SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
|
||||
"%2.3f %2.3f %2.3f %2.3f \n",
|
||||
si->psnr_pkt_received, i, cx_pkt->data.layer_psnr[i].sse[0],
|
||||
cx_pkt->data.layer_psnr[i].sse[1],
|
||||
cx_pkt->data.layer_psnr[i].sse[2],
|
||||
cx_pkt->data.layer_psnr[i].sse[3]);
|
||||
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[i][j] += cx_pkt->data.layer_psnr[i].psnr[j];
|
||||
si->sse_sum[i][j] += cx_pkt->data.layer_psnr[i].sse[j];
|
||||
}
|
||||
}
|
||||
++si->psnr_pkt_received;
|
||||
break;
|
||||
}
|
||||
case AOM_CODEC_SPATIAL_SVC_LAYER_SIZES: {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
||||
return AOM_CODEC_OK;
|
||||
}
|
||||
|
||||
const char *aom_svc_get_message(const SvcContext *svc_ctx) {
|
||||
const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || si == NULL) return NULL;
|
||||
return si->message_buffer;
|
||||
}
|
||||
|
||||
static double calc_psnr(double d) {
|
||||
if (d == 0) return 100;
|
||||
return -10.0 * log(d) / log(10.0);
|
||||
}
|
||||
|
||||
// dump accumulated statistics and reset accumulated values
|
||||
const char *aom_svc_dump_statistics(SvcContext *svc_ctx) {
|
||||
int number_of_frames;
|
||||
int i, j;
|
||||
uint32_t bytes_total = 0;
|
||||
double scale[COMPONENTS];
|
||||
double psnr[COMPONENTS];
|
||||
double mse[COMPONENTS];
|
||||
double y_scale;
|
||||
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || si == NULL) return NULL;
|
||||
|
||||
svc_log_reset(svc_ctx);
|
||||
|
||||
number_of_frames = si->psnr_pkt_received;
|
||||
if (number_of_frames <= 0) return aom_svc_get_message(svc_ctx);
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "\n");
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
|
||||
i, (double)si->psnr_sum[i][0] / number_of_frames,
|
||||
(double)si->psnr_sum[i][1] / number_of_frames,
|
||||
(double)si->psnr_sum[i][2] / number_of_frames,
|
||||
(double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
|
||||
// the following psnr calculation is deduced from ffmpeg.c#print_report
|
||||
y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
|
||||
scale[1] = y_scale;
|
||||
scale[2] = scale[3] = y_scale / 4; // U or V
|
||||
scale[0] = y_scale * 1.5; // total
|
||||
|
||||
for (j = 0; j < COMPONENTS; j++) {
|
||||
psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
|
||||
mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
|
||||
}
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
|
||||
psnr[1], psnr[2], psnr[3]);
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
|
||||
mse[1], mse[2], mse[3]);
|
||||
|
||||
bytes_total += si->bytes_sum[i];
|
||||
// Clear sums for next time.
|
||||
si->bytes_sum[i] = 0;
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[i][j] = 0;
|
||||
si->sse_sum[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// only display statistics once
|
||||
si->psnr_pkt_received = 0;
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
|
||||
return aom_svc_get_message(svc_ctx);
|
||||
}
|
||||
|
||||
void aom_svc_release(SvcContext *svc_ctx) {
|
||||
SvcInternal_t *si;
|
||||
if (svc_ctx == NULL) return;
|
||||
// do not use get_svc_internal as it will unnecessarily allocate an
|
||||
// SvcInternal_t if it was not already allocated
|
||||
si = (SvcInternal_t *)svc_ctx->internal;
|
||||
if (si != NULL) {
|
||||
free(si);
|
||||
svc_ctx->internal = NULL;
|
||||
}
|
||||
}
|
|
@ -100,14 +100,6 @@ endif
|
|||
aomenc.GUID = 548DEC74-7A15-4B2B-AFC3-AA102E7C25C1
|
||||
aomenc.DESCRIPTION = Full featured encoder
|
||||
|
||||
EXAMPLES-$(CONFIG_ENCODERS) += aom_temporal_svc_encoder.c
|
||||
aom_temporal_svc_encoder.SRCS += ivfenc.c ivfenc.h
|
||||
aom_temporal_svc_encoder.SRCS += tools_common.c tools_common.h
|
||||
aom_temporal_svc_encoder.SRCS += video_common.h
|
||||
aom_temporal_svc_encoder.SRCS += video_writer.h video_writer.c
|
||||
aom_temporal_svc_encoder.SRCS += aom_ports/msvc.h
|
||||
aom_temporal_svc_encoder.GUID = B18C08F2-A439-4502-A78E-849BE3D60947
|
||||
aom_temporal_svc_encoder.DESCRIPTION = Temporal SVC Encoder
|
||||
EXAMPLES-$(CONFIG_DECODERS) += simple_decoder.c
|
||||
simple_decoder.GUID = D3BBF1E9-2427-450D-BBFF-B2843C1D44CC
|
||||
simple_decoder.SRCS += ivfdec.h ivfdec.c
|
||||
|
|
|
@ -1,816 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
// This is an example demonstrating how to implement a multi-layer AVx
|
||||
// encoding scheme based on temporal scalability for video applications
|
||||
// that benefit from a scalable bitstream.
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./aom_config.h"
|
||||
#include "../aom_ports/aom_timer.h"
|
||||
#include "aom/aomcx.h"
|
||||
#include "aom/aom_encoder.h"
|
||||
|
||||
#include "../tools_common.h"
|
||||
#include "../video_writer.h"
|
||||
|
||||
static const char *exec_name;
|
||||
|
||||
void usage_exit(void) { exit(EXIT_FAILURE); }
|
||||
|
||||
// Denoiser states, for temporal denoising.
|
||||
enum denoiserState {
|
||||
kDenoiserOff,
|
||||
kDenoiserOnYOnly,
|
||||
kDenoiserOnYUV,
|
||||
kDenoiserOnYUVAggressive,
|
||||
kDenoiserOnAdaptive
|
||||
};
|
||||
|
||||
static int mode_to_num_layers[12] = { 1, 2, 2, 3, 3, 3, 3, 5, 2, 3, 3, 3 };
|
||||
|
||||
// For rate control encoding stats.
|
||||
struct RateControlMetrics {
|
||||
// Number of input frames per layer.
|
||||
int layer_input_frames[AOM_TS_MAX_LAYERS];
|
||||
// Total (cumulative) number of encoded frames per layer.
|
||||
int layer_tot_enc_frames[AOM_TS_MAX_LAYERS];
|
||||
// Number of encoded non-key frames per layer.
|
||||
int layer_enc_frames[AOM_TS_MAX_LAYERS];
|
||||
// Framerate per layer layer (cumulative).
|
||||
double layer_framerate[AOM_TS_MAX_LAYERS];
|
||||
// Target average frame size per layer (per-frame-bandwidth per layer).
|
||||
double layer_pfb[AOM_TS_MAX_LAYERS];
|
||||
// Actual average frame size per layer.
|
||||
double layer_avg_frame_size[AOM_TS_MAX_LAYERS];
|
||||
// Average rate mismatch per layer (|target - actual| / target).
|
||||
double layer_avg_rate_mismatch[AOM_TS_MAX_LAYERS];
|
||||
// Actual encoding bitrate per layer (cumulative).
|
||||
double layer_encoding_bitrate[AOM_TS_MAX_LAYERS];
|
||||
// Average of the short-time encoder actual bitrate.
|
||||
// TODO(marpan): Should we add these short-time stats for each layer?
|
||||
double avg_st_encoding_bitrate;
|
||||
// Variance of the short-time encoder actual bitrate.
|
||||
double variance_st_encoding_bitrate;
|
||||
// Window (number of frames) for computing short-timee encoding bitrate.
|
||||
int window_size;
|
||||
// Number of window measurements.
|
||||
int window_count;
|
||||
int layer_target_bitrate[AOM_MAX_LAYERS];
|
||||
};
|
||||
|
||||
// Note: these rate control metrics assume only 1 key frame in the
|
||||
// sequence (i.e., first frame only). So for temporal pattern# 7
|
||||
// (which has key frame for every frame on base layer), the metrics
|
||||
// computation will be off/wrong.
|
||||
// TODO(marpan): Update these metrics to account for multiple key frames
|
||||
// in the stream.
|
||||
static void set_rate_control_metrics(struct RateControlMetrics *rc,
|
||||
aom_codec_enc_cfg_t *cfg) {
|
||||
unsigned int i = 0;
|
||||
// Set the layer (cumulative) framerate and the target layer (non-cumulative)
|
||||
// per-frame-bandwidth, for the rate control encoding stats below.
|
||||
const double framerate = cfg->g_timebase.den / cfg->g_timebase.num;
|
||||
rc->layer_framerate[0] = framerate / cfg->ts_rate_decimator[0];
|
||||
rc->layer_pfb[0] =
|
||||
1000.0 * rc->layer_target_bitrate[0] / rc->layer_framerate[0];
|
||||
for (i = 0; i < cfg->ts_number_layers; ++i) {
|
||||
if (i > 0) {
|
||||
rc->layer_framerate[i] = framerate / cfg->ts_rate_decimator[i];
|
||||
rc->layer_pfb[i] = 1000.0 * (rc->layer_target_bitrate[i] -
|
||||
rc->layer_target_bitrate[i - 1]) /
|
||||
(rc->layer_framerate[i] - rc->layer_framerate[i - 1]);
|
||||
}
|
||||
rc->layer_input_frames[i] = 0;
|
||||
rc->layer_enc_frames[i] = 0;
|
||||
rc->layer_tot_enc_frames[i] = 0;
|
||||
rc->layer_encoding_bitrate[i] = 0.0;
|
||||
rc->layer_avg_frame_size[i] = 0.0;
|
||||
rc->layer_avg_rate_mismatch[i] = 0.0;
|
||||
}
|
||||
rc->window_count = 0;
|
||||
rc->window_size = 15;
|
||||
rc->avg_st_encoding_bitrate = 0.0;
|
||||
rc->variance_st_encoding_bitrate = 0.0;
|
||||
}
|
||||
|
||||
static void printout_rate_control_summary(struct RateControlMetrics *rc,
|
||||
aom_codec_enc_cfg_t *cfg,
|
||||
int frame_cnt) {
|
||||
unsigned int i = 0;
|
||||
int tot_num_frames = 0;
|
||||
double perc_fluctuation = 0.0;
|
||||
printf("Total number of processed frames: %d\n\n", frame_cnt - 1);
|
||||
printf("Rate control layer stats for %d layer(s):\n\n",
|
||||
cfg->ts_number_layers);
|
||||
for (i = 0; i < cfg->ts_number_layers; ++i) {
|
||||
const int num_dropped =
|
||||
(i > 0) ? (rc->layer_input_frames[i] - rc->layer_enc_frames[i])
|
||||
: (rc->layer_input_frames[i] - rc->layer_enc_frames[i] - 1);
|
||||
tot_num_frames += rc->layer_input_frames[i];
|
||||
rc->layer_encoding_bitrate[i] = 0.001 * rc->layer_framerate[i] *
|
||||
rc->layer_encoding_bitrate[i] /
|
||||
tot_num_frames;
|
||||
rc->layer_avg_frame_size[i] =
|
||||
rc->layer_avg_frame_size[i] / rc->layer_enc_frames[i];
|
||||
rc->layer_avg_rate_mismatch[i] =
|
||||
100.0 * rc->layer_avg_rate_mismatch[i] / rc->layer_enc_frames[i];
|
||||
printf("For layer#: %d \n", i);
|
||||
printf("Bitrate (target vs actual): %d %f \n", rc->layer_target_bitrate[i],
|
||||
rc->layer_encoding_bitrate[i]);
|
||||
printf("Average frame size (target vs actual): %f %f \n", rc->layer_pfb[i],
|
||||
rc->layer_avg_frame_size[i]);
|
||||
printf("Average rate_mismatch: %f \n", rc->layer_avg_rate_mismatch[i]);
|
||||
printf(
|
||||
"Number of input frames, encoded (non-key) frames, "
|
||||
"and perc dropped frames: %d %d %f \n",
|
||||
rc->layer_input_frames[i], rc->layer_enc_frames[i],
|
||||
100.0 * num_dropped / rc->layer_input_frames[i]);
|
||||
printf("\n");
|
||||
}
|
||||
rc->avg_st_encoding_bitrate = rc->avg_st_encoding_bitrate / rc->window_count;
|
||||
rc->variance_st_encoding_bitrate =
|
||||
rc->variance_st_encoding_bitrate / rc->window_count -
|
||||
(rc->avg_st_encoding_bitrate * rc->avg_st_encoding_bitrate);
|
||||
perc_fluctuation = 100.0 * sqrt(rc->variance_st_encoding_bitrate) /
|
||||
rc->avg_st_encoding_bitrate;
|
||||
printf("Short-time stats, for window of %d frames: \n", rc->window_size);
|
||||
printf("Average, rms-variance, and percent-fluct: %f %f %f \n",
|
||||
rc->avg_st_encoding_bitrate, sqrt(rc->variance_st_encoding_bitrate),
|
||||
perc_fluctuation);
|
||||
if ((frame_cnt - 1) != tot_num_frames)
|
||||
die("Error: Number of input frames not equal to output! \n");
|
||||
}
|
||||
|
||||
// Temporal scaling parameters:
|
||||
// NOTE: The 3 prediction frames cannot be used interchangeably due to
|
||||
// differences in the way they are handled throughout the code. The
|
||||
// frames should be allocated to layers in the order LAST, GF, ARF.
|
||||
// Other combinations work, but may produce slightly inferior results.
|
||||
static void set_temporal_layer_pattern(int layering_mode,
|
||||
aom_codec_enc_cfg_t *cfg,
|
||||
int *layer_flags,
|
||||
int *flag_periodicity) {
|
||||
switch (layering_mode) {
|
||||
case 0: {
|
||||
// 1-layer.
|
||||
int ids[1] = { 0 };
|
||||
cfg->ts_periodicity = 1;
|
||||
*flag_periodicity = 1;
|
||||
cfg->ts_number_layers = 1;
|
||||
cfg->ts_rate_decimator[0] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// Update L only.
|
||||
layer_flags[0] =
|
||||
AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
// 2-layers, 2-frame period.
|
||||
int ids[2] = { 0, 1 };
|
||||
cfg->ts_periodicity = 2;
|
||||
*flag_periodicity = 2;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 2;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
#if 1
|
||||
// 0=L, 1=GF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF;
|
||||
layer_flags[1] =
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_REF_ARF;
|
||||
#else
|
||||
// 0=L, 1=GF, Intra-layer prediction disabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF;
|
||||
layer_flags[1] = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_REF_LAST;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// 2-layers, 3-frame period.
|
||||
int ids[3] = { 0, 1, 1 };
|
||||
cfg->ts_periodicity = 3;
|
||||
*flag_periodicity = 3;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 3;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[2] =
|
||||
AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_ARF |
|
||||
AOM_EFLAG_NO_UPD_LAST;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// 3-layers, 6-frame period.
|
||||
int ids[6] = { 0, 2, 2, 1, 2, 2 };
|
||||
cfg->ts_periodicity = 6;
|
||||
*flag_periodicity = 6;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 6;
|
||||
cfg->ts_rate_decimator[1] = 3;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[3] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[1] = layer_flags[2] = layer_flags[4] = layer_flags[5] =
|
||||
AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_LAST;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction disabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] = AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled in layer 1, disabled
|
||||
// in layer 2.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
// 3-layers, 4-frame period.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 4;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF, Intra-layer prediction enabled.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = layer_flags[3] =
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF;
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
// NOTE: Probably of academic interest only.
|
||||
// 5-layers, 16-frame period.
|
||||
int ids[16] = { 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4 };
|
||||
cfg->ts_periodicity = 16;
|
||||
*flag_periodicity = 16;
|
||||
cfg->ts_number_layers = 5;
|
||||
cfg->ts_rate_decimator[0] = 16;
|
||||
cfg->ts_rate_decimator[1] = 8;
|
||||
cfg->ts_rate_decimator[2] = 4;
|
||||
cfg->ts_rate_decimator[3] = 2;
|
||||
cfg->ts_rate_decimator[4] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF;
|
||||
layer_flags[1] = layer_flags[3] = layer_flags[5] = layer_flags[7] =
|
||||
layer_flags[9] = layer_flags[11] = layer_flags[13] = layer_flags[15] =
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[2] = layer_flags[6] = layer_flags[10] = layer_flags[14] =
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_GF;
|
||||
layer_flags[4] = layer_flags[12] =
|
||||
AOM_EFLAG_NO_REF_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[8] = AOM_EFLAG_NO_REF_LAST | AOM_EFLAG_NO_REF_GF;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
// 2-layers, with sync point at first frame of layer 1.
|
||||
int ids[2] = { 0, 1 };
|
||||
cfg->ts_periodicity = 2;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 2;
|
||||
cfg->ts_rate_decimator[0] = 2;
|
||||
cfg->ts_rate_decimator[1] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF.
|
||||
// ARF is used as predictor for all frames, and is only updated on
|
||||
// key frame. Sync point every 8 frames.
|
||||
|
||||
// Layer 0: predict from L and ARF, update L and G.
|
||||
layer_flags[0] =
|
||||
AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_ARF;
|
||||
// Layer 1: sync point: predict from L and ARF, and update G.
|
||||
layer_flags[1] =
|
||||
AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
// Layer 0, predict from L and ARF, update L.
|
||||
layer_flags[2] =
|
||||
AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
|
||||
// Layer 1: predict from L, G and ARF, and update G.
|
||||
layer_flags[3] = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST |
|
||||
AOM_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 0.
|
||||
layer_flags[4] = layer_flags[2];
|
||||
// Layer 1.
|
||||
layer_flags[5] = layer_flags[3];
|
||||
// Layer 0.
|
||||
layer_flags[6] = layer_flags[4];
|
||||
// Layer 1.
|
||||
layer_flags[7] = layer_flags[5];
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
// 3-layers: Sync points for layer 1 and 2 every 8 frames.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
layer_flags[0] = AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_REF_GF |
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[1] = AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF;
|
||||
layer_flags[2] = AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[3] = layer_flags[5] =
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF;
|
||||
layer_flags[4] = AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
|
||||
AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[6] =
|
||||
AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ARF;
|
||||
layer_flags[7] = AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_ENTROPY;
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
// 3-layers structure where ARF is used as predictor for all frames,
|
||||
// and is only updated on key frame.
|
||||
// Sync points for layer 1 and 2 every 8 frames.
|
||||
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
// Layer 0: predict from L and ARF; update L and G.
|
||||
layer_flags[0] =
|
||||
AOM_EFLAG_FORCE_KF | AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF;
|
||||
// Layer 2: sync point: predict from L and ARF; update none.
|
||||
layer_flags[1] = AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_GF |
|
||||
AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST |
|
||||
AOM_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 1: sync point: predict from L and ARF; update G.
|
||||
layer_flags[2] =
|
||||
AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[3] = AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ENTROPY;
|
||||
// Layer 0: predict from L and ARF; update L.
|
||||
layer_flags[4] =
|
||||
AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[5] = layer_flags[3];
|
||||
// Layer 1: predict from L, G, ARF; update G.
|
||||
layer_flags[6] = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[7] = layer_flags[3];
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
default: {
|
||||
// 3-layers structure as in case 10, but no sync/refresh points for
|
||||
// layer 1 and 2.
|
||||
int ids[4] = { 0, 2, 1, 2 };
|
||||
cfg->ts_periodicity = 4;
|
||||
*flag_periodicity = 8;
|
||||
cfg->ts_number_layers = 3;
|
||||
cfg->ts_rate_decimator[0] = 4;
|
||||
cfg->ts_rate_decimator[1] = 2;
|
||||
cfg->ts_rate_decimator[2] = 1;
|
||||
memcpy(cfg->ts_layer_id, ids, sizeof(ids));
|
||||
// 0=L, 1=GF, 2=ARF.
|
||||
// Layer 0: predict from L and ARF; update L.
|
||||
layer_flags[0] =
|
||||
AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_REF_GF;
|
||||
layer_flags[4] = layer_flags[0];
|
||||
// Layer 1: predict from L, G, ARF; update G.
|
||||
layer_flags[2] = AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_LAST;
|
||||
layer_flags[6] = layer_flags[2];
|
||||
// Layer 2: predict from L, G, ARF; update none.
|
||||
layer_flags[1] = AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
|
||||
AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_ENTROPY;
|
||||
layer_flags[3] = layer_flags[1];
|
||||
layer_flags[5] = layer_flags[1];
|
||||
layer_flags[7] = layer_flags[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
AvxVideoWriter *outfile[AOM_TS_MAX_LAYERS] = { NULL };
|
||||
aom_codec_ctx_t codec;
|
||||
aom_codec_enc_cfg_t cfg;
|
||||
int frame_cnt = 0;
|
||||
aom_image_t raw;
|
||||
aom_codec_err_t res;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
int speed;
|
||||
int frame_avail;
|
||||
int got_data;
|
||||
int flags = 0;
|
||||
unsigned int i;
|
||||
int pts = 0; // PTS starts at 0.
|
||||
int frame_duration = 1; // 1 timebase tick per frame.
|
||||
int layering_mode = 0;
|
||||
int layer_flags[AOM_TS_MAX_PERIODICITY] = { 0 };
|
||||
int flag_periodicity = 1;
|
||||
#if AOM_ENCODER_ABI_VERSION > (4 + AOM_CODEC_ABI_VERSION)
|
||||
aom_svc_layer_id_t layer_id = { 0, 0 };
|
||||
#else
|
||||
aom_svc_layer_id_t layer_id = { 0 };
|
||||
#endif
|
||||
const AvxInterface *encoder = NULL;
|
||||
FILE *infile = NULL;
|
||||
struct RateControlMetrics rc;
|
||||
int64_t cx_time = 0;
|
||||
const int min_args_base = 11;
|
||||
#if CONFIG_AOM_HIGHBITDEPTH
|
||||
aom_bit_depth_t bit_depth = AOM_BITS_8;
|
||||
int input_bit_depth = 8;
|
||||
const int min_args = min_args_base + 1;
|
||||
#else
|
||||
const int min_args = min_args_base;
|
||||
#endif // CONFIG_AOM_HIGHBITDEPTH
|
||||
double sum_bitrate = 0.0;
|
||||
double sum_bitrate2 = 0.0;
|
||||
double framerate = 30.0;
|
||||
|
||||
exec_name = argv[0];
|
||||
// Check usage and arguments.
|
||||
if (argc < min_args) {
|
||||
#if CONFIG_AOM_HIGHBITDEPTH
|
||||
die(
|
||||
"Usage: %s <infile> <outfile> <codec_type(aom/av1)> <width> <height> "
|
||||
"<rate_num> <rate_den> <speed> <frame_drop_threshold> <mode> "
|
||||
"<Rate_0> ... <Rate_nlayers-1> <bit-depth> \n",
|
||||
argv[0]);
|
||||
#else
|
||||
die(
|
||||
"Usage: %s <infile> <outfile> <codec_type(aom/av1)> <width> <height> "
|
||||
"<rate_num> <rate_den> <speed> <frame_drop_threshold> <mode> "
|
||||
"<Rate_0> ... <Rate_nlayers-1> \n",
|
||||
argv[0]);
|
||||
#endif // CONFIG_AOM_HIGHBITDEPTH
|
||||
}
|
||||
|
||||
encoder = get_aom_encoder_by_name(argv[3]);
|
||||
if (!encoder) die("Unsupported codec.");
|
||||
|
||||
printf("Using %s\n", aom_codec_iface_name(encoder->codec_interface()));
|
||||
|
||||
width = strtol(argv[4], NULL, 0);
|
||||
height = strtol(argv[5], NULL, 0);
|
||||
if (width < 16 || width % 2 || height < 16 || height % 2) {
|
||||
die("Invalid resolution: %d x %d", width, height);
|
||||
}
|
||||
|
||||
layering_mode = strtol(argv[10], NULL, 0);
|
||||
if (layering_mode < 0 || layering_mode > 12) {
|
||||
die("Invalid layering mode (0..12) %s", argv[10]);
|
||||
}
|
||||
|
||||
if (argc != min_args + mode_to_num_layers[layering_mode]) {
|
||||
die("Invalid number of arguments");
|
||||
}
|
||||
|
||||
#if CONFIG_AOM_HIGHBITDEPTH
|
||||
switch (strtol(argv[argc - 1], NULL, 0)) {
|
||||
case 8:
|
||||
bit_depth = AOM_BITS_8;
|
||||
input_bit_depth = 8;
|
||||
break;
|
||||
case 10:
|
||||
bit_depth = AOM_BITS_10;
|
||||
input_bit_depth = 10;
|
||||
break;
|
||||
case 12:
|
||||
bit_depth = AOM_BITS_12;
|
||||
input_bit_depth = 12;
|
||||
break;
|
||||
default: die("Invalid bit depth (8, 10, 12) %s", argv[argc - 1]);
|
||||
}
|
||||
if (!aom_img_alloc(
|
||||
&raw, bit_depth == AOM_BITS_8 ? AOM_IMG_FMT_I420 : AOM_IMG_FMT_I42016,
|
||||
width, height, 32)) {
|
||||
die("Failed to allocate image", width, height);
|
||||
}
|
||||
#else
|
||||
if (!aom_img_alloc(&raw, AOM_IMG_FMT_I420, width, height, 32)) {
|
||||
die("Failed to allocate image", width, height);
|
||||
}
|
||||
#endif // CONFIG_AOM_HIGHBITDEPTH
|
||||
|
||||
// Populate encoder configuration.
|
||||
res = aom_codec_enc_config_default(encoder->codec_interface(), &cfg, 0);
|
||||
if (res) {
|
||||
printf("Failed to get config: %s\n", aom_codec_err_to_string(res));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Update the default configuration with our settings.
|
||||
cfg.g_w = width;
|
||||
cfg.g_h = height;
|
||||
|
||||
#if CONFIG_AOM_HIGHBITDEPTH
|
||||
if (bit_depth != AOM_BITS_8) {
|
||||
cfg.g_bit_depth = bit_depth;
|
||||
cfg.g_input_bit_depth = input_bit_depth;
|
||||
cfg.g_profile = 2;
|
||||
}
|
||||
#endif // CONFIG_AOM_HIGHBITDEPTH
|
||||
|
||||
// Timebase format e.g. 30fps: numerator=1, demoninator = 30.
|
||||
cfg.g_timebase.num = strtol(argv[6], NULL, 0);
|
||||
cfg.g_timebase.den = strtol(argv[7], NULL, 0);
|
||||
|
||||
speed = strtol(argv[8], NULL, 0);
|
||||
if (speed < 0) {
|
||||
die("Invalid speed setting: must be positive");
|
||||
}
|
||||
|
||||
for (i = min_args_base;
|
||||
(int)i < min_args_base + mode_to_num_layers[layering_mode]; ++i) {
|
||||
rc.layer_target_bitrate[i - 11] = strtol(argv[i], NULL, 0);
|
||||
if (strncmp(encoder->name, "aom", 3) == 0)
|
||||
cfg.ts_target_bitrate[i - 11] = rc.layer_target_bitrate[i - 11];
|
||||
else if (strncmp(encoder->name, "av1", 3) == 0)
|
||||
cfg.layer_target_bitrate[i - 11] = rc.layer_target_bitrate[i - 11];
|
||||
}
|
||||
|
||||
// Real time parameters.
|
||||
cfg.rc_dropframe_thresh = strtol(argv[9], NULL, 0);
|
||||
cfg.rc_end_usage = AOM_CBR;
|
||||
cfg.rc_min_quantizer = 2;
|
||||
cfg.rc_max_quantizer = 56;
|
||||
if (strncmp(encoder->name, "av1", 3) == 0) cfg.rc_max_quantizer = 52;
|
||||
cfg.rc_undershoot_pct = 50;
|
||||
cfg.rc_overshoot_pct = 50;
|
||||
cfg.rc_buf_initial_sz = 500;
|
||||
cfg.rc_buf_optimal_sz = 600;
|
||||
cfg.rc_buf_sz = 1000;
|
||||
|
||||
// Disable dynamic resizing by default.
|
||||
cfg.rc_resize_allowed = 0;
|
||||
|
||||
// Use 1 thread as default.
|
||||
cfg.g_threads = 1;
|
||||
|
||||
// Enable error resilient mode.
|
||||
cfg.g_error_resilient = 1;
|
||||
cfg.g_lag_in_frames = 0;
|
||||
cfg.kf_mode = AOM_KF_AUTO;
|
||||
|
||||
// Disable automatic keyframe placement.
|
||||
cfg.kf_min_dist = cfg.kf_max_dist = 3000;
|
||||
|
||||
cfg.temporal_layering_mode = AV1E_TEMPORAL_LAYERING_MODE_BYPASS;
|
||||
|
||||
set_temporal_layer_pattern(layering_mode, &cfg, layer_flags,
|
||||
&flag_periodicity);
|
||||
|
||||
set_rate_control_metrics(&rc, &cfg);
|
||||
|
||||
// Target bandwidth for the whole stream.
|
||||
// Set to layer_target_bitrate for highest layer (total bitrate).
|
||||
cfg.rc_target_bitrate = rc.layer_target_bitrate[cfg.ts_number_layers - 1];
|
||||
|
||||
// Open input file.
|
||||
if (!(infile = fopen(argv[1], "rb"))) {
|
||||
die("Failed to open %s for reading", argv[1]);
|
||||
}
|
||||
|
||||
framerate = cfg.g_timebase.den / cfg.g_timebase.num;
|
||||
// Open an output file for each stream.
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) {
|
||||
char file_name[PATH_MAX];
|
||||
AvxVideoInfo info;
|
||||
info.codec_fourcc = encoder->fourcc;
|
||||
info.frame_width = cfg.g_w;
|
||||
info.frame_height = cfg.g_h;
|
||||
info.time_base.numerator = cfg.g_timebase.num;
|
||||
info.time_base.denominator = cfg.g_timebase.den;
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "%s_%d.ivf", argv[2], i);
|
||||
outfile[i] = aom_video_writer_open(file_name, kContainerIVF, &info);
|
||||
if (!outfile[i]) die("Failed to open %s for writing", file_name);
|
||||
|
||||
assert(outfile[i] != NULL);
|
||||
}
|
||||
// No spatial layers in this encoder.
|
||||
cfg.ss_number_layers = 1;
|
||||
|
||||
// Initialize codec.
|
||||
#if CONFIG_AOM_HIGHBITDEPTH
|
||||
if (aom_codec_enc_init(
|
||||
&codec, encoder->codec_interface(), &cfg,
|
||||
bit_depth == AOM_BITS_8 ? 0 : AOM_CODEC_USE_HIGHBITDEPTH))
|
||||
#else
|
||||
if (aom_codec_enc_init(&codec, encoder->codec_interface(), &cfg, 0))
|
||||
#endif // CONFIG_AOM_HIGHBITDEPTH
|
||||
die_codec(&codec, "Failed to initialize encoder");
|
||||
|
||||
if (strncmp(encoder->name, "aom", 3) == 0) {
|
||||
aom_codec_control(&codec, AOME_SET_CPUUSED, -speed);
|
||||
aom_codec_control(&codec, AOME_SET_NOISE_SENSITIVITY, kDenoiserOff);
|
||||
aom_codec_control(&codec, AOME_SET_STATIC_THRESHOLD, 1);
|
||||
} else if (strncmp(encoder->name, "av1", 3) == 0) {
|
||||
aom_svc_extra_cfg_t svc_params;
|
||||
aom_codec_control(&codec, AOME_SET_CPUUSED, speed);
|
||||
aom_codec_control(&codec, AV1E_SET_AQ_MODE, 3);
|
||||
aom_codec_control(&codec, AV1E_SET_FRAME_PERIODIC_BOOST, 0);
|
||||
aom_codec_control(&codec, AV1E_SET_NOISE_SENSITIVITY, 0);
|
||||
aom_codec_control(&codec, AOME_SET_STATIC_THRESHOLD, 1);
|
||||
aom_codec_control(&codec, AV1E_SET_TUNE_CONTENT, 0);
|
||||
aom_codec_control(&codec, AV1E_SET_TILE_COLUMNS, (cfg.g_threads >> 1));
|
||||
if (aom_codec_control(&codec, AV1E_SET_SVC, layering_mode > 0 ? 1 : 0))
|
||||
die_codec(&codec, "Failed to set SVC");
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) {
|
||||
svc_params.max_quantizers[i] = cfg.rc_max_quantizer;
|
||||
svc_params.min_quantizers[i] = cfg.rc_min_quantizer;
|
||||
}
|
||||
svc_params.scaling_factor_num[0] = cfg.g_h;
|
||||
svc_params.scaling_factor_den[0] = cfg.g_h;
|
||||
aom_codec_control(&codec, AV1E_SET_SVC_PARAMETERS, &svc_params);
|
||||
}
|
||||
if (strncmp(encoder->name, "aom", 3) == 0) {
|
||||
aom_codec_control(&codec, AOME_SET_SCREEN_CONTENT_MODE, 0);
|
||||
}
|
||||
aom_codec_control(&codec, AOME_SET_TOKEN_PARTITIONS, 1);
|
||||
// This controls the maximum target size of the key frame.
|
||||
// For generating smaller key frames, use a smaller max_intra_size_pct
|
||||
// value, like 100 or 200.
|
||||
{
|
||||
const int max_intra_size_pct = 900;
|
||||
aom_codec_control(&codec, AOME_SET_MAX_INTRA_BITRATE_PCT,
|
||||
max_intra_size_pct);
|
||||
}
|
||||
|
||||
frame_avail = 1;
|
||||
while (frame_avail || got_data) {
|
||||
struct aom_usec_timer timer;
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *pkt;
|
||||
#if AOM_ENCODER_ABI_VERSION > (4 + AOM_CODEC_ABI_VERSION)
|
||||
// Update the temporal layer_id. No spatial layers in this test.
|
||||
layer_id.spatial_layer_id = 0;
|
||||
#endif
|
||||
layer_id.temporal_layer_id =
|
||||
cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity];
|
||||
if (strncmp(encoder->name, "av1", 3) == 0) {
|
||||
aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id);
|
||||
} else if (strncmp(encoder->name, "aom", 3) == 0) {
|
||||
aom_codec_control(&codec, AOME_SET_TEMPORAL_LAYER_ID,
|
||||
layer_id.temporal_layer_id);
|
||||
}
|
||||
flags = layer_flags[frame_cnt % flag_periodicity];
|
||||
if (layering_mode == 0) flags = 0;
|
||||
frame_avail = aom_img_read(&raw, infile);
|
||||
if (frame_avail) ++rc.layer_input_frames[layer_id.temporal_layer_id];
|
||||
aom_usec_timer_start(&timer);
|
||||
if (aom_codec_encode(&codec, frame_avail ? &raw : NULL, pts, 1, flags,
|
||||
AOM_DL_REALTIME)) {
|
||||
die_codec(&codec, "Failed to encode frame");
|
||||
}
|
||||
aom_usec_timer_mark(&timer);
|
||||
cx_time += aom_usec_timer_elapsed(&timer);
|
||||
// Reset KF flag.
|
||||
if (layering_mode != 7) {
|
||||
layer_flags[0] &= ~AOM_EFLAG_FORCE_KF;
|
||||
}
|
||||
got_data = 0;
|
||||
while ((pkt = aom_codec_get_cx_data(&codec, &iter))) {
|
||||
got_data = 1;
|
||||
switch (pkt->kind) {
|
||||
case AOM_CODEC_CX_FRAME_PKT:
|
||||
for (i = cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity];
|
||||
i < cfg.ts_number_layers; ++i) {
|
||||
aom_video_writer_write_frame(outfile[i], pkt->data.frame.buf,
|
||||
pkt->data.frame.sz, pts);
|
||||
++rc.layer_tot_enc_frames[i];
|
||||
rc.layer_encoding_bitrate[i] += 8.0 * pkt->data.frame.sz;
|
||||
// Keep count of rate control stats per layer (for non-key frames).
|
||||
if (i == cfg.ts_layer_id[frame_cnt % cfg.ts_periodicity] &&
|
||||
!(pkt->data.frame.flags & AOM_FRAME_IS_KEY)) {
|
||||
rc.layer_avg_frame_size[i] += 8.0 * pkt->data.frame.sz;
|
||||
rc.layer_avg_rate_mismatch[i] +=
|
||||
fabs(8.0 * pkt->data.frame.sz - rc.layer_pfb[i]) /
|
||||
rc.layer_pfb[i];
|
||||
++rc.layer_enc_frames[i];
|
||||
}
|
||||
}
|
||||
// Update for short-time encoding bitrate states, for moving window
|
||||
// of size rc->window, shifted by rc->window / 2.
|
||||
// Ignore first window segment, due to key frame.
|
||||
if (frame_cnt > rc.window_size) {
|
||||
sum_bitrate += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
|
||||
if (frame_cnt % rc.window_size == 0) {
|
||||
rc.window_count += 1;
|
||||
rc.avg_st_encoding_bitrate += sum_bitrate / rc.window_size;
|
||||
rc.variance_st_encoding_bitrate +=
|
||||
(sum_bitrate / rc.window_size) *
|
||||
(sum_bitrate / rc.window_size);
|
||||
sum_bitrate = 0.0;
|
||||
}
|
||||
}
|
||||
// Second shifted window.
|
||||
if (frame_cnt > rc.window_size + rc.window_size / 2) {
|
||||
sum_bitrate2 += 0.001 * 8.0 * pkt->data.frame.sz * framerate;
|
||||
if (frame_cnt > 2 * rc.window_size &&
|
||||
frame_cnt % rc.window_size == 0) {
|
||||
rc.window_count += 1;
|
||||
rc.avg_st_encoding_bitrate += sum_bitrate2 / rc.window_size;
|
||||
rc.variance_st_encoding_bitrate +=
|
||||
(sum_bitrate2 / rc.window_size) *
|
||||
(sum_bitrate2 / rc.window_size);
|
||||
sum_bitrate2 = 0.0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
++frame_cnt;
|
||||
pts += frame_duration;
|
||||
}
|
||||
fclose(infile);
|
||||
printout_rate_control_summary(&rc, &cfg, frame_cnt);
|
||||
printf("\n");
|
||||
printf("Frame cnt and encoding time/FPS stats for encoding: %d %f %f \n",
|
||||
frame_cnt, 1000 * (float)cx_time / (double)(frame_cnt * 1000000),
|
||||
1000000 * (double)frame_cnt / (double)cx_time);
|
||||
|
||||
if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
|
||||
|
||||
// Try to rewrite the output file headers with the actual frame count.
|
||||
for (i = 0; i < cfg.ts_number_layers; ++i) aom_video_writer_close(outfile[i]);
|
||||
|
||||
aom_img_free(&raw);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
#!/bin/sh
|
||||
## Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
##
|
||||
## This source code is subject to the terms of the BSD 2 Clause License and
|
||||
## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
## was not distributed with this source code in the LICENSE file, you can
|
||||
## obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
## Media Patent License 1.0 was not distributed with this source code in the
|
||||
## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
##
|
||||
## This file tests the libaom aom_temporal_svc_encoder example. To add new
|
||||
## tests to this file, do the following:
|
||||
## 1. Write a shell function (this is your test).
|
||||
## 2. Add the function to aom_tsvc_encoder_tests (on a new line).
|
||||
##
|
||||
. $(dirname $0)/tools_common.sh
|
||||
|
||||
# Environment check: $YUV_RAW_INPUT is required.
|
||||
aom_tsvc_encoder_verify_environment() {
|
||||
if [ ! -e "${YUV_RAW_INPUT}" ]; then
|
||||
echo "Libaom test data must exist in LIBAOM_TEST_DATA_PATH."
|
||||
return 1
|
||||
fi
|
||||
if [ "$(aom_config_option_enabled CONFIG_TEMPORAL_DENOISING)" != "yes" ]; then
|
||||
elog "Warning: Temporal denoising is disabled! Spatial denoising will be " \
|
||||
"used instead, which is probably not what you want for this test."
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs aom_temporal_svc_encoder using the codec specified by $1 and output file
|
||||
# name by $2. Additional positional parameters are passed directly to
|
||||
# aom_temporal_svc_encoder.
|
||||
aom_tsvc_encoder() {
|
||||
local encoder="${LIBAOM_BIN_PATH}/aom_temporal_svc_encoder"
|
||||
encoder="${encoder}${AOM_TEST_EXE_SUFFIX}"
|
||||
local codec="$1"
|
||||
local output_file_base="$2"
|
||||
local output_file="${AOM_TEST_OUTPUT_DIR}/${output_file_base}"
|
||||
local timebase_num="1"
|
||||
local timebase_den="1000"
|
||||
local speed="6"
|
||||
local frame_drop_thresh="30"
|
||||
|
||||
shift 2
|
||||
|
||||
if [ ! -x "${encoder}" ]; then
|
||||
elog "${encoder} does not exist or is not executable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "${AOM_TEST_PREFIX}" "${encoder}" "${YUV_RAW_INPUT}" "${output_file}" \
|
||||
"${codec}" "${YUV_RAW_INPUT_WIDTH}" "${YUV_RAW_INPUT_HEIGHT}" \
|
||||
"${timebase_num}" "${timebase_den}" "${speed}" "${frame_drop_thresh}" \
|
||||
"$@" \
|
||||
${devnull}
|
||||
}
|
||||
|
||||
# Confirms that all expected output files exist given the output file name
|
||||
# passed to aom_temporal_svc_encoder.
|
||||
# The file name passed to aom_temporal_svc_encoder is joined with the stream
|
||||
# number and the extension .ivf to produce per stream output files. Here $1 is
|
||||
# file name, and $2 is expected number of files.
|
||||
files_exist() {
|
||||
local file_name="${AOM_TEST_OUTPUT_DIR}/$1"
|
||||
local num_files="$(($2 - 1))"
|
||||
for stream_num in $(seq 0 ${num_files}); do
|
||||
[ -e "${file_name}_${stream_num}.ivf" ] || return 1
|
||||
done
|
||||
}
|
||||
|
||||
# Run aom_temporal_svc_encoder in all supported modes for aom and av1.
|
||||
|
||||
aom_tsvc_encoder_aom_mode_0() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 0 200 || return 1
|
||||
# Mode 0 produces 1 stream
|
||||
files_exist "${FUNCNAME}" 1 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_1() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 1 200 400 || return 1
|
||||
# Mode 1 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_2() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 2 200 400 || return 1
|
||||
# Mode 2 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_3() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 3 200 400 600 || return 1
|
||||
# Mode 3 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_4() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 4 200 400 600 || return 1
|
||||
# Mode 4 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_5() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 5 200 400 600 || return 1
|
||||
# Mode 5 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_6() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 6 200 400 600 || return 1
|
||||
# Mode 6 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_7() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 7 200 400 600 800 1000 || return 1
|
||||
# Mode 7 produces 5 streams
|
||||
files_exist "${FUNCNAME}" 5 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_8() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 8 200 400 || return 1
|
||||
# Mode 8 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_9() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 9 200 400 600 || return 1
|
||||
# Mode 9 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_10() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 10 200 400 600 || return 1
|
||||
# Mode 10 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_aom_mode_11() {
|
||||
if [ "$(aom_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder aom "${FUNCNAME}" 11 200 400 600 || return 1
|
||||
# Mode 11 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_0() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 0 200 || return 1
|
||||
# Mode 0 produces 1 stream
|
||||
files_exist "${FUNCNAME}" 1 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_1() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 1 200 400 || return 1
|
||||
# Mode 1 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_2() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 2 200 400 || return 1
|
||||
# Mode 2 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_3() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 3 200 400 600 || return 1
|
||||
# Mode 3 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_4() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 4 200 400 600 || return 1
|
||||
# Mode 4 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_5() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 5 200 400 600 || return 1
|
||||
# Mode 5 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_6() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 6 200 400 600 || return 1
|
||||
# Mode 6 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_7() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 7 200 400 600 800 1000 || return 1
|
||||
# Mode 7 produces 5 streams
|
||||
files_exist "${FUNCNAME}" 5 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_8() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 8 200 400 || return 1
|
||||
# Mode 8 produces 2 streams
|
||||
files_exist "${FUNCNAME}" 2 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_9() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 9 200 400 600 || return 1
|
||||
# Mode 9 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_10() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 10 200 400 600 || return 1
|
||||
# Mode 10 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_av1_mode_11() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
aom_tsvc_encoder av1 "${FUNCNAME}" 11 200 400 600 || return 1
|
||||
# Mode 11 produces 3 streams
|
||||
files_exist "${FUNCNAME}" 3 || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
aom_tsvc_encoder_tests="aom_tsvc_encoder_aom_mode_0
|
||||
aom_tsvc_encoder_aom_mode_1
|
||||
aom_tsvc_encoder_aom_mode_2
|
||||
aom_tsvc_encoder_aom_mode_3
|
||||
aom_tsvc_encoder_aom_mode_4
|
||||
aom_tsvc_encoder_aom_mode_5
|
||||
aom_tsvc_encoder_aom_mode_6
|
||||
aom_tsvc_encoder_aom_mode_7
|
||||
aom_tsvc_encoder_aom_mode_8
|
||||
aom_tsvc_encoder_aom_mode_9
|
||||
aom_tsvc_encoder_aom_mode_10
|
||||
aom_tsvc_encoder_aom_mode_11
|
||||
aom_tsvc_encoder_av1_mode_0
|
||||
aom_tsvc_encoder_av1_mode_1
|
||||
aom_tsvc_encoder_av1_mode_2
|
||||
aom_tsvc_encoder_av1_mode_3
|
||||
aom_tsvc_encoder_av1_mode_4
|
||||
aom_tsvc_encoder_av1_mode_5
|
||||
aom_tsvc_encoder_av1_mode_6
|
||||
aom_tsvc_encoder_av1_mode_7
|
||||
aom_tsvc_encoder_av1_mode_8
|
||||
aom_tsvc_encoder_av1_mode_9
|
||||
aom_tsvc_encoder_av1_mode_10
|
||||
aom_tsvc_encoder_av1_mode_11"
|
||||
|
||||
run_tests aom_tsvc_encoder_verify_environment "${aom_tsvc_encoder_tests}"
|
|
@ -1,72 +0,0 @@
|
|||
#!/bin/sh
|
||||
## Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
##
|
||||
## This source code is subject to the terms of the BSD 2 Clause License and
|
||||
## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
## was not distributed with this source code in the LICENSE file, you can
|
||||
## obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
## Media Patent License 1.0 was not distributed with this source code in the
|
||||
## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
##
|
||||
## This file tests the libaom av1_spatial_svc_encoder example. To add new
|
||||
## tests to to this file, do the following:
|
||||
## 1. Write a shell function (this is your test).
|
||||
## 2. Add the function to av1_spatial_svc_tests (on a new line).
|
||||
##
|
||||
. $(dirname $0)/tools_common.sh
|
||||
|
||||
# Environment check: $YUV_RAW_INPUT is required.
|
||||
av1_spatial_svc_encoder_verify_environment() {
|
||||
if [ ! -e "${YUV_RAW_INPUT}" ]; then
|
||||
echo "Libaom test data must exist in LIBAOM_TEST_DATA_PATH."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs av1_spatial_svc_encoder. $1 is the test name.
|
||||
av1_spatial_svc_encoder() {
|
||||
local readonly \
|
||||
encoder="${LIBAOM_BIN_PATH}/av1_spatial_svc_encoder${AOM_TEST_EXE_SUFFIX}"
|
||||
local readonly test_name="$1"
|
||||
local readonly \
|
||||
output_file="${AOM_TEST_OUTPUT_DIR}/av1_ssvc_encoder${test_name}.ivf"
|
||||
local readonly frames_to_encode=10
|
||||
local readonly max_kf=9999
|
||||
|
||||
shift
|
||||
|
||||
if [ ! -x "${encoder}" ]; then
|
||||
elog "${encoder} does not exist or is not executable."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval "${AOM_TEST_PREFIX}" "${encoder}" -w "${YUV_RAW_INPUT_WIDTH}" \
|
||||
-h "${YUV_RAW_INPUT_HEIGHT}" -k "${max_kf}" -f "${frames_to_encode}" \
|
||||
"$@" "${YUV_RAW_INPUT}" "${output_file}" ${devnull}
|
||||
|
||||
[ -e "${output_file}" ] || return 1
|
||||
}
|
||||
|
||||
# Each test is run with layer count 1-$av1_ssvc_test_layers.
|
||||
av1_ssvc_test_layers=5
|
||||
|
||||
av1_spatial_svc() {
|
||||
if [ "$(av1_encode_available)" = "yes" ]; then
|
||||
local readonly test_name="av1_spatial_svc"
|
||||
for layers in $(seq 1 ${av1_ssvc_test_layers}); do
|
||||
av1_spatial_svc_encoder "${test_name}" -sl ${layers}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
readonly av1_spatial_svc_tests="DISABLED_av1_spatial_svc_mode_i
|
||||
DISABLED_av1_spatial_svc_mode_altip
|
||||
DISABLED_av1_spatial_svc_mode_ip
|
||||
DISABLED_av1_spatial_svc_mode_gf
|
||||
av1_spatial_svc"
|
||||
|
||||
if [ "$(aom_config_option_enabled CONFIG_SPATIAL_SVC)" = "yes" ]; then
|
||||
run_tests \
|
||||
av1_spatial_svc_encoder_verify_environment \
|
||||
"${av1_spatial_svc_tests}"
|
||||
fi
|
790
test/svc_test.cc
790
test/svc_test.cc
|
@ -1,790 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
|
||||
*
|
||||
* This source code is subject to the terms of the BSD 2 Clause License and
|
||||
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
|
||||
* was not distributed with this source code in the LICENSE file, you can
|
||||
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
|
||||
* Media Patent License 1.0 was not distributed with this source code in the
|
||||
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "third_party/googletest/src/include/gtest/gtest.h"
|
||||
#include "test/codec_factory.h"
|
||||
#include "test/decode_test_driver.h"
|
||||
#include "test/i420_video_source.h"
|
||||
|
||||
#include "av1/decoder/decoder.h"
|
||||
|
||||
#include "aom/svc_context.h"
|
||||
#include "aom/aomcx.h"
|
||||
#include "aom/aom_encoder.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using libaom_test::CodecFactory;
|
||||
using libaom_test::Decoder;
|
||||
using libaom_test::DxDataIterator;
|
||||
using libaom_test::AV1CodecFactory;
|
||||
|
||||
class SvcTest : public ::testing::Test {
|
||||
protected:
|
||||
static const uint32_t kWidth = 352;
|
||||
static const uint32_t kHeight = 288;
|
||||
|
||||
SvcTest()
|
||||
: codec_iface_(0), test_file_name_("hantro_collage_w352h288.yuv"),
|
||||
codec_initialized_(false), decoder_(0) {
|
||||
memset(&svc_, 0, sizeof(svc_));
|
||||
memset(&codec_, 0, sizeof(codec_));
|
||||
memset(&codec_enc_, 0, sizeof(codec_enc_));
|
||||
}
|
||||
|
||||
virtual ~SvcTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
svc_.log_level = SVC_LOG_DEBUG;
|
||||
svc_.log_print = 0;
|
||||
|
||||
codec_iface_ = aom_codec_av1_cx();
|
||||
const aom_codec_err_t res =
|
||||
aom_codec_enc_config_default(codec_iface_, &codec_enc_, 0);
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
|
||||
codec_enc_.g_w = kWidth;
|
||||
codec_enc_.g_h = kHeight;
|
||||
codec_enc_.g_timebase.num = 1;
|
||||
codec_enc_.g_timebase.den = 60;
|
||||
codec_enc_.kf_min_dist = 100;
|
||||
codec_enc_.kf_max_dist = 100;
|
||||
|
||||
aom_codec_dec_cfg_t dec_cfg = aom_codec_dec_cfg_t();
|
||||
AV1CodecFactory codec_factory;
|
||||
decoder_ = codec_factory.CreateDecoder(dec_cfg, 0);
|
||||
|
||||
tile_columns_ = 0;
|
||||
tile_rows_ = 0;
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
ReleaseEncoder();
|
||||
delete (decoder_);
|
||||
}
|
||||
|
||||
void InitializeEncoder() {
|
||||
const aom_codec_err_t res =
|
||||
aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
aom_codec_control(&codec_, AOME_SET_CPUUSED, 4); // Make the test faster
|
||||
aom_codec_control(&codec_, AV1E_SET_TILE_COLUMNS, tile_columns_);
|
||||
aom_codec_control(&codec_, AV1E_SET_TILE_ROWS, tile_rows_);
|
||||
codec_initialized_ = true;
|
||||
}
|
||||
|
||||
void ReleaseEncoder() {
|
||||
aom_svc_release(&svc_);
|
||||
if (codec_initialized_) aom_codec_destroy(&codec_);
|
||||
codec_initialized_ = false;
|
||||
}
|
||||
|
||||
void GetStatsData(std::string *const stats_buf) {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *cx_pkt;
|
||||
|
||||
while ((cx_pkt = aom_codec_get_cx_data(&codec_, &iter)) != NULL) {
|
||||
if (cx_pkt->kind == AOM_CODEC_STATS_PKT) {
|
||||
EXPECT_GT(cx_pkt->data.twopass_stats.sz, 0U);
|
||||
ASSERT_TRUE(cx_pkt->data.twopass_stats.buf != NULL);
|
||||
stats_buf->append(static_cast<char *>(cx_pkt->data.twopass_stats.buf),
|
||||
cx_pkt->data.twopass_stats.sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pass1EncodeNFrames(const int n, const int layers,
|
||||
std::string *const stats_buf) {
|
||||
aom_codec_err_t res;
|
||||
|
||||
ASSERT_GT(n, 0);
|
||||
ASSERT_GT(layers, 0);
|
||||
svc_.spatial_layers = layers;
|
||||
codec_enc_.g_pass = AOM_RC_FIRST_PASS;
|
||||
InitializeEncoder();
|
||||
|
||||
libaom_test::I420VideoSource video(
|
||||
test_file_name_, codec_enc_.g_w, codec_enc_.g_h,
|
||||
codec_enc_.g_timebase.den, codec_enc_.g_timebase.num, 0, 30);
|
||||
video.Begin();
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
res = aom_svc_encode(&svc_, &codec_, video.img(), video.pts(),
|
||||
video.duration(), AOM_DL_GOOD_QUALITY);
|
||||
ASSERT_EQ(AOM_CODEC_OK, res);
|
||||
GetStatsData(stats_buf);
|
||||
video.Next();
|
||||
}
|
||||
|
||||
// Flush encoder and test EOS packet.
|
||||
res = aom_svc_encode(&svc_, &codec_, NULL, video.pts(), video.duration(),
|
||||
AOM_DL_GOOD_QUALITY);
|
||||
ASSERT_EQ(AOM_CODEC_OK, res);
|
||||
GetStatsData(stats_buf);
|
||||
|
||||
ReleaseEncoder();
|
||||
}
|
||||
|
||||
void StoreFrames(const size_t max_frame_received,
|
||||
struct aom_fixed_buf *const outputs,
|
||||
size_t *const frame_received) {
|
||||
aom_codec_iter_t iter = NULL;
|
||||
const aom_codec_cx_pkt_t *cx_pkt;
|
||||
|
||||
while ((cx_pkt = aom_codec_get_cx_data(&codec_, &iter)) != NULL) {
|
||||
if (cx_pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
|
||||
const size_t frame_size = cx_pkt->data.frame.sz;
|
||||
|
||||
EXPECT_GT(frame_size, 0U);
|
||||
ASSERT_TRUE(cx_pkt->data.frame.buf != NULL);
|
||||
ASSERT_LT(*frame_received, max_frame_received);
|
||||
|
||||
if (*frame_received == 0)
|
||||
EXPECT_EQ(1, !!(cx_pkt->data.frame.flags & AOM_FRAME_IS_KEY));
|
||||
|
||||
outputs[*frame_received].buf = malloc(frame_size + 16);
|
||||
ASSERT_TRUE(outputs[*frame_received].buf != NULL);
|
||||
memcpy(outputs[*frame_received].buf, cx_pkt->data.frame.buf,
|
||||
frame_size);
|
||||
outputs[*frame_received].sz = frame_size;
|
||||
++(*frame_received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pass2EncodeNFrames(std::string *const stats_buf, const int n,
|
||||
const int layers,
|
||||
struct aom_fixed_buf *const outputs) {
|
||||
aom_codec_err_t res;
|
||||
size_t frame_received = 0;
|
||||
|
||||
ASSERT_TRUE(outputs != NULL);
|
||||
ASSERT_GT(n, 0);
|
||||
ASSERT_GT(layers, 0);
|
||||
svc_.spatial_layers = layers;
|
||||
codec_enc_.rc_target_bitrate = 500;
|
||||
if (codec_enc_.g_pass == AOM_RC_LAST_PASS) {
|
||||
ASSERT_TRUE(stats_buf != NULL);
|
||||
ASSERT_GT(stats_buf->size(), 0U);
|
||||
codec_enc_.rc_twopass_stats_in.buf = &(*stats_buf)[0];
|
||||
codec_enc_.rc_twopass_stats_in.sz = stats_buf->size();
|
||||
}
|
||||
InitializeEncoder();
|
||||
|
||||
libaom_test::I420VideoSource video(
|
||||
test_file_name_, codec_enc_.g_w, codec_enc_.g_h,
|
||||
codec_enc_.g_timebase.den, codec_enc_.g_timebase.num, 0, 30);
|
||||
video.Begin();
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
res = aom_svc_encode(&svc_, &codec_, video.img(), video.pts(),
|
||||
video.duration(), AOM_DL_GOOD_QUALITY);
|
||||
ASSERT_EQ(AOM_CODEC_OK, res);
|
||||
StoreFrames(n, outputs, &frame_received);
|
||||
video.Next();
|
||||
}
|
||||
|
||||
// Flush encoder.
|
||||
res = aom_svc_encode(&svc_, &codec_, NULL, 0, video.duration(),
|
||||
AOM_DL_GOOD_QUALITY);
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
StoreFrames(n, outputs, &frame_received);
|
||||
|
||||
EXPECT_EQ(frame_received, static_cast<size_t>(n));
|
||||
|
||||
ReleaseEncoder();
|
||||
}
|
||||
|
||||
void DecodeNFrames(const struct aom_fixed_buf *const inputs, const int n) {
|
||||
int decoded_frames = 0;
|
||||
int received_frames = 0;
|
||||
|
||||
ASSERT_TRUE(inputs != NULL);
|
||||
ASSERT_GT(n, 0);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
ASSERT_TRUE(inputs[i].buf != NULL);
|
||||
ASSERT_GT(inputs[i].sz, 0U);
|
||||
const aom_codec_err_t res_dec = decoder_->DecodeFrame(
|
||||
static_cast<const uint8_t *>(inputs[i].buf), inputs[i].sz);
|
||||
ASSERT_EQ(AOM_CODEC_OK, res_dec) << decoder_->DecodeError();
|
||||
++decoded_frames;
|
||||
|
||||
DxDataIterator dec_iter = decoder_->GetDxData();
|
||||
while (dec_iter.Next() != NULL) {
|
||||
++received_frames;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(decoded_frames, n);
|
||||
EXPECT_EQ(received_frames, n);
|
||||
}
|
||||
|
||||
void DropEnhancementLayers(struct aom_fixed_buf *const inputs,
|
||||
const int num_super_frames,
|
||||
const int remained_spatial_layers) {
|
||||
ASSERT_TRUE(inputs != NULL);
|
||||
ASSERT_GT(num_super_frames, 0);
|
||||
ASSERT_GT(remained_spatial_layers, 0);
|
||||
|
||||
for (int i = 0; i < num_super_frames; ++i) {
|
||||
uint32_t frame_sizes[8] = { 0 };
|
||||
int frame_count = 0;
|
||||
int frames_found = 0;
|
||||
int frame;
|
||||
ASSERT_TRUE(inputs[i].buf != NULL);
|
||||
ASSERT_GT(inputs[i].sz, 0U);
|
||||
|
||||
aom_codec_err_t res = av1_parse_superframe_index(
|
||||
static_cast<const uint8_t *>(inputs[i].buf), inputs[i].sz,
|
||||
frame_sizes, &frame_count, NULL, NULL);
|
||||
ASSERT_EQ(AOM_CODEC_OK, res);
|
||||
|
||||
if (frame_count == 0) {
|
||||
// There's no super frame but only a single frame.
|
||||
ASSERT_EQ(1, remained_spatial_layers);
|
||||
} else {
|
||||
// Found a super frame.
|
||||
uint8_t *frame_data = static_cast<uint8_t *>(inputs[i].buf);
|
||||
uint8_t *frame_start = frame_data;
|
||||
for (frame = 0; frame < frame_count; ++frame) {
|
||||
// Looking for a visible frame.
|
||||
if (frame_data[0] & 0x02) {
|
||||
++frames_found;
|
||||
if (frames_found == remained_spatial_layers) break;
|
||||
}
|
||||
frame_data += frame_sizes[frame];
|
||||
}
|
||||
ASSERT_LT(frame, frame_count)
|
||||
<< "Couldn't find a visible frame. "
|
||||
<< "remained_spatial_layers: " << remained_spatial_layers
|
||||
<< " super_frame: " << i;
|
||||
if (frame == frame_count - 1) continue;
|
||||
|
||||
frame_data += frame_sizes[frame];
|
||||
|
||||
// We need to add one more frame for multiple frame contexts.
|
||||
uint8_t marker =
|
||||
static_cast<const uint8_t *>(inputs[i].buf)[inputs[i].sz - 1];
|
||||
const uint32_t mag = ((marker >> 3) & 0x3) + 1;
|
||||
const size_t index_sz = 2 + mag * frame_count;
|
||||
const size_t new_index_sz = 2 + mag * (frame + 1);
|
||||
marker &= 0x0f8;
|
||||
marker |= frame;
|
||||
|
||||
// Copy existing frame sizes.
|
||||
memmove(frame_data + 1, frame_start + inputs[i].sz - index_sz + 1,
|
||||
new_index_sz - 2);
|
||||
// New marker.
|
||||
frame_data[0] = marker;
|
||||
frame_data += (mag * (frame + 1) + 1);
|
||||
|
||||
*frame_data++ = marker;
|
||||
inputs[i].sz = frame_data - frame_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeBitstreamBuffers(struct aom_fixed_buf *const inputs, const int n) {
|
||||
ASSERT_TRUE(inputs != NULL);
|
||||
ASSERT_GT(n, 0);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
free(inputs[i].buf);
|
||||
inputs[i].buf = NULL;
|
||||
inputs[i].sz = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SvcContext svc_;
|
||||
aom_codec_ctx_t codec_;
|
||||
struct aom_codec_enc_cfg codec_enc_;
|
||||
aom_codec_iface_t *codec_iface_;
|
||||
std::string test_file_name_;
|
||||
bool codec_initialized_;
|
||||
Decoder *decoder_;
|
||||
int tile_columns_;
|
||||
int tile_rows_;
|
||||
};
|
||||
|
||||
TEST_F(SvcTest, SvcInit) {
|
||||
// test missing parameters
|
||||
aom_codec_err_t res = aom_svc_init(NULL, &codec_, codec_iface_, &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
res = aom_svc_init(&svc_, NULL, codec_iface_, &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
res = aom_svc_init(&svc_, &codec_, NULL, &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_init(&svc_, &codec_, codec_iface_, NULL);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
svc_.spatial_layers = 6; // too many layers
|
||||
res = aom_svc_init(&svc_, &codec_, codec_iface_, &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
svc_.spatial_layers = 0; // use default layers
|
||||
InitializeEncoder();
|
||||
EXPECT_EQ(AOM_SS_DEFAULT_LAYERS, svc_.spatial_layers);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, InitTwoLayers) {
|
||||
svc_.spatial_layers = 2;
|
||||
InitializeEncoder();
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, InvalidOptions) {
|
||||
aom_codec_err_t res = aom_svc_set_options(&svc_, NULL);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "not-an-option=1");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetLayersOption) {
|
||||
aom_codec_err_t res = aom_svc_set_options(&svc_, "spatial-layers=3");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
InitializeEncoder();
|
||||
EXPECT_EQ(3, svc_.spatial_layers);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetMultipleOptions) {
|
||||
aom_codec_err_t res =
|
||||
aom_svc_set_options(&svc_, "spatial-layers=2 scale-factors=1/3,2/3");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
InitializeEncoder();
|
||||
EXPECT_EQ(2, svc_.spatial_layers);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetScaleFactorsOption) {
|
||||
svc_.spatial_layers = 2;
|
||||
aom_codec_err_t res =
|
||||
aom_svc_set_options(&svc_, "scale-factors=not-scale-factors");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "scale-factors=1/3, 3*3");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "scale-factors=1/3");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "scale-factors=1/3,2/3");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
InitializeEncoder();
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetQuantizersOption) {
|
||||
svc_.spatial_layers = 2;
|
||||
aom_codec_err_t res = aom_svc_set_options(&svc_, "max-quantizers=nothing");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "min-quantizers=nothing");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "max-quantizers=40");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "min-quantizers=40");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "max-quantizers=30,30 min-quantizers=40,40");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "max-quantizers=40,40 min-quantizers=30,30");
|
||||
InitializeEncoder();
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetAutoAltRefOption) {
|
||||
svc_.spatial_layers = 5;
|
||||
aom_codec_err_t res = aom_svc_set_options(&svc_, "auto-alt-refs=none");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
res = aom_svc_set_options(&svc_, "auto-alt-refs=1,1,1,1,0");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=0,1,1,1,0");
|
||||
InitializeEncoder();
|
||||
}
|
||||
|
||||
// Test that decoder can handle an SVC frame as the first frame in a sequence.
|
||||
TEST_F(SvcTest, OnePassEncodeOneFrame) {
|
||||
codec_enc_.g_pass = AOM_RC_ONE_PASS;
|
||||
aom_fixed_buf output = { 0 };
|
||||
Pass2EncodeNFrames(NULL, 1, 2, &output);
|
||||
DecodeNFrames(&output, 1);
|
||||
FreeBitstreamBuffers(&output, 1);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, OnePassEncodeThreeFrames) {
|
||||
codec_enc_.g_pass = AOM_RC_ONE_PASS;
|
||||
codec_enc_.g_lag_in_frames = 0;
|
||||
aom_fixed_buf outputs[3];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(NULL, 3, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 3);
|
||||
FreeBitstreamBuffers(&outputs[0], 3);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode10Frames) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(10, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode20FramesWithAltRef) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(20, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1");
|
||||
aom_fixed_buf outputs[20];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 20, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 20);
|
||||
FreeBitstreamBuffers(&outputs[0], 20);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2SpatialLayersDecodeBaseLayerOnly) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(10, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 2, &outputs[0]);
|
||||
DropEnhancementLayers(&outputs[0], 10, 1);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode5SpatialLayersDecode54321Layers) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(10, 5, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=0,1,1,1,0");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 5, &outputs[0]);
|
||||
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 4);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 3);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 2);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 1);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2SNRLayers) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1,1/1");
|
||||
Pass1EncodeNFrames(20, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1 scale-factors=1/1,1/1");
|
||||
aom_fixed_buf outputs[20];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 20, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 20);
|
||||
FreeBitstreamBuffers(&outputs[0], 20);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode3SNRLayersDecode321Layers) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1,1/1,1/1");
|
||||
Pass1EncodeNFrames(20, 3, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1,1 scale-factors=1/1,1/1,1/1");
|
||||
aom_fixed_buf outputs[20];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 20, 3, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 20);
|
||||
DropEnhancementLayers(&outputs[0], 20, 2);
|
||||
DecodeNFrames(&outputs[0], 20);
|
||||
DropEnhancementLayers(&outputs[0], 20, 1);
|
||||
DecodeNFrames(&outputs[0], 20);
|
||||
|
||||
FreeBitstreamBuffers(&outputs[0], 20);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, SetMultipleFrameContextsOption) {
|
||||
svc_.spatial_layers = 5;
|
||||
aom_codec_err_t res = aom_svc_set_options(&svc_, "multi-frame-contexts=1");
|
||||
EXPECT_EQ(AOM_CODEC_OK, res);
|
||||
res = aom_svc_init(&svc_, &codec_, aom_codec_av1_cx(), &codec_enc_);
|
||||
EXPECT_EQ(AOM_CODEC_INVALID_PARAM, res);
|
||||
|
||||
svc_.spatial_layers = 2;
|
||||
res = aom_svc_set_options(&svc_, "multi-frame-contexts=1");
|
||||
InitializeEncoder();
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2SpatialLayersWithMultipleFrameContexts) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(10, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1 multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest,
|
||||
TwoPassEncode2SpatialLayersWithMultipleFrameContextsDecodeBaselayer) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
Pass1EncodeNFrames(10, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1,1 multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 2, &outputs[0]);
|
||||
DropEnhancementLayers(&outputs[0], 10, 1);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2SNRLayersWithMultipleFrameContexts) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1,1/1");
|
||||
Pass1EncodeNFrames(10, 2, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_,
|
||||
"auto-alt-refs=1,1 scale-factors=1/1,1/1 "
|
||||
"multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 2, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest,
|
||||
TwoPassEncode3SNRLayersWithMultipleFrameContextsDecode321Layer) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1,1/1,1/1");
|
||||
Pass1EncodeNFrames(10, 3, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_,
|
||||
"auto-alt-refs=1,1,1 scale-factors=1/1,1/1,1/1 "
|
||||
"multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 3, &outputs[0]);
|
||||
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 2);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
DropEnhancementLayers(&outputs[0], 10, 1);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2TemporalLayers) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1 scale-factors=1/1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2TemporalLayersWithMultipleFrameContexts) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_,
|
||||
"auto-alt-refs=1 scale-factors=1/1 "
|
||||
"multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2TemporalLayersDecodeBaseLayer) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1 scale-factors=1/1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
|
||||
aom_fixed_buf base_layer[5];
|
||||
for (int i = 0; i < 5; ++i) base_layer[i] = outputs[i * 2];
|
||||
|
||||
DecodeNFrames(&base_layer[0], 5);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest,
|
||||
TwoPassEncode2TemporalLayersWithMultipleFrameContextsDecodeBaseLayer) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
aom_svc_set_options(&svc_,
|
||||
"auto-alt-refs=1 scale-factors=1/1 "
|
||||
"multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
|
||||
aom_fixed_buf base_layer[5];
|
||||
for (int i = 0; i < 5; ++i) base_layer[i] = outputs[i * 2];
|
||||
|
||||
DecodeNFrames(&base_layer[0], 5);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2TemporalLayersWithTiles) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
aom_svc_set_options(&svc_, "auto-alt-refs=1 scale-factors=1/1");
|
||||
codec_enc_.g_w = 704;
|
||||
codec_enc_.g_h = 144;
|
||||
tile_columns_ = 1;
|
||||
tile_rows_ = 1;
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
TEST_F(SvcTest, TwoPassEncode2TemporalLayersWithMultipleFrameContextsAndTiles) {
|
||||
// First pass encode
|
||||
std::string stats_buf;
|
||||
aom_svc_set_options(&svc_, "scale-factors=1/1");
|
||||
svc_.temporal_layers = 2;
|
||||
Pass1EncodeNFrames(10, 1, &stats_buf);
|
||||
|
||||
// Second pass encode
|
||||
codec_enc_.g_pass = AOM_RC_LAST_PASS;
|
||||
svc_.temporal_layers = 2;
|
||||
codec_enc_.g_error_resilient = 0;
|
||||
codec_enc_.g_w = 704;
|
||||
codec_enc_.g_h = 144;
|
||||
tile_columns_ = 1;
|
||||
tile_rows_ = 1;
|
||||
aom_svc_set_options(&svc_,
|
||||
"auto-alt-refs=1 scale-factors=1/1 "
|
||||
"multi-frame-contexts=1");
|
||||
aom_fixed_buf outputs[10];
|
||||
memset(&outputs[0], 0, sizeof(outputs));
|
||||
Pass2EncodeNFrames(&stats_buf, 10, 1, &outputs[0]);
|
||||
DecodeNFrames(&outputs[0], 10);
|
||||
FreeBitstreamBuffers(&outputs[0], 10);
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -107,10 +107,6 @@ LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += variance_test.cc
|
|||
LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += quantize_test.cc
|
||||
LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += subtract_test.cc
|
||||
|
||||
ifeq ($(CONFIG_AV1_ENCODER),yes)
|
||||
LIBAOM_TEST_SRCS-$(CONFIG_SPATIAL_SVC) += svc_test.cc
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_AV1_ENCODER)$(CONFIG_AV1_TEMPORAL_DENOISING),yesyes)
|
||||
LIBAOM_TEST_SRCS-$(HAVE_SSE2) += denoiser_sse2_test.cc
|
||||
endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче