/*****************************************************************************
* This file is part of Kvazaar HEVC encoder.
*
* Copyright (C) 2013-2015 Tampere University of Technology and others (see
* COPYING file).
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with Kvazaar. If not, see .
****************************************************************************/
/*
* \file
*/
#include "encoder.h"
#include
#include
#include
#include
#include "tables.h"
#include "config.h"
#include "cabac.h"
#include "image.h"
#include "nal.h"
#include "context.h"
#include "transform.h"
#include "intra.h"
#include "inter.h"
#include "filter.h"
#include "search.h"
#include "sao.h"
#include "rdo.h"
int encoder_control_init(encoder_control_t * const encoder, const config_t * const cfg) {
if (!cfg) {
fprintf(stderr, "Config object must not be null!\n");
return 0;
}
encoder->threadqueue = MALLOC(threadqueue_queue, 1);
encoder->owf = cfg->owf;
//Init threadqueue
if (!encoder->threadqueue || !threadqueue_init(encoder->threadqueue, cfg->threads, encoder->owf > 0)) {
fprintf(stderr, "Could not initialize threadqueue");
return 0;
}
// Config pointer to config struct
encoder->cfg = cfg;
encoder->bitdepth = 8;
// deblocking filter
encoder->deblock_enable = 1;
encoder->beta_offset_div2 = 0;
encoder->tc_offset_div2 = 0;
// SAO
encoder->sao_enable = 1;
// Rate-distortion optimization level
encoder->rdo = 1;
encoder->full_intra_search = 0;
// Initialize the scaling list
scalinglist_init(&encoder->scaling_list);
// CQM
{
FILE* cqmfile;
cqmfile = cfg->cqmfile ? fopen(cfg->cqmfile, "rb") : NULL;
if (cqmfile) {
scalinglist_parse(&encoder->scaling_list, cqmfile);
fclose(cqmfile);
}
}
scalinglist_process(&encoder->scaling_list, encoder->bitdepth);
encoder_control_input_init(encoder, cfg->width, cfg->height);
//Tiles
encoder->tiles_enable = (encoder->cfg->tiles_width_count > 0 || encoder->cfg->tiles_height_count > 0);
{
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;
if (encoder->cfg->tiles_width_count >= encoder->in.width_in_lcu) {
fprintf(stderr, "Too many tiles (width)!\n");
return 0;
} else if (encoder->cfg->tiles_height_count >= encoder->in.height_in_lcu) {
fprintf(stderr, "Too many tiles (height)!\n");
return 0;
}
//Will be (perhaps) changed later
encoder->tiles_uniform_spacing_flag = 1;
//tilesn[x,y] contains the number of _separation_ between tiles, whereas the encoder needs the number of tiles.
encoder->tiles_num_tile_columns = encoder->cfg->tiles_width_count + 1;
encoder->tiles_num_tile_rows = encoder->cfg->tiles_height_count + 1;
tiles_col_width = MALLOC(int32_t, encoder->tiles_num_tile_columns);
tiles_row_height = MALLOC(int32_t, encoder->tiles_num_tile_rows);
tiles_col_bd = MALLOC(int32_t, encoder->tiles_num_tile_columns + 1);
tiles_row_bd = MALLOC(int32_t, encoder->tiles_num_tile_rows + 1);
tiles_ctb_addr_rs_to_ts = MALLOC(int32_t, num_ctbs);
tiles_ctb_addr_ts_to_rs = MALLOC(int32_t, num_ctbs);
tiles_tile_id = MALLOC(int32_t, num_ctbs);
//(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;
}
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;
}
//(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];
}
//(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) {
int tileX = 0, tileY = 0;
int tbX = j % encoder->in.width_in_lcu;
int tbY = j / encoder->in.width_in_lcu;
for (i = 0; i < encoder->tiles_num_tile_columns; ++i) {
if (tbX >= tiles_col_bd[i]) tileX = i;
}
for (i = 0; i < encoder->tiles_num_tile_rows; ++i) {
if (tbY >= tiles_row_bd[i]) tileY = i;
}
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];
}
tiles_ctb_addr_rs_to_ts[j] += (tbY - tiles_row_bd[tileY]) * tiles_col_width[tileX] +
tbX - tiles_col_bd[tileX];
}
//(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;
}
//(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;
}
}
encoder->tiles_col_width = tiles_col_width;
encoder->tiles_row_height = tiles_row_height;
encoder->tiles_row_bd = tiles_row_bd;
encoder->tiles_col_bd = tiles_col_bd;
encoder->tiles_ctb_addr_rs_to_ts = tiles_ctb_addr_rs_to_ts;
encoder->tiles_ctb_addr_ts_to_rs = tiles_ctb_addr_ts_to_rs;
encoder->tiles_tile_id = tiles_tile_id;
//Slices
{
int *slice_addresses_in_ts;
encoder->slice_count = encoder->cfg->slice_count;
if (encoder->slice_count == 0) {
encoder->slice_count = 1;
slice_addresses_in_ts = MALLOC(int, encoder->slice_count);
slice_addresses_in_ts[0] = 0;
} else {
int i;
slice_addresses_in_ts = MALLOC(int, encoder->slice_count);
if (!encoder->cfg->slice_addresses_in_ts) {
slice_addresses_in_ts[0] = 0;
for (i=1; i < encoder->slice_count; ++i) {
slice_addresses_in_ts[i] = encoder->in.width_in_lcu * encoder->in.height_in_lcu * i / encoder->slice_count;
}
} else {
for (i=0; i < encoder->slice_count; ++i) {
slice_addresses_in_ts[i] = encoder->cfg->slice_addresses_in_ts[i];
}
}
}
encoder->slice_addresses_in_ts = slice_addresses_in_ts;
}
encoder->wpp = encoder->cfg->wpp;
#ifdef _DEBUG
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) {
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];
const char slice_start = lcu_at_slice_start(encoder, lcu_id_ts) ? '|' : ' ';
const char slice_end = lcu_at_slice_end(encoder, lcu_id_ts) ? '|' : ' ';
printf("%c%03d%c", slice_start, encoder->tiles_tile_id[lcu_id_ts], slice_end);
}
printf("\n");
}
printf("\n");
if (encoder->wpp) {
printf("Wavefront Parallel Processing: enabled\n");
} else {
printf("Wavefront Parallel Processing: disabled\n");
}
printf("\n");
#endif //_DEBUG
}
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));
assert(WITHIN(cfg->pu_depth_inter.min, PU_DEPTH_INTRA_MIN, PU_DEPTH_INTRA_MAX));
assert(WITHIN(cfg->pu_depth_inter.max, PU_DEPTH_INTRA_MIN, PU_DEPTH_INTRA_MAX));
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;
return 1;
}
int encoder_control_finalize(encoder_control_t * const encoder) {
//Slices
FREE_POINTER(encoder->slice_addresses_in_ts);
//Tiles
FREE_POINTER(encoder->tiles_col_width);
FREE_POINTER(encoder->tiles_row_height);
FREE_POINTER(encoder->tiles_col_bd);
FREE_POINTER(encoder->tiles_row_bd);
FREE_POINTER(encoder->tiles_ctb_addr_rs_to_ts);
FREE_POINTER(encoder->tiles_ctb_addr_ts_to_rs);
FREE_POINTER(encoder->tiles_tile_id);
scalinglist_destroy(&encoder->scaling_list);
if (!threadqueue_finalize(encoder->threadqueue)) {
fprintf(stderr, "Could not initialize threadqueue");
return 0;
}
FREE_POINTER(encoder->threadqueue);
return 1;
}
void encoder_control_input_init(encoder_control_t * const encoder,
const int32_t width, const int32_t height)
{
encoder->in.width = width;
encoder->in.height = height;
encoder->in.real_width = width;
encoder->in.real_height = height;
encoder->in.bitdepth = encoder->bitdepth;
// 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++;
}
#ifdef _DEBUG
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
}