| /* | |
| * 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; | |
| } | |
| } | |
| 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; | |
| 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)); | |
| } |