|  | /* | 
|  | * 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 <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; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (fscanf(file, "\n\tcY")) { | 
|  | aom_internal_error(error_info, AOM_CODEC_ERROR, | 
|  | "Unable to read Y coeffs header (cY)"); | 
|  | return; | 
|  | } | 
|  | 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; | 
|  | } | 
|  | } | 
|  | if (fscanf(file, "\n\tcCb")) { | 
|  | aom_internal_error(error_info, AOM_CODEC_ERROR, | 
|  | "Unable to read Cb coeffs header (cCb)"); | 
|  | return; | 
|  | } | 
|  | 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; | 
|  | } | 
|  | } | 
|  | if (fscanf(file, "\n\tcCr")) { | 
|  | aom_internal_error(error_info, AOM_CODEC_ERROR, | 
|  | "Unable read to Cr coeffs header (cCr)"); | 
|  | return; | 
|  | } | 
|  | 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; | 
|  | } | 
|  | } | 
|  | (void)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 = NULL; | 
|  | 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 beginning 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 = NULL; | 
|  | 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 = NULL; | 
|  |  | 
|  | 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)); | 
|  | } |