2014-01-24 10:37:15 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* This file is part of Kvazaar HEVC encoder.
|
2014-02-21 13:00:20 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
|
2014-01-24 10:37:15 +00:00
|
|
|
* COPYING file).
|
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Kvazaar is free software: you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU Lesser General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2.1 of the License, or (at your
|
|
|
|
* option) any later version.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* Kvazaar is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
|
|
* more details.
|
2014-01-24 10:37:15 +00:00
|
|
|
*
|
2015-02-23 11:18:48 +00:00
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with Kvazaar. If not, see <http://www.gnu.org/licenses/>.
|
2014-01-24 10:37:15 +00:00
|
|
|
****************************************************************************/
|
|
|
|
|
2013-09-18 09:16:03 +00:00
|
|
|
#include "encoder.h"
|
|
|
|
|
2012-06-05 11:01:47 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2013-09-18 09:16:03 +00:00
|
|
|
|
2016-01-25 10:08:27 +00:00
|
|
|
#include "cfg.h"
|
2016-09-28 20:05:21 +00:00
|
|
|
#include "strategyselector.h"
|
2016-04-01 14:14:23 +00:00
|
|
|
|
2012-06-05 11:01:47 +00:00
|
|
|
|
2015-05-27 12:41:45 +00:00
|
|
|
static int encoder_control_init_gop_layer_weights(encoder_control_t * const);
|
2014-05-08 10:00:23 +00:00
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
static int size_of_wpp_ends(int threads)
|
|
|
|
{
|
|
|
|
// Based on the shape of the area where all threads can't yet run in parallel.
|
|
|
|
return 4 * threads * threads - 2 * threads;
|
|
|
|
}
|
|
|
|
|
2015-06-30 08:03:26 +00:00
|
|
|
static int select_owf_auto(const kvz_config *const cfg)
|
2015-06-30 06:14:31 +00:00
|
|
|
{
|
2016-09-27 20:14:57 +00:00
|
|
|
if (cfg->intra_period == 1) {
|
|
|
|
if (cfg->wpp) {
|
|
|
|
// If wpp is on, select owf such that less than 15% of the
|
|
|
|
// frame is covered by the are threads can not work at the same time.
|
|
|
|
const int lcu_width = CEILDIV(cfg->width, LCU_WIDTH);
|
|
|
|
const int lcu_height = CEILDIV(cfg->height, LCU_WIDTH);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-27 20:14:57 +00:00
|
|
|
// Find the largest number of threads per frame that satifies the
|
|
|
|
// the condition: wpp start/stop inefficiency takes up less than 15%
|
|
|
|
// of frame area.
|
|
|
|
int threads_per_frame = 1;
|
|
|
|
const int wpp_treshold = lcu_width * lcu_height * 15 / 100;
|
|
|
|
while ((threads_per_frame + 1) * 2 < lcu_width &&
|
|
|
|
threads_per_frame + 1 < lcu_height &&
|
|
|
|
size_of_wpp_ends(threads_per_frame + 1) < wpp_treshold) {
|
|
|
|
++threads_per_frame;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-27 20:14:57 +00:00
|
|
|
const int threads = MAX(cfg->threads, 1);
|
|
|
|
const int frames = CEILDIV(threads, threads_per_frame);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-27 20:14:57 +00:00
|
|
|
// Convert from number of parallel frames to number of additional frames.
|
|
|
|
return CLIP(0, threads - 1, frames - 1);
|
|
|
|
} else {
|
|
|
|
// If wpp is not on, select owf such that there is enough
|
|
|
|
// tiles for twice the number of threads.
|
|
|
|
|
|
|
|
int tiles_per_frame = cfg->tiles_width_count * cfg->tiles_height_count;
|
|
|
|
int threads = (cfg->threads > 1 ? cfg->threads : 1);
|
|
|
|
int frames = CEILDIV(threads * 4, tiles_per_frame);
|
|
|
|
|
|
|
|
// Limit number of frames to 1.25x the number of threads for the case
|
|
|
|
// where there is only 1 tile per frame.
|
|
|
|
frames = CLIP(1, threads * 4 / 3, frames);
|
|
|
|
return frames - 1;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
} else {
|
2016-09-27 20:14:57 +00:00
|
|
|
// Try and estimate a good number of parallel frames for inter.
|
|
|
|
const int lcu_width = CEILDIV(cfg->width, LCU_WIDTH);
|
|
|
|
const int lcu_height = CEILDIV(cfg->height, LCU_WIDTH);
|
|
|
|
int threads_per_frame = MIN(lcu_width / 2, lcu_height);
|
|
|
|
int threads = cfg->threads;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-27 20:14:57 +00:00
|
|
|
// If all threads fit into one frame, at least two parallel frames should
|
|
|
|
// be used to reduce the effect of WPP spin-up and wind-down.
|
|
|
|
int frames = 1;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-27 20:14:57 +00:00
|
|
|
while (threads > 0 && threads_per_frame > 0) {
|
|
|
|
frames += 1;
|
|
|
|
threads -= threads_per_frame;
|
|
|
|
threads_per_frame -= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->gop_lowdelay && cfg->gop_lp_definition.t > 1) {
|
|
|
|
// Temporal skipping makes every other frame very fast to encode so
|
|
|
|
// more parallel frames should be used.
|
|
|
|
frames *= 2;
|
|
|
|
}
|
|
|
|
return CLIP(0, cfg->threads * 2 - 1, frames - 1);
|
2015-06-30 06:14:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-28 20:05:21 +00:00
|
|
|
|
|
|
|
static unsigned cfg_num_threads(void)
|
|
|
|
{
|
|
|
|
unsigned cpus = kvz_g_hardware_flags.physical_cpu_count;
|
|
|
|
unsigned fake_cpus = kvz_g_hardware_flags.logical_cpu_count - cpus;
|
|
|
|
|
|
|
|
// Default to 4 if we don't know the number of CPUs.
|
|
|
|
if (cpus == 0) return 4;
|
|
|
|
|
|
|
|
// 1.5 times the number of physical cores seems to be a good compromise
|
|
|
|
// when hyperthreading is available on Haswell.
|
|
|
|
return cpus + fake_cpus / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
/**
|
|
|
|
* \brief Allocate and initialize an encoder control structure.
|
|
|
|
*
|
|
|
|
* \param cfg encoder configuration
|
|
|
|
* \return initialized encoder control or NULL on failure
|
|
|
|
*/
|
2016-09-27 19:12:02 +00:00
|
|
|
encoder_control_t* kvz_encoder_control_init(kvz_config *const cfg) {
|
2015-06-30 06:14:31 +00:00
|
|
|
encoder_control_t *encoder = NULL;
|
|
|
|
|
2014-01-31 18:34:50 +00:00
|
|
|
if (!cfg) {
|
|
|
|
fprintf(stderr, "Config object must not be null!\n");
|
2015-06-30 06:14:31 +00:00
|
|
|
goto init_failed;
|
2014-01-31 18:34:50 +00:00
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-09-28 20:05:21 +00:00
|
|
|
if (cfg->threads == -1) {
|
|
|
|
cfg->threads = cfg_num_threads();
|
|
|
|
}
|
|
|
|
|
2016-09-27 19:12:02 +00:00
|
|
|
if (cfg->gop_len > 0) {
|
|
|
|
if (cfg->tmvp_enable) {
|
|
|
|
cfg->tmvp_enable = false;
|
|
|
|
fprintf(stderr, "Disabling TMVP because GOP is used.\n");
|
|
|
|
}
|
|
|
|
if (cfg->gop_lowdelay) {
|
|
|
|
kvz_config_process_lp_gop(cfg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
// Make sure that the parameters make sense.
|
2015-08-26 08:50:27 +00:00
|
|
|
if (!kvz_config_validate(cfg)) {
|
2015-06-30 06:14:31 +00:00
|
|
|
goto init_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
encoder = calloc(1, sizeof(encoder_control_t));
|
|
|
|
if (!encoder) {
|
|
|
|
fprintf(stderr, "Failed to allocate encoder control.\n");
|
|
|
|
goto init_failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to set owf before initializing threadqueue.
|
|
|
|
if (cfg->owf >= 0) {
|
|
|
|
encoder->owf = cfg->owf;
|
|
|
|
} else {
|
|
|
|
encoder->owf = select_owf_auto(cfg);
|
|
|
|
fprintf(stderr, "--owf=auto value set to %d.\n", encoder->owf);
|
|
|
|
}
|
2016-01-25 17:05:10 +00:00
|
|
|
if (cfg->source_scan_type != KVZ_INTERLACING_NONE) {
|
|
|
|
// If using interlaced coding with OWF, the OWF has to be an even number
|
|
|
|
// to ensure that the pair of fields will be output for the same picture.
|
|
|
|
if (encoder->owf % 2 == 1) {
|
|
|
|
encoder->owf += 1;
|
|
|
|
}
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2015-03-04 14:30:20 +00:00
|
|
|
encoder->threadqueue = MALLOC(threadqueue_queue_t, 1);
|
2015-06-30 06:14:31 +00:00
|
|
|
if (!encoder->threadqueue ||
|
2015-08-26 08:50:27 +00:00
|
|
|
!kvz_threadqueue_init(encoder->threadqueue,
|
2015-06-30 06:14:31 +00:00
|
|
|
cfg->threads,
|
|
|
|
encoder->owf > 0)) {
|
|
|
|
fprintf(stderr, "Could not initialize threadqueue.\n");
|
|
|
|
goto init_failed;
|
2014-05-13 09:28:15 +00:00
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-17 12:42:20 +00:00
|
|
|
// Config pointer to config struct
|
|
|
|
encoder->cfg = cfg;
|
2015-08-11 05:42:09 +00:00
|
|
|
|
|
|
|
encoder->bitdepth = KVZ_BIT_DEPTH;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-08-16 16:03:21 +00:00
|
|
|
encoder->chroma_format = KVZ_FORMAT2CSP(cfg->input_format);
|
|
|
|
|
2014-01-31 18:34:50 +00:00
|
|
|
// deblocking filter
|
2014-04-17 12:42:20 +00:00
|
|
|
encoder->deblock_enable = 1;
|
|
|
|
encoder->beta_offset_div2 = 0;
|
|
|
|
encoder->tc_offset_div2 = 0;
|
2014-01-31 18:34:50 +00:00
|
|
|
// SAO
|
2014-04-17 12:42:20 +00:00
|
|
|
encoder->sao_enable = 1;
|
2014-04-07 11:36:01 +00:00
|
|
|
// Rate-distortion optimization level
|
2014-04-17 12:42:20 +00:00
|
|
|
encoder->rdo = 1;
|
2014-06-17 12:32:05 +00:00
|
|
|
encoder->full_intra_search = 0;
|
2016-01-22 10:44:04 +00:00
|
|
|
|
2017-01-31 13:11:50 +00:00
|
|
|
encoder->pps.dependent_slice_segments_enabled_flag = 0;
|
|
|
|
|
2016-01-22 10:44:04 +00:00
|
|
|
// Interlacing
|
2015-08-13 09:53:14 +00:00
|
|
|
encoder->in.source_scan_type = (int8_t)cfg->source_scan_type;
|
2016-01-22 10:44:04 +00:00
|
|
|
encoder->vui.field_seq_flag = encoder->cfg->source_scan_type != 0;
|
|
|
|
encoder->vui.frame_field_info_present_flag = encoder->cfg->source_scan_type != 0;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-01-31 18:34:50 +00:00
|
|
|
// Initialize the scaling list
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_scalinglist_init(&encoder->scaling_list);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-15 12:20:26 +00:00
|
|
|
// CQM
|
2016-10-31 02:33:14 +00:00
|
|
|
if (cfg->cqmfile) {
|
|
|
|
FILE* cqmfile = fopen(cfg->cqmfile, "rb");
|
2014-04-15 12:20:26 +00:00
|
|
|
if (cqmfile) {
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_scalinglist_parse(&encoder->scaling_list, cqmfile);
|
2014-04-15 12:20:26 +00:00
|
|
|
fclose(cqmfile);
|
2016-10-31 02:33:14 +00:00
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Could not open CQM file.\n");
|
|
|
|
goto init_failed;
|
2014-04-15 12:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_scalinglist_process(&encoder->scaling_list, encoder->bitdepth);
|
2016-10-31 02:33:14 +00:00
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_encoder_control_input_init(encoder, cfg->width, cfg->height);
|
2015-05-27 12:41:45 +00:00
|
|
|
|
2016-01-14 20:08:35 +00:00
|
|
|
if (cfg->framerate_num != 0) {
|
|
|
|
double framerate = cfg->framerate_num / (double)cfg->framerate_denom;
|
|
|
|
encoder->target_avg_bppic = cfg->target_bitrate / (framerate);
|
|
|
|
} else {
|
|
|
|
encoder->target_avg_bppic = cfg->target_bitrate / cfg->framerate;
|
|
|
|
}
|
2015-05-27 12:41:45 +00:00
|
|
|
encoder->target_avg_bpp = encoder->target_avg_bppic / encoder->in.pixels_per_pic;
|
|
|
|
|
|
|
|
if (!encoder_control_init_gop_layer_weights(encoder)) {
|
2015-06-30 06:14:31 +00:00
|
|
|
goto init_failed;
|
2015-05-27 12:41:45 +00:00
|
|
|
}
|
2016-01-14 09:48:35 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//Tiles
|
2016-03-07 15:21:40 +00:00
|
|
|
encoder->tiles_enable = encoder->cfg->tiles_width_count > 1 ||
|
|
|
|
encoder->cfg->tiles_height_count > 1;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-01 12:10:22 +00:00
|
|
|
{
|
2014-04-02 09:31:12 +00:00
|
|
|
int i, j; //iteration variables
|
|
|
|
const int num_ctbs = encoder->in.width_in_lcu * encoder->in.height_in_lcu;
|
|
|
|
int tileIdx, x, y; //iterations variable for 6-9
|
|
|
|
|
|
|
|
//Temporary pointers to allow encoder fields to be const
|
|
|
|
int32_t *tiles_col_width, *tiles_row_height, *tiles_ctb_addr_rs_to_ts, *tiles_ctb_addr_ts_to_rs, *tiles_tile_id, *tiles_col_bd, *tiles_row_bd;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2016-03-07 15:21:40 +00:00
|
|
|
if (encoder->cfg->tiles_width_count > encoder->in.width_in_lcu) {
|
2014-05-01 05:11:31 +00:00
|
|
|
fprintf(stderr, "Too many tiles (width)!\n");
|
2015-06-30 06:14:31 +00:00
|
|
|
goto init_failed;
|
|
|
|
|
2016-03-07 15:21:40 +00:00
|
|
|
} else if (encoder->cfg->tiles_height_count > encoder->in.height_in_lcu) {
|
2014-05-01 05:11:31 +00:00
|
|
|
fprintf(stderr, "Too many tiles (height)!\n");
|
2015-06-30 06:14:31 +00:00
|
|
|
goto init_failed;
|
2014-05-01 05:11:31 +00:00
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//Will be (perhaps) changed later
|
|
|
|
encoder->tiles_uniform_spacing_flag = 1;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//tilesn[x,y] contains the number of _separation_ between tiles, whereas the encoder needs the number of tiles.
|
2016-03-07 15:21:40 +00:00
|
|
|
encoder->tiles_num_tile_columns = encoder->cfg->tiles_width_count;
|
|
|
|
encoder->tiles_num_tile_rows = encoder->cfg->tiles_height_count;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
|
|
|
encoder->tiles_col_width = tiles_col_width =
|
|
|
|
MALLOC(int32_t, encoder->tiles_num_tile_columns);
|
|
|
|
encoder->tiles_row_height = tiles_row_height =
|
|
|
|
MALLOC(int32_t, encoder->tiles_num_tile_rows);
|
|
|
|
|
|
|
|
encoder->tiles_col_bd = tiles_col_bd =
|
2015-07-06 07:48:19 +00:00
|
|
|
MALLOC(int32_t, encoder->tiles_num_tile_columns + 1);
|
|
|
|
encoder->tiles_row_bd = tiles_row_bd =
|
2015-06-30 06:14:31 +00:00
|
|
|
MALLOC(int32_t, encoder->tiles_num_tile_rows + 1);
|
|
|
|
|
|
|
|
encoder->tiles_ctb_addr_rs_to_ts = tiles_ctb_addr_rs_to_ts =
|
|
|
|
MALLOC(int32_t, num_ctbs);
|
|
|
|
encoder->tiles_ctb_addr_ts_to_rs = tiles_ctb_addr_ts_to_rs =
|
|
|
|
MALLOC(int32_t, num_ctbs);
|
|
|
|
encoder->tiles_tile_id = tiles_tile_id =
|
|
|
|
MALLOC(int32_t, num_ctbs);
|
|
|
|
|
|
|
|
if (!tiles_col_width ||
|
|
|
|
!tiles_row_height ||
|
|
|
|
!tiles_row_bd ||
|
|
|
|
!tiles_col_bd ||
|
|
|
|
!tiles_ctb_addr_rs_to_ts ||
|
|
|
|
!tiles_ctb_addr_ts_to_rs ||
|
|
|
|
!tiles_tile_id) {
|
|
|
|
goto init_failed;
|
|
|
|
}
|
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//(6-3) and (6-4) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
if (!encoder->cfg->tiles_width_split) {
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_columns; ++i) {
|
|
|
|
tiles_col_width[i] = ((i+1) * encoder->in.width_in_lcu) / encoder->tiles_num_tile_columns -
|
|
|
|
i * encoder->in.width_in_lcu / encoder->tiles_num_tile_columns;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int32_t last_pos_in_px = 0;
|
|
|
|
tiles_col_width[encoder->tiles_num_tile_columns-1] = encoder->in.width_in_lcu;
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_columns - 1; ++i) {
|
|
|
|
int32_t column_width_in_lcu = (cfg->tiles_width_split[i] - last_pos_in_px) / LCU_WIDTH;
|
|
|
|
last_pos_in_px = cfg->tiles_width_split[i];
|
|
|
|
tiles_col_width[i] = column_width_in_lcu;
|
|
|
|
tiles_col_width[encoder->tiles_num_tile_columns - 1] -= column_width_in_lcu;
|
|
|
|
}
|
|
|
|
encoder->tiles_uniform_spacing_flag = 0;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
if (!encoder->cfg->tiles_height_split) {
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_rows; ++i) {
|
|
|
|
tiles_row_height[i] = ((i+1) * encoder->in.height_in_lcu) / encoder->tiles_num_tile_rows -
|
|
|
|
i * encoder->in.height_in_lcu / encoder->tiles_num_tile_rows;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int32_t last_pos_in_px = 0;
|
|
|
|
tiles_row_height[encoder->tiles_num_tile_rows-1] = encoder->in.height_in_lcu;
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_rows - 1; ++i) {
|
|
|
|
int32_t row_height_in_lcu = (cfg->tiles_height_split[i] - last_pos_in_px) / LCU_WIDTH;
|
|
|
|
last_pos_in_px = cfg->tiles_height_split[i];
|
|
|
|
tiles_row_height[i] = row_height_in_lcu;
|
|
|
|
tiles_row_height[encoder->tiles_num_tile_rows - 1] -= row_height_in_lcu;
|
|
|
|
}
|
|
|
|
encoder->tiles_uniform_spacing_flag = 0;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//(6-5) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
tiles_col_bd[0] = 0;
|
|
|
|
for (i = 0; i < encoder->tiles_num_tile_columns; ++i) {
|
|
|
|
tiles_col_bd[i+1] = tiles_col_bd[i] + tiles_col_width[i];
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//(6-6) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
tiles_row_bd[0] = 0;
|
|
|
|
for (i = 0; i < encoder->tiles_num_tile_rows; ++i) {
|
|
|
|
tiles_row_bd[i+1] = tiles_row_bd[i] + tiles_row_height[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
//(6-7) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
//j == ctbAddrRs
|
|
|
|
for (j = 0; j < num_ctbs; ++j) {
|
2014-04-24 06:41:05 +00:00
|
|
|
int tileX = 0, tileY = 0;
|
2014-04-02 09:31:12 +00:00
|
|
|
int tbX = j % encoder->in.width_in_lcu;
|
|
|
|
int tbY = j / encoder->in.width_in_lcu;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
for (i = 0; i < encoder->tiles_num_tile_columns; ++i) {
|
|
|
|
if (tbX >= tiles_col_bd[i]) tileX = i;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
for (i = 0; i < encoder->tiles_num_tile_rows; ++i) {
|
|
|
|
if (tbY >= tiles_row_bd[i]) tileY = i;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
tiles_ctb_addr_rs_to_ts[j] = 0;
|
|
|
|
for (i = 0; i < tileX; ++i) {
|
|
|
|
tiles_ctb_addr_rs_to_ts[j] += tiles_row_height[tileY] * tiles_col_width[i];
|
|
|
|
}
|
|
|
|
for (i = 0; i < tileY; ++i) {
|
|
|
|
tiles_ctb_addr_rs_to_ts[j] += encoder->in.width_in_lcu * tiles_row_height[i];
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
tiles_ctb_addr_rs_to_ts[j] += (tbY - tiles_row_bd[tileY]) * tiles_col_width[tileX] +
|
2014-04-02 09:31:12 +00:00
|
|
|
tbX - tiles_col_bd[tileX];
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//(6-8) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
//Make reverse map from tile scan to raster scan
|
|
|
|
for (j = 0; j < num_ctbs; ++j) {
|
|
|
|
tiles_ctb_addr_ts_to_rs[tiles_ctb_addr_rs_to_ts[j]] = j;
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//(6-9) in ITU-T Rec. H.265 (04/2013)
|
|
|
|
tileIdx = 0;
|
|
|
|
for (j=0; j < encoder->tiles_num_tile_rows; ++j) {
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_columns; ++i) {
|
|
|
|
for (y = tiles_row_bd[j]; y < tiles_row_bd[j+1]; ++y) {
|
|
|
|
for (x = tiles_col_bd[i]; x < tiles_col_bd[i+1]; ++x) {
|
|
|
|
tiles_tile_id[tiles_ctb_addr_rs_to_ts[y * encoder->in.width_in_lcu + x]] = tileIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++tileIdx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
//Slices
|
|
|
|
{
|
|
|
|
int *slice_addresses_in_ts;
|
|
|
|
encoder->slice_count = encoder->cfg->slice_count;
|
|
|
|
if (encoder->slice_count == 0) {
|
|
|
|
encoder->slice_count = 1;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
|
|
|
encoder->slice_addresses_in_ts = slice_addresses_in_ts =
|
|
|
|
MALLOC(int, encoder->slice_count);
|
|
|
|
if (!slice_addresses_in_ts) goto init_failed;
|
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
slice_addresses_in_ts[0] = 0;
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
} else {
|
2015-06-30 06:14:31 +00:00
|
|
|
encoder->slice_addresses_in_ts = slice_addresses_in_ts =
|
|
|
|
MALLOC(int, encoder->slice_count);
|
|
|
|
if (!slice_addresses_in_ts) goto init_failed;
|
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
if (!encoder->cfg->slice_addresses_in_ts) {
|
|
|
|
slice_addresses_in_ts[0] = 0;
|
2015-06-30 06:14:31 +00:00
|
|
|
for (int i=1; i < encoder->slice_count; ++i) {
|
2014-05-05 13:17:22 +00:00
|
|
|
slice_addresses_in_ts[i] = encoder->in.width_in_lcu * encoder->in.height_in_lcu * i / encoder->slice_count;
|
|
|
|
}
|
|
|
|
} else {
|
2015-06-30 06:14:31 +00:00
|
|
|
for (int i=0; i < encoder->slice_count; ++i) {
|
2014-05-05 13:17:22 +00:00
|
|
|
slice_addresses_in_ts[i] = encoder->cfg->slice_addresses_in_ts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
encoder->wpp = encoder->cfg->wpp;
|
2014-04-02 09:31:12 +00:00
|
|
|
|
2015-09-10 10:27:50 +00:00
|
|
|
#ifdef _DEBUG_PRINT_THREADING_INFO
|
2014-04-22 12:49:39 +00:00
|
|
|
printf("Tiles columns width:");
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_columns; ++i) {
|
|
|
|
printf(" %d", encoder->tiles_col_width[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
printf("Tiles row height:");
|
|
|
|
for (i=0; i < encoder->tiles_num_tile_rows; ++i) {
|
|
|
|
printf(" %d", encoder->tiles_row_height[i]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
//Print tile index map
|
|
|
|
for (y = 0; y < encoder->in.height_in_lcu; ++y) {
|
|
|
|
for (x = 0; x < encoder->in.width_in_lcu; ++x) {
|
2014-05-05 13:17:22 +00:00
|
|
|
const int lcu_id_rs = y * encoder->in.width_in_lcu + x;
|
|
|
|
const int lcu_id_ts = encoder->tiles_ctb_addr_rs_to_ts[lcu_id_rs];
|
2015-08-26 08:50:27 +00:00
|
|
|
const char slice_start = kvz_lcu_at_slice_start(encoder, lcu_id_ts) ? '|' : ' ';
|
|
|
|
const char slice_end = kvz_lcu_at_slice_end(encoder, lcu_id_ts) ? '|' : ' ';
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
printf("%c%03d%c", slice_start, encoder->tiles_tile_id[lcu_id_ts], slice_end);
|
2014-04-22 12:49:39 +00:00
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
2014-05-05 13:17:22 +00:00
|
|
|
printf("\n");
|
|
|
|
if (encoder->wpp) {
|
|
|
|
printf("Wavefront Parallel Processing: enabled\n");
|
|
|
|
} else {
|
|
|
|
printf("Wavefront Parallel Processing: disabled\n");
|
|
|
|
}
|
|
|
|
printf("\n");
|
2015-09-14 09:43:28 +00:00
|
|
|
#endif //KVZ_DEBUG
|
2014-04-02 09:31:12 +00:00
|
|
|
}
|
2015-05-15 11:04:56 +00:00
|
|
|
|
2015-01-09 10:04:23 +00:00
|
|
|
assert(WITHIN(cfg->pu_depth_inter.min, PU_DEPTH_INTER_MIN, PU_DEPTH_INTER_MAX));
|
|
|
|
assert(WITHIN(cfg->pu_depth_inter.max, PU_DEPTH_INTER_MIN, PU_DEPTH_INTER_MAX));
|
2015-04-02 07:31:56 +00:00
|
|
|
assert(WITHIN(cfg->pu_depth_intra.min, PU_DEPTH_INTRA_MIN, PU_DEPTH_INTRA_MAX));
|
|
|
|
assert(WITHIN(cfg->pu_depth_intra.max, PU_DEPTH_INTRA_MIN, PU_DEPTH_INTRA_MAX));
|
2015-01-09 10:04:23 +00:00
|
|
|
encoder->pu_depth_inter.min = cfg->pu_depth_inter.min;
|
|
|
|
encoder->pu_depth_inter.max = cfg->pu_depth_inter.max;
|
|
|
|
encoder->pu_depth_intra.min = cfg->pu_depth_intra.min;
|
|
|
|
encoder->pu_depth_intra.max = cfg->pu_depth_intra.max;
|
|
|
|
|
2015-05-15 11:04:56 +00:00
|
|
|
// deblocking filter
|
2016-05-30 11:26:13 +00:00
|
|
|
encoder->deblock_enable = (int8_t) (encoder->cfg->deblock_enable &&
|
|
|
|
!encoder->cfg->lossless);
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->beta_offset_div2 = (int8_t) encoder->cfg->deblock_beta;
|
|
|
|
encoder->tc_offset_div2 = (int8_t) encoder->cfg->deblock_tc;
|
2015-05-15 11:04:56 +00:00
|
|
|
// SAO
|
2016-05-30 11:26:13 +00:00
|
|
|
encoder->sao_enable = (int8_t) (encoder->cfg->sao_enable &&
|
|
|
|
!encoder->cfg->lossless);
|
2015-05-15 11:04:56 +00:00
|
|
|
// RDO
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->rdoq_enable = (int8_t) encoder->cfg->rdoq_enable;
|
|
|
|
encoder->rdo = (int8_t) encoder->cfg->rdo;
|
2016-05-30 11:26:13 +00:00
|
|
|
encoder->sign_hiding = (encoder->cfg->signhide_enable &&
|
|
|
|
!encoder->cfg->lossless);
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->full_intra_search = (int8_t) encoder->cfg->full_intra_search;
|
2015-05-15 11:04:56 +00:00
|
|
|
// TR SKIP
|
2016-05-30 11:26:13 +00:00
|
|
|
encoder->trskip_enable = (int8_t) (encoder->cfg->trskip_enable &&
|
|
|
|
!encoder->cfg->lossless);
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->tr_depth_intra = (int8_t) encoder->cfg->tr_depth_intra;
|
2015-05-15 11:04:56 +00:00
|
|
|
// MOTION ESTIMATION
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->fme_level = (int8_t) encoder->cfg->fme_level;
|
2015-05-15 11:04:56 +00:00
|
|
|
// VUI
|
2016-06-03 00:59:11 +00:00
|
|
|
encoder->vui.sar_width = (int16_t) encoder->cfg->vui.sar_width;
|
|
|
|
encoder->vui.sar_height = (int16_t) encoder->cfg->vui.sar_height;
|
|
|
|
encoder->vui.overscan = encoder->cfg->vui.overscan;
|
|
|
|
encoder->vui.videoformat = encoder->cfg->vui.videoformat;
|
|
|
|
encoder->vui.fullrange = encoder->cfg->vui.fullrange;
|
|
|
|
encoder->vui.colorprim = encoder->cfg->vui.colorprim;
|
|
|
|
encoder->vui.transfer = encoder->cfg->vui.transfer;
|
|
|
|
encoder->vui.colormatrix = encoder->cfg->vui.colormatrix;
|
|
|
|
encoder->vui.chroma_loc = (int8_t) encoder->cfg->vui.chroma_loc;
|
2016-01-14 20:08:35 +00:00
|
|
|
|
|
|
|
// If fractional framerate is set, use that instead of the floating point framerate.
|
|
|
|
if (cfg->framerate_num != 0) {
|
|
|
|
encoder->vui.timing_info_present_flag = 1;
|
|
|
|
encoder->vui.num_units_in_tick = cfg->framerate_denom;
|
|
|
|
encoder->vui.time_scale = cfg->framerate_num;
|
2016-01-28 19:04:52 +00:00
|
|
|
if (cfg->source_scan_type != KVZ_INTERLACING_NONE) {
|
|
|
|
// when field_seq_flag=1, the time_scale and num_units_in_tick refer to
|
|
|
|
// field rate rather than frame rate.
|
|
|
|
encoder->vui.time_scale *= 2;
|
|
|
|
}
|
2016-01-14 20:08:35 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 11:04:56 +00:00
|
|
|
// AUD
|
|
|
|
encoder->aud_enable = (int8_t)encoder->cfg->aud_enable;
|
|
|
|
|
2015-10-15 12:11:12 +00:00
|
|
|
if (encoder->cfg->vps_period >= 0) {
|
|
|
|
encoder->vps_period = encoder->cfg->vps_period * encoder->cfg->intra_period;
|
|
|
|
} else {
|
|
|
|
encoder->vps_period = -1;
|
|
|
|
}
|
2015-05-15 11:04:56 +00:00
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
return encoder;
|
|
|
|
|
|
|
|
init_failed:
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_encoder_control_free(encoder);
|
2015-06-30 06:14:31 +00:00
|
|
|
return NULL;
|
2014-04-17 12:42:20 +00:00
|
|
|
}
|
2014-01-31 18:34:50 +00:00
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
/**
|
|
|
|
* \brief Free an encoder control structure.
|
|
|
|
*/
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_encoder_control_free(encoder_control_t *const encoder) {
|
2015-06-30 06:14:31 +00:00
|
|
|
if (!encoder) return;
|
|
|
|
|
2014-05-05 13:17:22 +00:00
|
|
|
//Slices
|
|
|
|
FREE_POINTER(encoder->slice_addresses_in_ts);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-04-02 09:31:12 +00:00
|
|
|
//Tiles
|
2014-05-01 12:10:22 +00:00
|
|
|
FREE_POINTER(encoder->tiles_col_width);
|
|
|
|
FREE_POINTER(encoder->tiles_row_height);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-01 12:10:22 +00:00
|
|
|
FREE_POINTER(encoder->tiles_col_bd);
|
|
|
|
FREE_POINTER(encoder->tiles_row_bd);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-01 12:10:22 +00:00
|
|
|
FREE_POINTER(encoder->tiles_ctb_addr_rs_to_ts);
|
|
|
|
FREE_POINTER(encoder->tiles_ctb_addr_ts_to_rs);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2014-05-01 12:10:22 +00:00
|
|
|
FREE_POINTER(encoder->tiles_tile_id);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_scalinglist_destroy(&encoder->scaling_list);
|
2015-06-30 06:14:31 +00:00
|
|
|
|
|
|
|
if (encoder->threadqueue) {
|
2015-08-26 08:50:27 +00:00
|
|
|
kvz_threadqueue_finalize(encoder->threadqueue);
|
2014-05-13 09:28:15 +00:00
|
|
|
}
|
|
|
|
FREE_POINTER(encoder->threadqueue);
|
|
|
|
|
2015-06-30 06:14:31 +00:00
|
|
|
free(encoder);
|
2014-04-17 12:42:20 +00:00
|
|
|
}
|
2014-01-31 18:34:50 +00:00
|
|
|
|
2015-08-26 08:50:27 +00:00
|
|
|
void kvz_encoder_control_input_init(encoder_control_t * const encoder,
|
2015-08-18 16:11:08 +00:00
|
|
|
const int32_t width, int32_t height)
|
2014-05-07 07:56:16 +00:00
|
|
|
{
|
2015-08-18 16:11:08 +00:00
|
|
|
// Halve for interlaced content
|
|
|
|
if (encoder->in.source_scan_type != 0) height /= 2;
|
|
|
|
|
2014-05-07 07:56:16 +00:00
|
|
|
encoder->in.width = width;
|
|
|
|
encoder->in.height = height;
|
|
|
|
encoder->in.real_width = width;
|
|
|
|
encoder->in.real_height = height;
|
|
|
|
|
|
|
|
// If input dimensions are not divisible by the smallest block size, add
|
|
|
|
// pixels to the dimensions, so that they are. These extra pixels will be
|
|
|
|
// compressed along with the real ones but they will be cropped out before
|
|
|
|
// rendering.
|
|
|
|
if (encoder->in.width % CU_MIN_SIZE_PIXELS) {
|
|
|
|
encoder->in.width += CU_MIN_SIZE_PIXELS - (width % CU_MIN_SIZE_PIXELS);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encoder->in.height % CU_MIN_SIZE_PIXELS) {
|
|
|
|
encoder->in.height += CU_MIN_SIZE_PIXELS - (height % CU_MIN_SIZE_PIXELS);
|
|
|
|
}
|
|
|
|
|
|
|
|
encoder->in.height_in_lcu = encoder->in.height / LCU_WIDTH;
|
|
|
|
encoder->in.width_in_lcu = encoder->in.width / LCU_WIDTH;
|
|
|
|
|
|
|
|
// Add one extra LCU when image not divisible by LCU_WIDTH
|
|
|
|
if (encoder->in.height_in_lcu * LCU_WIDTH < height) {
|
|
|
|
encoder->in.height_in_lcu++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encoder->in.width_in_lcu * LCU_WIDTH < width) {
|
|
|
|
encoder->in.width_in_lcu++;
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:41:45 +00:00
|
|
|
encoder->in.pixels_per_pic = encoder->in.width * encoder->in.height;
|
2014-05-07 07:56:16 +00:00
|
|
|
|
|
|
|
|
2015-09-14 09:43:28 +00:00
|
|
|
#ifdef KVZ_DEBUG
|
2014-05-07 07:56:16 +00:00
|
|
|
if (width != encoder->in.width || height != encoder->in.height) {
|
|
|
|
printf("Picture buffer has been extended to be a multiple of the smallest block size:\r\n");
|
|
|
|
printf(" Width = %d (%d), Height = %d (%d)\r\n", width, encoder->in.width, height,
|
|
|
|
encoder->in.height);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:41:45 +00:00
|
|
|
/**
|
|
|
|
* \brief Initialize GOP layer weights.
|
|
|
|
* \return 1 on success, 0 on failure.
|
|
|
|
*
|
|
|
|
* Selects appropriate weights for layers according to the target bpp.
|
|
|
|
* Only GOP structures with exactly four layers are supported.
|
|
|
|
*/
|
|
|
|
static int encoder_control_init_gop_layer_weights(encoder_control_t * const encoder)
|
|
|
|
{
|
|
|
|
|
2015-06-30 08:03:26 +00:00
|
|
|
kvz_gop_config const * const gop = encoder->cfg->gop;
|
2015-05-27 12:41:45 +00:00
|
|
|
const int8_t gop_len = encoder->cfg->gop_len;
|
|
|
|
|
|
|
|
int num_layers = 0;
|
|
|
|
for (int i = 0; i < gop_len; ++i) {
|
|
|
|
num_layers = MAX(gop[i].layer, num_layers);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (num_layers) {
|
|
|
|
case 0:
|
2016-05-25 11:08:06 +00:00
|
|
|
case 1:
|
2015-05-27 12:41:45 +00:00
|
|
|
break;
|
|
|
|
|
2016-05-25 11:08:06 +00:00
|
|
|
// Use the first layers of the 4-layer weights.
|
|
|
|
case 2:
|
2015-11-02 10:22:25 +00:00
|
|
|
case 3:
|
2016-05-25 11:08:06 +00:00
|
|
|
|
2015-05-27 12:41:45 +00:00
|
|
|
case 4:
|
2016-05-25 11:08:06 +00:00
|
|
|
if (encoder->cfg->gop_lowdelay) {
|
|
|
|
// These weights are based on http://doi.org/10.1109/TIP.2014.2336550
|
|
|
|
// They are meant for lp-g4d3r4t1 gop, but work ok for others.
|
|
|
|
if (encoder->target_avg_bpp <= 0.05) {
|
|
|
|
encoder->gop_layer_weights[0] = 14;
|
|
|
|
encoder->gop_layer_weights[1] = 3;
|
|
|
|
encoder->gop_layer_weights[2] = 2;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else if (encoder->target_avg_bpp <= 0.1) {
|
|
|
|
encoder->gop_layer_weights[0] = 12;
|
|
|
|
encoder->gop_layer_weights[1] = 3;
|
|
|
|
encoder->gop_layer_weights[2] = 2;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else if (encoder->target_avg_bpp <= 0.2) {
|
|
|
|
encoder->gop_layer_weights[0] = 10;
|
|
|
|
encoder->gop_layer_weights[1] = 3;
|
|
|
|
encoder->gop_layer_weights[2] = 2;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else {
|
|
|
|
encoder->gop_layer_weights[0] = 6;
|
|
|
|
encoder->gop_layer_weights[1] = 3;
|
|
|
|
encoder->gop_layer_weights[2] = 2;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
}
|
2015-05-27 12:41:45 +00:00
|
|
|
} else {
|
2016-05-25 11:08:06 +00:00
|
|
|
// These weights are from http://doi.org/10.1109/TIP.2014.2336550
|
|
|
|
if (encoder->target_avg_bpp <= 0.05) {
|
|
|
|
encoder->gop_layer_weights[0] = 30;
|
|
|
|
encoder->gop_layer_weights[1] = 8;
|
|
|
|
encoder->gop_layer_weights[2] = 4;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else if (encoder->target_avg_bpp <= 0.1) {
|
|
|
|
encoder->gop_layer_weights[0] = 25;
|
|
|
|
encoder->gop_layer_weights[1] = 7;
|
|
|
|
encoder->gop_layer_weights[2] = 4;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else if (encoder->target_avg_bpp <= 0.2) {
|
|
|
|
encoder->gop_layer_weights[0] = 20;
|
|
|
|
encoder->gop_layer_weights[1] = 6;
|
|
|
|
encoder->gop_layer_weights[2] = 4;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
} else {
|
|
|
|
encoder->gop_layer_weights[0] = 15;
|
|
|
|
encoder->gop_layer_weights[1] = 5;
|
|
|
|
encoder->gop_layer_weights[2] = 4;
|
|
|
|
encoder->gop_layer_weights[3] = 1;
|
|
|
|
}
|
2015-05-27 12:41:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unsupported number of GOP layers (%d)\n", num_layers);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalize weights so that the sum of weights in a GOP is one.
|
|
|
|
double sum_weights = 0;
|
|
|
|
for (int i = 0; i < gop_len; ++i) {
|
|
|
|
sum_weights += encoder->gop_layer_weights[gop[i].layer - 1];
|
|
|
|
}
|
|
|
|
for (int i = 0; i < num_layers; ++i) {
|
|
|
|
encoder->gop_layer_weights[i] /= sum_weights;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|