| /* |
| * Copyright (c) 2021, Alliance for Open Media. All rights reserved |
| * |
| * This source code is subject to the terms of the BSD 3-Clause Clear License |
| * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear |
| * License was not distributed with this source code in the LICENSE file, you |
| * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. 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 |
| * aomedia.org/license/patent-license/. |
| */ |
| |
| /*!\file |
| * \brief This file has the implementation details of the grain table. |
| * |
| * The file format is an ascii representation for readability and |
| * editability. Array parameters are separated from the non-array |
| * parameters and prefixed with a few characters to make for easy |
| * localization with a parameter set. Each entry is prefixed with "E" |
| * and the other parameters are only specified if "update-parms" is |
| * non-zero. |
| * |
| * filmgrn1 |
| * E <start-time> <end-time> <apply-grain> <random-seed> <update-parms> |
| * p <ar_coeff_lag> <ar_coeff_shift> <grain_scale_shift> ... |
| * sY <num_y_points> <point_0_x> <point_0_y> ... |
| * sCb <num_cb_points> <point_0_x> <point_0_y> ... |
| * sCr <num_cr_points> <point_0_x> <point_0_y> ... |
| * cY <ar_coeff_y_0> .... |
| * cCb <ar_coeff_cb_0> .... |
| * cCr <ar_coeff_cr_0> .... |
| * E <start-time> ... |
| */ |
| #include <string.h> |
| #include <stdio.h> |
| #include "aom_dsp/aom_dsp_common.h" |
| #include "aom_dsp/grain_table.h" |
| #include "aom_mem/aom_mem.h" |
| |
| static const char kFileMagic[8] = "filmgrn1"; |
| |
| static void grain_table_entry_read(FILE *file, |
| struct aom_internal_error_info *error_info, |
| aom_film_grain_table_entry_t *entry) { |
| aom_film_grain_t *pars = &entry->params; |
| int num_read = |
| fscanf(file, "E %" PRId64 " %" PRId64 " %d %hd %d\n", &entry->start_time, |
| &entry->end_time, &pars->apply_grain, &pars->random_seed, |
| &pars->update_parameters); |
| if (num_read == 0 && feof(file)) return; |
| if (num_read != 5) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read entry header. Read %d != 5", num_read); |
| return; |
| } |
| if (pars->update_parameters) { |
| num_read = fscanf(file, "p %d %d %d %d %d %d %d %d %d %d %d %d\n", |
| &pars->ar_coeff_lag, &pars->ar_coeff_shift, |
| &pars->grain_scale_shift, &pars->scaling_shift, |
| &pars->chroma_scaling_from_luma, &pars->overlap_flag, |
| &pars->cb_mult, &pars->cb_luma_mult, &pars->cb_offset, |
| &pars->cr_mult, &pars->cr_luma_mult, &pars->cr_offset); |
| if (num_read != 12) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read entry params. Read %d != 12", |
| num_read); |
| return; |
| } |
| if (!fscanf(file, "\tsY %d ", &pars->num_y_points)) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read num y points"); |
| return; |
| } |
| for (int i = 0; i < pars->num_y_points; ++i) { |
| if (2 != fscanf(file, "%d %d", &pars->scaling_points_y[i][0], |
| &pars->scaling_points_y[i][1])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read y scaling points"); |
| return; |
| } |
| } |
| if (!fscanf(file, "\n\tsCb %d", &pars->num_cb_points)) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read num cb points"); |
| return; |
| } |
| for (int i = 0; i < pars->num_cb_points; ++i) { |
| if (2 != fscanf(file, "%d %d", &pars->scaling_points_cb[i][0], |
| &pars->scaling_points_cb[i][1])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read cb scaling points"); |
| return; |
| } |
| } |
| if (!fscanf(file, "\n\tsCr %d", &pars->num_cr_points)) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read num cr points"); |
| return; |
| } |
| for (int i = 0; i < pars->num_cr_points; ++i) { |
| if (2 != fscanf(file, "%d %d", &pars->scaling_points_cr[i][0], |
| &pars->scaling_points_cr[i][1])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read cr scaling points"); |
| return; |
| } |
| } |
| |
| fscanf(file, "\n\tcY"); |
| const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1); |
| for (int i = 0; i < n; ++i) { |
| if (1 != fscanf(file, "%d", &pars->ar_coeffs_y[i])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read Y coeffs"); |
| return; |
| } |
| } |
| fscanf(file, "\n\tcCb"); |
| for (int i = 0; i <= n; ++i) { |
| if (1 != fscanf(file, "%d", &pars->ar_coeffs_cb[i])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read Cb coeffs"); |
| return; |
| } |
| } |
| fscanf(file, "\n\tcCr"); |
| for (int i = 0; i <= n; ++i) { |
| if (1 != fscanf(file, "%d", &pars->ar_coeffs_cr[i])) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read Cr coeffs"); |
| return; |
| } |
| } |
| fscanf(file, "\n"); |
| } |
| } |
| |
| static void grain_table_entry_write(FILE *file, |
| aom_film_grain_table_entry_t *entry) { |
| const aom_film_grain_t *pars = &entry->params; |
| fprintf(file, "E %" PRId64 " %" PRId64 " %d %d %d\n", entry->start_time, |
| entry->end_time, pars->apply_grain, pars->random_seed, |
| pars->update_parameters); |
| if (pars->update_parameters) { |
| fprintf(file, "\tp %d %d %d %d %d %d %d %d %d %d %d %d\n", |
| pars->ar_coeff_lag, pars->ar_coeff_shift, pars->grain_scale_shift, |
| pars->scaling_shift, pars->chroma_scaling_from_luma, |
| pars->overlap_flag, pars->cb_mult, pars->cb_luma_mult, |
| pars->cb_offset, pars->cr_mult, pars->cr_luma_mult, |
| pars->cr_offset); |
| fprintf(file, "\tsY %d ", pars->num_y_points); |
| for (int i = 0; i < pars->num_y_points; ++i) { |
| fprintf(file, " %d %d", pars->scaling_points_y[i][0], |
| pars->scaling_points_y[i][1]); |
| } |
| fprintf(file, "\n\tsCb %d", pars->num_cb_points); |
| for (int i = 0; i < pars->num_cb_points; ++i) { |
| fprintf(file, " %d %d", pars->scaling_points_cb[i][0], |
| pars->scaling_points_cb[i][1]); |
| } |
| fprintf(file, "\n\tsCr %d", pars->num_cr_points); |
| for (int i = 0; i < pars->num_cr_points; ++i) { |
| fprintf(file, " %d %d", pars->scaling_points_cr[i][0], |
| pars->scaling_points_cr[i][1]); |
| } |
| fprintf(file, "\n\tcY"); |
| const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1); |
| for (int i = 0; i < n; ++i) { |
| fprintf(file, " %d", pars->ar_coeffs_y[i]); |
| } |
| fprintf(file, "\n\tcCb"); |
| for (int i = 0; i <= n; ++i) { |
| fprintf(file, " %d", pars->ar_coeffs_cb[i]); |
| } |
| fprintf(file, "\n\tcCr"); |
| for (int i = 0; i <= n; ++i) { |
| fprintf(file, " %d", pars->ar_coeffs_cr[i]); |
| } |
| fprintf(file, "\n"); |
| } |
| } |
| |
| void aom_film_grain_table_append(aom_film_grain_table_t *t, int64_t time_stamp, |
| int64_t end_time, |
| const aom_film_grain_t *grain) { |
| if (!t->tail || memcmp(grain, &t->tail->params, sizeof(*grain))) { |
| aom_film_grain_table_entry_t *new_tail = aom_malloc(sizeof(*new_tail)); |
| memset(new_tail, 0, sizeof(*new_tail)); |
| if (t->tail) t->tail->next = new_tail; |
| if (!t->head) t->head = new_tail; |
| t->tail = new_tail; |
| |
| new_tail->start_time = time_stamp; |
| new_tail->end_time = end_time; |
| new_tail->params = *grain; |
| } else { |
| t->tail->end_time = AOMMAX(t->tail->end_time, end_time); |
| t->tail->start_time = AOMMIN(t->tail->start_time, time_stamp); |
| } |
| } |
| |
| int aom_film_grain_table_lookup(aom_film_grain_table_t *t, int64_t time_stamp, |
| int64_t end_time, int erase, |
| aom_film_grain_t *grain) { |
| aom_film_grain_table_entry_t *entry = t->head; |
| aom_film_grain_table_entry_t *prev_entry = 0; |
| uint16_t random_seed = grain ? grain->random_seed : 0; |
| if (grain) memset(grain, 0, sizeof(*grain)); |
| |
| while (entry) { |
| aom_film_grain_table_entry_t *next = entry->next; |
| if (time_stamp >= entry->start_time && time_stamp < entry->end_time) { |
| if (grain) { |
| *grain = entry->params; |
| if (time_stamp != 0) grain->random_seed = random_seed; |
| } |
| if (!erase) return 1; |
| |
| const int64_t entry_end_time = entry->end_time; |
| if (time_stamp <= entry->start_time && end_time >= entry->end_time) { |
| if (t->tail == entry) t->tail = prev_entry; |
| if (prev_entry) { |
| prev_entry->next = entry->next; |
| } else { |
| t->head = entry->next; |
| } |
| aom_free(entry); |
| } else if (time_stamp <= entry->start_time && |
| end_time < entry->end_time) { |
| entry->start_time = end_time; |
| } else if (time_stamp > entry->start_time && |
| end_time >= entry->end_time) { |
| entry->end_time = time_stamp; |
| } else { |
| aom_film_grain_table_entry_t *new_entry = |
| aom_malloc(sizeof(*new_entry)); |
| new_entry->next = entry->next; |
| new_entry->start_time = end_time; |
| new_entry->end_time = entry->end_time; |
| new_entry->params = entry->params; |
| entry->next = new_entry; |
| entry->end_time = time_stamp; |
| if (t->tail == entry) t->tail = new_entry; |
| } |
| // If segments aren't aligned, delete from the beggining of subsequent |
| // segments |
| if (end_time > entry_end_time) { |
| aom_film_grain_table_lookup(t, entry->end_time, end_time, 1, 0); |
| } |
| return 1; |
| } |
| prev_entry = entry; |
| entry = next; |
| } |
| return 0; |
| } |
| |
| aom_codec_err_t aom_film_grain_table_read( |
| aom_film_grain_table_t *t, const char *filename, |
| struct aom_internal_error_info *error_info) { |
| FILE *file = fopen(filename, "rb"); |
| if (!file) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open %s", |
| filename); |
| return error_info->error_code; |
| } |
| error_info->error_code = AOM_CODEC_OK; |
| |
| // Read in one extra character as there should be white space after |
| // the header. |
| char magic[9]; |
| if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to read (or invalid) file magic"); |
| fclose(file); |
| return error_info->error_code; |
| } |
| |
| aom_film_grain_table_entry_t *prev_entry = 0; |
| while (!feof(file)) { |
| aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry)); |
| memset(entry, 0, sizeof(*entry)); |
| grain_table_entry_read(file, error_info, entry); |
| entry->next = 0; |
| |
| if (prev_entry) prev_entry->next = entry; |
| if (!t->head) t->head = entry; |
| t->tail = entry; |
| prev_entry = entry; |
| |
| if (error_info->error_code != AOM_CODEC_OK) break; |
| } |
| |
| fclose(file); |
| return error_info->error_code; |
| } |
| |
| aom_codec_err_t aom_film_grain_table_write( |
| const aom_film_grain_table_t *t, const char *filename, |
| struct aom_internal_error_info *error_info) { |
| error_info->error_code = AOM_CODEC_OK; |
| |
| FILE *file = fopen(filename, "wb"); |
| if (!file) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open file %s", |
| filename); |
| return error_info->error_code; |
| } |
| |
| if (!fwrite(kFileMagic, 8, 1, file)) { |
| aom_internal_error(error_info, AOM_CODEC_ERROR, |
| "Unable to write file magic"); |
| fclose(file); |
| return error_info->error_code; |
| } |
| |
| fprintf(file, "\n"); |
| aom_film_grain_table_entry_t *entry = t->head; |
| while (entry) { |
| grain_table_entry_write(file, entry); |
| entry = entry->next; |
| } |
| fclose(file); |
| return error_info->error_code; |
| } |
| |
| void aom_film_grain_table_free(aom_film_grain_table_t *t) { |
| aom_film_grain_table_entry_t *entry = t->head; |
| while (entry) { |
| aom_film_grain_table_entry_t *next = entry->next; |
| aom_free(entry); |
| entry = next; |
| } |
| memset(t, 0, sizeof(*t)); |
| } |