diff --git a/README.md b/README.md index 6e8085a5..82fe4e86 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ meant to be user configurable later. beta and tc range is -6..6 [0:0] --no-sao : Disable sample adaptive offset --aud : Use access unit delimiters + --cqmfile : Custom Quantization Matrices from a file Video Usability Information: --sar : Specify Sample Aspect Ratio diff --git a/src/config.c b/src/config.c index 3f7897ac..841e0e2a 100644 --- a/src/config.c +++ b/src/config.c @@ -74,6 +74,7 @@ int config_init(config *cfg) cfg->vui.colormatrix = 2; /* undef */ cfg->vui.chroma_loc = 0; /* left center */ cfg->aud_enable = 0; + cfg->cqmfile = NULL; return 1; } @@ -87,6 +88,7 @@ int config_destroy(config *cfg) { FREE_POINTER(cfg->input); FREE_POINTER(cfg->output); + FREE_POINTER(cfg->cqmfile); free(cfg); return 1; @@ -234,6 +236,8 @@ static int config_parse(config *cfg, const char *name, const char *value) } OPT("aud") cfg->aud_enable = atobool(value); + OPT("cqmfile") + cfg->cqmfile = copy_string(value); else return 0; #undef OPT @@ -277,6 +281,7 @@ int config_read(config *cfg,int argc, char *argv[]) { "colormatrix", required_argument, NULL, 0 }, { "chromaloc", required_argument, NULL, 0 }, { "aud", no_argument, NULL, 0 }, + { "cqmfile", required_argument, NULL, 0 }, {0, 0, 0, 0} }; diff --git a/src/config.h b/src/config.h index 1175fd90..8159297c 100644 --- a/src/config.h +++ b/src/config.h @@ -58,6 +58,7 @@ typedef struct int8_t chroma_loc; /*!< \brief Chroma sample location */ } vui; int8_t aud_enable; /*!< \brief Flag to use access unit delimiters */ + char * cqmfile; /*!< \brief Pointer to custom quantization matrices filename */ } config; /* Function definitions */ diff --git a/src/encmain.c b/src/encmain.c index 260a74dd..cd3e730a 100644 --- a/src/encmain.c +++ b/src/encmain.c @@ -59,6 +59,7 @@ int main(int argc, char *argv[]) config *cfg = NULL; //!< Global configuration FILE *input = NULL; //!< input file (YUV) FILE *output = NULL; //!< output file (HEVC NAL stream) + FILE *cqmfile = NULL; //!< HM-compatible CQM file encoder_control *encoder = NULL; //!< Encoder control struct double psnr[3] = { 0.0, 0.0, 0.0 }; uint64_t curpos = 0; @@ -102,6 +103,7 @@ int main(int argc, char *argv[]) " beta and tc range is -6..6 [0:0]\n" " --no-sao : Disable sample adaptive offset\n" " --aud : Use access unit delimiters\n" + " --cqmfile : Custom Quantization Matrices from a file\n" "\n" " Video Usability Information:\n" " --sar : Specify Sample Aspect Ratio\n" @@ -202,6 +204,9 @@ int main(int argc, char *argv[]) encoder->vui.chroma_loc = encoder->cfg->vui.chroma_loc; // AUD encoder->aud_enable = encoder->cfg->aud_enable; + // CQM + cqmfile = cfg->cqmfile ? fopen(cfg->cqmfile, "rb") : NULL; + encoder->cqmfile = cqmfile; init_encoder_input(&encoder->in, input, cfg->width, cfg->height); @@ -290,6 +295,7 @@ int main(int argc, char *argv[]) fclose(input); fclose(output); + fclose(cqmfile); #ifdef _DEBUG fclose(recout); #endif diff --git a/src/encoder.c b/src/encoder.c index b6f90cfd..ca9b7fe8 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -398,6 +398,8 @@ void encode_one_frame(encoder_control* encoder) picture_list_rem(encoder->ref, encoder->ref->used_size - 1, 1); } + if (encoder->frame == 0 && encoder->cqmfile) + scalinglist_parse(encoder->cqmfile); encoder->poc = 0; @@ -433,6 +435,7 @@ void encode_one_frame(encoder_control* encoder) bitstream_clear_buffer(encoder->stream); if (encoder->frame == 0) { + // Prefix SEI encode_prefix_sei_version(encoder); bitstream_align(encoder->stream); bitstream_flush(encoder->stream); @@ -749,6 +752,61 @@ void encode_PTL(encoder_control *encoder) // end PTL } +static void encode_scaling_list(encoder_control* encoder) +{ + uint32_t size_id; + for (size_id = 0; size_id < SCALING_LIST_SIZE_NUM; size_id++) { + uint32_t list_id; + for (list_id = 0; list_id < g_scaling_list_num[size_id]; list_id++) { + uint8_t scaling_list_pred_mode_flag = 1; + int32_t pred_list_idx; + int32_t i; + uint32_t ref_matrix_id; + + for (pred_list_idx = list_id; pred_list_idx >= 0; pred_list_idx--) { + int32_t *pred_list = (list_id == pred_list_idx) ? + scalinglist_get_default(size_id, pred_list_idx) : + g_scaling_list_coeff[size_id][pred_list_idx]; + + if (!memcmp(g_scaling_list_coeff[size_id][list_id], pred_list, sizeof(int32_t) * MIN(8, g_scaling_list_size[size_id])) && + ((size_id < SCALING_LIST_16x16) || + (g_scaling_list_dc[size_id][list_id] == g_scaling_list_dc[size_id][pred_list_idx]))) { + ref_matrix_id = pred_list_idx; + scaling_list_pred_mode_flag = 0; + break; + } + } + WRITE_U(encoder->stream, scaling_list_pred_mode_flag, 1, "scaling_list_pred_mode_flag" ); + + if (!scaling_list_pred_mode_flag) { + WRITE_UE(encoder->stream, list_id - ref_matrix_id, "scaling_list_pred_matrix_id_delta"); + } else { + int32_t delta; + int32_t coef_num = MIN(MAX_MATRIX_COEF_NUM, g_scaling_list_size[size_id]); + uint32_t *scan = (size_id == 0) ? g_sig_last_scan[SCAN_DIAG][1] : g_sig_last_scan_32x32; + int32_t next_coef = 8; + int32_t *coef_list = g_scaling_list_coeff[size_id][list_id]; + + if (size_id >= SCALING_LIST_16x16) { + WRITE_SE(encoder->stream, g_scaling_list_dc[size_id][list_id] - 8, "scaling_list_dc_coef_minus8"); + next_coef = g_scaling_list_dc[size_id][list_id]; + } + + for (i = 0; i < coef_num; i++) { + delta = coef_list[scan[i]] - next_coef; + next_coef = coef_list[scan[i]]; + if (delta > 127) + delta -= 256; + if (delta < -128) + delta += 256; + + WRITE_SE(encoder->stream, delta, "scaling_list_delta_coef"); + } + } + } + } +} + void encode_seq_parameter_set(encoder_control* encoder) { encoder_input* const in = &encoder->in; @@ -813,11 +871,12 @@ void encode_seq_parameter_set(encoder_control* encoder) WRITE_UE(encoder->stream, TR_DEPTH_INTER, "max_transform_hierarchy_depth_inter"); WRITE_UE(encoder->stream, TR_DEPTH_INTRA, "max_transform_hierarchy_depth_intra"); - // Use default scaling list - WRITE_U(encoder->stream, ENABLE_SCALING_LIST, 1, "scaling_list_enable_flag"); - #if ENABLE_SCALING_LIST == 1 - WRITE_U(encoder->stream, 0, 1, "sps_scaling_list_data_present_flag"); - #endif + // scaling list + WRITE_U(encoder->stream, g_scaling_list_enable, 1, "scaling_list_enable_flag"); + if (g_scaling_list_enable) { + WRITE_U(encoder->stream, 1, 1, "sps_scaling_list_data_present_flag"); + encode_scaling_list(encoder); + } WRITE_U(encoder->stream, 0, 1, "amp_enabled_flag"); WRITE_U(encoder->stream, encoder->sao_enable ? 1 : 0, 1, diff --git a/src/encoder.h b/src/encoder.h index 403d7e2f..429b05d0 100644 --- a/src/encoder.h +++ b/src/encoder.h @@ -94,6 +94,7 @@ typedef struct } vui; int8_t aud_enable; + FILE *cqmfile; // \brief Costum Quantization Matrices } encoder_control; void init_tables(void); diff --git a/src/global.h b/src/global.h index 0caf7b56..526e19cd 100644 --- a/src/global.h +++ b/src/global.h @@ -73,7 +73,6 @@ typedef int16_t coefficient; #define ENABLE_PCM 0 /*!< Setting to 1 will enable using PCM blocks (current intra-search does not consider PCM) */ #define ENABLE_SIGN_HIDING 1 -#define ENABLE_SCALING_LIST 0 /*!< Enable usage of (default) scaling list */ #define ENABLE_TEMPORAL_MVP 0 /*!< Enable usage of temporal Motion Vector Prediction */ diff --git a/src/transform.c b/src/transform.c index ff5bd000..ea0d2ec7 100644 --- a/src/transform.c +++ b/src/transform.c @@ -33,6 +33,9 @@ ////////////////////////////////////////////////////////////////////////// // INITIALIZATIONS // + +#define SCALING_LIST_REM_NUM 6 + const int16_t g_t4[4][4] = { { 64, 64, 64, 64}, @@ -149,9 +152,12 @@ const uint8_t g_chroma_scale[58]= 45,46,47,48,49,50,51 }; -int32_t *g_quant_coeff[4][6][6]; -int32_t *g_de_quant_coeff[4][6][6]; -double *g_error_scale[4][6][6]; +uint8_t g_scaling_list_enable = 0; +int32_t g_scaling_list_dc [SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM] = { { 0 } }; +int32_t *g_scaling_list_coeff[SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM] = { { NULL } }; +int32_t *g_quant_coeff [SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM][SCALING_LIST_REM_NUM]; +int32_t *g_de_quant_coeff [SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM][SCALING_LIST_REM_NUM]; +double *g_error_scale [SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM][SCALING_LIST_REM_NUM]; const uint8_t g_scaling_list_num[4] = { 6, 6, 6, 2}; const uint16_t g_scaling_list_size[4] = { 16, 64, 256,1024}; @@ -181,6 +187,7 @@ void scalinglist_init() g_error_scale[sizeId][listId][qp] = (double*)calloc(g_scaling_list_size[sizeId], sizeof(double)); } } + g_scaling_list_coeff[sizeId][listId] = (int32_t*)calloc(MIN(MAX_MATRIX_COEF_NUM, g_scaling_list_size[sizeId]), sizeof(int32_t)); } } // alias, assign pointer to an existing array @@ -208,6 +215,7 @@ void scalinglist_destroy() free( g_error_scale[sizeId][listId][qp]); } } + free(g_scaling_list_coeff[sizeId][listId]); } } } @@ -219,26 +227,13 @@ void scalinglist_destroy() */ void scalinglist_process() { - #define SCALING_LIST_SIZE_NUM 4 - #define SCALING_LIST_REM_NUM 6 uint32_t size,list,qp; for (size = 0; size < SCALING_LIST_SIZE_NUM; size++) { - int32_t* list_ptr = (int32_t *)g_quant_intra_default_8x8; // Default to "8x8" intra - for (list = 0; list < g_scaling_list_num[size]; list++) { - switch(size) { - case 0: // 4x4 - list_ptr = (int32_t *)g_quant_default_4x4; - break; - case 1: // 8x8 - case 2: // 16x16 - if (list > 2) list_ptr = (int32_t*)g_quant_inter_default_8x8; - break; - case 3: // 32x32 - if (list > 0) list_ptr = (int32_t*)g_quant_inter_default_8x8; - break; - } + int32_t *list_ptr = g_scaling_list_enable ? + g_scaling_list_coeff[size][list] : + scalinglist_get_default(size, list); for (qp = 0; qp < SCALING_LIST_REM_NUM; qp++) { scalinglist_set(list_ptr, list, size, qp); @@ -246,8 +241,6 @@ void scalinglist_process() } } } - #undef SCALING_LIST_SIZE_NUM - #undef SCALING_LIST_REM_NUM } @@ -334,19 +327,19 @@ void scalinglist_process_dec( int32_t *coeff, int32_t *dequantcoeff, int32_t inv */ void scalinglist_set(int32_t *coeff, uint32_t listId, uint32_t sizeId, uint32_t qp) { - #define SCALING_LIST_DC 16 uint32_t width = g_scaling_list_size_x[sizeId]; uint32_t height = g_scaling_list_size_x[sizeId]; uint32_t ratio = g_scaling_list_size_x[sizeId] / MIN(8, g_scaling_list_size_x[sizeId]); int32_t *quantcoeff = g_quant_coeff[sizeId][listId][qp]; int32_t *dequantcoeff = g_de_quant_coeff[sizeId][listId][qp]; + uint32_t dc = g_scaling_list_dc[sizeId][listId] != 0 ? g_scaling_list_dc[sizeId][listId] : 16; // Encoder list scalinglist_process_enc(coeff, quantcoeff, g_quant_scales[qp]<<4, height, width, ratio, - MIN(8, g_scaling_list_size_x[sizeId]), SCALING_LIST_DC, ENABLE_SCALING_LIST ? 0 : 1); + MIN(8, g_scaling_list_size_x[sizeId]), dc, !g_scaling_list_enable); // Decoder list scalinglist_process_dec(coeff, dequantcoeff, g_inv_quant_scales[qp], height, width, ratio, - MIN(8, g_scaling_list_size_x[sizeId]), SCALING_LIST_DC, ENABLE_SCALING_LIST ? 0 : 1); + MIN(8, g_scaling_list_size_x[sizeId]), dc, !g_scaling_list_enable); // TODO: support NSQT @@ -357,7 +350,6 @@ void scalinglist_set(int32_t *coeff, uint32_t listId, uint32_t sizeId, uint32_t // quantcoeff = g_quant_coeff[listId][qp][sizeId-1][/*SCALING_LIST_HOR*/2]; // scalinglist_process_enc(coeff,quantcoeff,g_quantScales[qp]<<4,height>>2,width,ratio,MIN(8,g_scalingListSizeX[sizeId]),/*scalingList->getScalingListDC(sizeId,listId)*/0); // } - #undef SCALING_LIST_DC } @@ -962,12 +954,12 @@ void dequant(encoder_control *encoder, int16_t *q_coef, int16_t *coef, int32_t w shift = 20 - QUANT_SHIFT - transform_shift; UNREFERENCED_PARAMETER(block_type); - #if ENABLE_SCALING_LIST == 1 + if (g_scaling_list_enable) { uint32_t log2_tr_size = g_convert_to_bit[ width ] + 2; int32_t scalinglist_type = (block_type == CU_INTRA ? 0 : 3) + (int8_t)("\0\3\1\2"[type]); - dequant_coef = g_de_quant_coeff[log2_tr_size-2][scalinglist_type][qp_scaled%6]; + int32_t *dequant_coef = g_de_quant_coeff[log2_tr_size-2][scalinglist_type][qp_scaled%6]; shift += 4; if (shift >qp_scaled / 6) { @@ -986,9 +978,7 @@ void dequant(encoder_control *encoder, int16_t *q_coef, int16_t *coef, int32_t w coef[n] = (int16_t)CLIP(-32768, 32767, coeff_q << (qp_scaled/6 - shift)); } } - } - #else - { + } else { int32_t scale = g_inv_quant_scales[qp_scaled%6] << (qp_scaled/6); add = 1 << (shift-1); @@ -998,5 +988,145 @@ void dequant(encoder_control *encoder, int16_t *q_coef, int16_t *coef, int32_t w coef[n] = (int16_t)CLIP(-32768, 32767, coeff_q); } } - #endif +} + +int32_t *scalinglist_get_default(uint32_t size_id, uint32_t list_id) +{ + int32_t *list_ptr = (int32_t *)g_quant_intra_default_8x8; // Default to "8x8" intra + switch(size_id) { + case SCALING_LIST_4x4: + list_ptr = (int32_t *)g_quant_default_4x4; + break; + case SCALING_LIST_8x8: + case SCALING_LIST_16x16: + if (list_id > 2) list_ptr = (int32_t *)g_quant_inter_default_8x8; + break; + case SCALING_LIST_32x32: + if (list_id > 0) list_ptr = (int32_t *)g_quant_inter_default_8x8; + break; + } + return list_ptr; +} + +int scalinglist_parse(FILE *fp) +{ + #define LINE_BUFSIZE 1024 + static const char matrix_type[4][6][20] = + { + { + "INTRA4X4_LUMA", + "INTRA4X4_CHROMAU", + "INTRA4X4_CHROMAV", + "INTER4X4_LUMA", + "INTER4X4_CHROMAU", + "INTER4X4_CHROMAV" + }, + { + "INTRA8X8_LUMA", + "INTRA8X8_CHROMAU", + "INTRA8X8_CHROMAV", + "INTER8X8_LUMA", + "INTER8X8_CHROMAU", + "INTER8X8_CHROMAV" + }, + { + "INTRA16X16_LUMA", + "INTRA16X16_CHROMAU", + "INTRA16X16_CHROMAV", + "INTER16X16_LUMA", + "INTER16X16_CHROMAU", + "INTER16X16_CHROMAV" + }, + { + "INTRA32X32_LUMA", + "INTER32X32_LUMA", + }, + }; + static const char matrix_type_dc[2][6][22] = + { + { + "INTRA16X16_LUMA_DC", + "INTRA16X16_CHROMAU_DC", + "INTRA16X16_CHROMAV_DC", + "INTER16X16_LUMA_DC", + "INTER16X16_CHROMAU_DC", + "INTER16X16_CHROMAV_DC" + }, + { + "INTRA32X32_LUMA_DC", + "INTER32X32_LUMA_DC", + }, + }; + + uint32_t size_id; + for (size_id = 0; size_id < SCALING_LIST_SIZE_NUM; size_id++) { + uint32_t list_id; + uint32_t size = MIN(MAX_MATRIX_COEF_NUM, (int32_t)g_scaling_list_size[size_id]); + uint32_t *scan = (size_id == 0) ? g_sig_last_scan[SCAN_DIAG][1] : g_sig_last_scan_32x32; + + for (list_id = 0; list_id < g_scaling_list_num[size_id]; list_id++) { + int found; + uint32_t i; + int32_t data; + int32_t *coeff = g_scaling_list_coeff[size_id][list_id]; + char line[LINE_BUFSIZE + 1] = { 0 }; // +1 for null-terminator + + // Go back for each matrix. + fseek(fp, 0, SEEK_SET); + + do { + if (!fgets(line, LINE_BUFSIZE, fp) || + ((found = !!strstr(line, matrix_type[size_id][list_id])) == 0 && feof(fp))) + return 0; + } while (!found); + + for (i = 0; i < size;) { + char *p; + if (!fgets(line, LINE_BUFSIZE, fp)) + return 0; + p = line; + + // Read coefficients per line. + // The comma (,) character is used as a separator. + // The coefficients are stored in up-right diagonal order. + do { + int ret = sscanf(p, "%d", &data); + if (ret != 1) + break; + else if (data < 1 || data > 255) + return 0; + + coeff[i++] = data; + if (i == size) + break; + + // Seek to the next newline, null-terminator or comma. + while (*p != '\n' && *p != '\0' && *p != ',') + ++p; + if (*p == ',') + ++p; + } while (*p != '\n' && *p != '\0'); + } + + // Set DC value. + if (size_id >= SCALING_LIST_16x16) { + fseek(fp, 0, SEEK_SET); + + do { + if (!fgets(line, LINE_BUFSIZE, fp) || + ((found = !!strstr(line, matrix_type_dc[size_id - SCALING_LIST_16x16][list_id])) == 0 && feof(fp))) + return 0; + } while (!found); + if (1 != fscanf(fp, "%d", &data) || data < 1 || data > 255) + return 0; + + g_scaling_list_dc[size_id][list_id] = data; + } else + g_scaling_list_dc[size_id][list_id] = coeff[0]; + } + } + + g_scaling_list_enable = 1; + return 1; + #undef LINE_BUFSIZE } diff --git a/src/transform.h b/src/transform.h index 6bbf00a3..c4035e2e 100644 --- a/src/transform.h +++ b/src/transform.h @@ -30,9 +30,21 @@ #include +#define SCALING_LIST_4x4 0 +#define SCALING_LIST_8x8 1 +#define SCALING_LIST_16x16 2 +#define SCALING_LIST_32x32 3 +#define SCALING_LIST_SIZE_NUM 4 +#define SCALING_LIST_NUM 6 +#define MAX_MATRIX_COEF_NUM 64 +extern uint8_t g_scaling_list_enable; +extern int32_t g_scaling_list_dc [SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM]; +extern int32_t* g_scaling_list_coeff[SCALING_LIST_SIZE_NUM][SCALING_LIST_NUM]; extern int32_t* g_quant_coeff[4][6][6]; extern double* g_error_scale[4][6][6]; +extern const uint8_t g_scaling_list_num[4]; +extern const uint16_t g_scaling_list_size[4]; extern const int32_t g_quant_intra_default_8x8[64]; extern const uint8_t g_chroma_scale[58]; extern const int16_t g_inv_quant_scales[6]; @@ -53,4 +65,7 @@ void scalinglist_set(int32_t *coeff, uint32_t list_id, uint32_t size_id, uint32_ void scalinglist_set_err_scale(uint32_t list,uint32_t size, uint32_t qp); void scalinglist_destroy(); +int32_t *scalinglist_get_default(uint32_t size_id, uint32_t list_id); +int scalinglist_parse(FILE *fp); + #endif