/* * Copyright (c) 2016, 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. */ /*!\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 * p ... * sY ... * sCb ... * sCr ... * cY .... * cCb .... * cCr .... * E ... */ #include #include #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"); } } 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; int16_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)); }