blob: af9da5915726a467a8ea126a2acc4ceec2a637a7 [file] [log] [blame]
/*
* Copyright (c) 2019, 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 <stdint.h>
#include "config/aom_config.h"
#include "config/aom_scale_rtcd.h"
#include "aom/aom_codec.h"
#include "aom/aom_encoder.h"
#include "aom_ports/system_state.h"
#include "av1/common/onyxc_int.h"
#include "av1/encoder/encoder.h"
#include "av1/encoder/firstpass.h"
#include "av1/encoder/gop_structure.h"
// Set parameters for frames between 'start' and 'end' (excluding both).
static void set_multi_layer_params(GF_GROUP *const gf_group, int start, int end,
int *frame_ind, int arf_ind, int level) {
assert(level >= MIN_PYRAMID_LVL);
const int num_frames_to_process = end - start - 1;
assert(num_frames_to_process >= 0);
if (num_frames_to_process == 0) return;
// Either we are at the last level of the pyramid, or we don't have enough
// frames between 'l' and 'r' to create one more level.
if (level == MIN_PYRAMID_LVL || num_frames_to_process < 3) {
// Leaf nodes.
while (++start < end) {
gf_group->update_type[*frame_ind] = LF_UPDATE;
gf_group->arf_src_offset[*frame_ind] = 0;
gf_group->arf_pos_in_gf[*frame_ind] = 0;
gf_group->arf_update_idx[*frame_ind] = arf_ind;
gf_group->frame_disp_idx[*frame_ind] = start;
gf_group->pyramid_level[*frame_ind] = MIN_PYRAMID_LVL;
++gf_group->pyramid_lvl_nodes[MIN_PYRAMID_LVL];
++(*frame_ind);
}
} else {
const int m = (start + end) / 2;
const int arf_pos_in_gf = *frame_ind;
// Internal ARF.
gf_group->update_type[*frame_ind] = INTNL_ARF_UPDATE;
gf_group->arf_src_offset[*frame_ind] = m - start - 1;
gf_group->arf_pos_in_gf[*frame_ind] = 0;
gf_group->arf_update_idx[*frame_ind] = 1; // mark all internal ARF 1
gf_group->frame_disp_idx[*frame_ind] = m;
gf_group->pyramid_level[*frame_ind] = level;
++gf_group->pyramid_lvl_nodes[level];
++(*frame_ind);
// Frames displayed before this internal ARF.
set_multi_layer_params(gf_group, start, m, frame_ind, 1, level - 1);
// Overlay for internal ARF.
gf_group->update_type[*frame_ind] = INTNL_OVERLAY_UPDATE;
gf_group->arf_src_offset[*frame_ind] = 0;
gf_group->arf_pos_in_gf[*frame_ind] = arf_pos_in_gf; // For bit allocation.
gf_group->arf_update_idx[*frame_ind] = 1;
gf_group->frame_disp_idx[*frame_ind] = m;
gf_group->pyramid_level[*frame_ind] = MIN_PYRAMID_LVL;
++(*frame_ind);
// Frames displayed after this internal ARF.
set_multi_layer_params(gf_group, m, end, frame_ind, arf_ind, level - 1);
}
}
static int construct_multi_layer_gf_structure(
GF_GROUP *const gf_group, int gf_interval, int pyr_height,
FRAME_UPDATE_TYPE first_frame_update_type) {
gf_group->pyramid_height = pyr_height;
av1_zero_array(gf_group->pyramid_lvl_nodes, MAX_PYRAMID_LVL);
int frame_index = 0;
// Keyframe / Overlay frame / Golden frame.
assert(gf_interval >= 1);
assert(first_frame_update_type == KF_UPDATE ||
first_frame_update_type == OVERLAY_UPDATE ||
first_frame_update_type == GF_UPDATE);
gf_group->update_type[frame_index] = first_frame_update_type;
gf_group->arf_src_offset[frame_index] = 0;
gf_group->arf_pos_in_gf[frame_index] = 0;
gf_group->arf_update_idx[frame_index] = 0;
gf_group->pyramid_level[frame_index] = MIN_PYRAMID_LVL;
++frame_index;
// ALTREF.
const int use_altref = (gf_group->pyramid_height > 0);
if (use_altref) {
gf_group->update_type[frame_index] = ARF_UPDATE;
gf_group->arf_src_offset[frame_index] = gf_interval - 1;
gf_group->arf_pos_in_gf[frame_index] = 0;
gf_group->arf_update_idx[frame_index] = 0;
gf_group->frame_disp_idx[frame_index] = gf_interval;
gf_group->pyramid_level[frame_index] = gf_group->pyramid_height;
++frame_index;
}
// Rest of the frames.
const int next_height =
use_altref ? gf_group->pyramid_height - 1 : gf_group->pyramid_height;
assert(next_height >= MIN_PYRAMID_LVL);
set_multi_layer_params(gf_group, 0, gf_interval, &frame_index, 0,
next_height);
return frame_index;
}
#define CHECK_GF_PARAMETER 0
#if CHECK_GF_PARAMETER
void check_frame_params(GF_GROUP *const gf_group, int gf_interval) {
static const char *update_type_strings[FRAME_UPDATE_TYPES] = {
"KF_UPDATE", "LF_UPDATE", "GF_UPDATE",
"ARF_UPDATE", "OVERLAY_UPDATE", "INTNL_OVERLAY_UPDATE",
"INTNL_ARF_UPDATE"
};
FILE *fid = fopen("GF_PARAMS.txt", "a");
fprintf(fid, "\ngf_interval = {%d}\n", gf_interval);
for (int i = 0; i <= gf_group->size; ++i) {
fprintf(fid, "#%2d : %s %d %d %d %d\n", i,
update_type_strings[gf_group->update_type[i]],
gf_group->arf_src_offset[i], gf_group->arf_pos_in_gf[i],
gf_group->arf_update_idx[i], gf_group->pyramid_level[i]);
}
fprintf(fid, "number of nodes in each level: \n");
for (int i = 0; i < gf_group->pyramid_height; ++i) {
fprintf(fid, "lvl %d: %d ", i, gf_group->pyramid_lvl_nodes[i]);
}
fprintf(fid, "\n");
fclose(fid);
}
#endif // CHECK_GF_PARAMETER
static INLINE int max_pyramid_height_from_width(int pyramid_width) {
if (pyramid_width > 12) return 4;
if (pyramid_width > 6) return 3;
if (pyramid_width > 3) return 2;
if (pyramid_width > 1) return 1;
return 0;
}
static int get_pyramid_height(const AV1_COMP *const cpi) {
const RATE_CONTROL *const rc = &cpi->rc;
assert(IMPLIES(cpi->oxcf.gf_max_pyr_height == MIN_PYRAMID_LVL,
!rc->source_alt_ref_pending)); // define_gf_group() enforced.
if (!rc->source_alt_ref_pending) {
return MIN_PYRAMID_LVL;
}
assert(cpi->oxcf.gf_max_pyr_height > MIN_PYRAMID_LVL);
if (!cpi->internal_altref_allowed) {
assert(MIN_PYRAMID_LVL + 1 <= cpi->oxcf.gf_max_pyr_height);
return MIN_PYRAMID_LVL + 1;
}
return AOMMIN(max_pyramid_height_from_width(rc->baseline_gf_interval),
cpi->oxcf.gf_max_pyr_height);
}
#define REF_IDX(ref) ((ref)-LAST_FRAME)
static INLINE void reset_ref_frame_idx(int *ref_idx, int reset_value) {
for (int i = 0; i < REF_FRAMES; ++i) ref_idx[i] = reset_value;
}
static INLINE void set_ref_frame_disp_idx(GF_GROUP *const gf_group) {
for (int i = 0; i <= gf_group->size; ++i) {
for (int ref = 0; ref < INTER_REFS_PER_FRAME + 1; ++ref) {
int ref_gop_idx = gf_group->ref_frame_gop_idx[i][ref];
if (ref_gop_idx == -1) {
gf_group->ref_frame_disp_idx[i][ref] = -1;
} else {
gf_group->ref_frame_disp_idx[i][ref] =
gf_group->frame_disp_idx[ref_gop_idx];
}
}
}
}
static void set_gop_ref_frame_map(GF_GROUP *const gf_group) {
// Initialize the reference slots as all -1.
for (int frame_idx = 0; frame_idx <= gf_group->size; ++frame_idx)
reset_ref_frame_idx(gf_group->ref_frame_gop_idx[frame_idx], -1);
// Set the map for frames in the current gop
for (int frame_idx = 0; frame_idx < gf_group->size; ++frame_idx) {
const FRAME_UPDATE_TYPE update_type = gf_group->update_type[frame_idx];
// TODO(yuec): need to figure out how to determine
// (1) whether a KEY_FRAME has show_frame on
// (2) whether a frame with INTNL_OVERLAY_UPDATE type has
// show_existing_frame on
const int show_frame =
update_type != ARF_UPDATE && update_type != INTNL_ARF_UPDATE;
const int show_existing_frame =
update_type == OVERLAY_UPDATE || update_type == INTNL_OVERLAY_UPDATE;
int this_ref_map[INTER_REFS_PER_FRAME + 1];
memcpy(this_ref_map, gf_group->ref_frame_gop_idx[frame_idx],
sizeof(this_ref_map));
int *next_ref_map = &gf_group->ref_frame_gop_idx[frame_idx + 1][0];
switch (update_type) {
case KF_UPDATE:
if (show_frame) {
reset_ref_frame_idx(this_ref_map, frame_idx);
} else {
this_ref_map[REF_IDX(LAST3_FRAME)] = frame_idx;
this_ref_map[REF_IDX(EXTREF_FRAME)] = frame_idx;
this_ref_map[REF_IDX(ALTREF2_FRAME)] = frame_idx;
this_ref_map[REF_IDX(GOLDEN_FRAME)] = frame_idx;
this_ref_map[REF_IDX(ALTREF_FRAME)] = frame_idx;
}
break;
case LF_UPDATE: this_ref_map[REF_IDX(LAST3_FRAME)] = frame_idx; break;
case GF_UPDATE:
this_ref_map[REF_IDX(LAST3_FRAME)] = frame_idx;
this_ref_map[REF_IDX(GOLDEN_FRAME)] = frame_idx;
break;
case OVERLAY_UPDATE:
this_ref_map[REF_IDX(ALTREF_FRAME)] = frame_idx;
break;
case ARF_UPDATE: this_ref_map[REF_IDX(ALTREF_FRAME)] = frame_idx; break;
case INTNL_OVERLAY_UPDATE:
if (!show_existing_frame)
this_ref_map[REF_IDX(LAST3_FRAME)] = frame_idx;
break;
case INTNL_ARF_UPDATE:
this_ref_map[REF_IDX(EXTREF_FRAME)] = frame_idx;
break;
default: assert(0); break;
}
memcpy(next_ref_map, this_ref_map, sizeof(this_ref_map));
switch (update_type) {
case LF_UPDATE:
case GF_UPDATE:
next_ref_map[REF_IDX(LAST3_FRAME)] = this_ref_map[REF_IDX(LAST2_FRAME)];
next_ref_map[REF_IDX(LAST2_FRAME)] = this_ref_map[REF_IDX(LAST_FRAME)];
next_ref_map[REF_IDX(LAST_FRAME)] = this_ref_map[REF_IDX(LAST3_FRAME)];
break;
case INTNL_OVERLAY_UPDATE:
if (!show_existing_frame) {
next_ref_map[REF_IDX(LAST3_FRAME)] =
this_ref_map[REF_IDX(LAST2_FRAME)];
next_ref_map[REF_IDX(LAST2_FRAME)] =
this_ref_map[REF_IDX(LAST_FRAME)];
next_ref_map[REF_IDX(LAST_FRAME)] =
this_ref_map[REF_IDX(LAST3_FRAME)];
} else {
next_ref_map[REF_IDX(LAST_FRAME)] =
this_ref_map[REF_IDX(BWDREF_FRAME)];
next_ref_map[REF_IDX(LAST2_FRAME)] =
this_ref_map[REF_IDX(LAST_FRAME)];
next_ref_map[REF_IDX(LAST3_FRAME)] =
this_ref_map[REF_IDX(LAST2_FRAME)];
next_ref_map[REF_IDX(BWDREF_FRAME)] =
this_ref_map[REF_IDX(ALTREF2_FRAME)];
next_ref_map[REF_IDX(ALTREF2_FRAME)] =
this_ref_map[REF_IDX(EXTREF_FRAME)];
next_ref_map[REF_IDX(EXTREF_FRAME)] =
this_ref_map[REF_IDX(LAST3_FRAME)];
}
break;
case INTNL_ARF_UPDATE:
if (!show_existing_frame) {
next_ref_map[REF_IDX(BWDREF_FRAME)] =
this_ref_map[REF_IDX(EXTREF_FRAME)];
next_ref_map[REF_IDX(ALTREF2_FRAME)] =
this_ref_map[REF_IDX(BWDREF_FRAME)];
next_ref_map[REF_IDX(EXTREF_FRAME)] =
this_ref_map[REF_IDX(ALTREF2_FRAME)];
}
break;
case OVERLAY_UPDATE:
next_ref_map[REF_IDX(ALTREF_FRAME)] =
this_ref_map[REF_IDX(GOLDEN_FRAME)];
next_ref_map[REF_IDX(GOLDEN_FRAME)] =
this_ref_map[REF_IDX(ALTREF_FRAME)];
break;
default: break;
}
}
// Set the map in display order index by converting from gop indices in the
// above map
set_ref_frame_disp_idx(gf_group);
}
void av1_gop_setup_structure(AV1_COMP *cpi,
const EncodeFrameParams *const frame_params) {
RATE_CONTROL *const rc = &cpi->rc;
TWO_PASS *const twopass = &cpi->twopass;
GF_GROUP *const gf_group = &twopass->gf_group;
const int key_frame = (frame_params->frame_type == KEY_FRAME);
const FRAME_UPDATE_TYPE first_frame_update_type =
key_frame ? KF_UPDATE
: rc->source_alt_ref_active ? OVERLAY_UPDATE : GF_UPDATE;
gf_group->size = construct_multi_layer_gf_structure(
gf_group, rc->baseline_gf_interval, get_pyramid_height(cpi),
first_frame_update_type);
// We need to configure the frame at the end of the sequence + 1 that
// will be the start frame for the next group. Otherwise prior to the
// call to av1_get_second_pass_params(), the data will be undefined.
gf_group->update_type[gf_group->size] =
(rc->source_alt_ref_pending) ? OVERLAY_UPDATE : GF_UPDATE;
gf_group->arf_update_idx[gf_group->size] = 0;
gf_group->arf_pos_in_gf[gf_group->size] = 0;
set_gop_ref_frame_map(gf_group);
#if CHECK_GF_PARAMETER
check_frame_params(gf_group, rc->baseline_gf_interval);
#endif
}