1 /*
2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 /*!\file
13 * \brief This file has the implementation details of the grain table.
14 *
15 * The file format is an ascii representation for readability and
16 * editability. Array parameters are separated from the non-array
17 * parameters and prefixed with a few characters to make for easy
18 * localization with a parameter set. Each entry is prefixed with "E"
19 * and the other parameters are only specified if "update-parms" is
20 * non-zero.
21 *
22 * filmgrn1
23 * E <start-time> <end-time> <apply-grain> <random-seed> <update-parms>
24 * p <ar_coeff_lag> <ar_coeff_shift> <grain_scale_shift> ...
25 * sY <num_y_points> <point_0_x> <point_0_y> ...
26 * sCb <num_cb_points> <point_0_x> <point_0_y> ...
27 * sCr <num_cr_points> <point_0_x> <point_0_y> ...
28 * cY <ar_coeff_y_0> ....
29 * cCb <ar_coeff_cb_0> ....
30 * cCr <ar_coeff_cr_0> ....
31 * E <start-time> ...
32 */
33 #include <string.h>
34 #include <stdio.h>
35 #include "aom_dsp/aom_dsp_common.h"
36 #include "aom_dsp/grain_table.h"
37 #include "aom_mem/aom_mem.h"
38
39 static const char kFileMagic[8] = "filmgrn1";
40
grain_table_entry_read(FILE * file,struct aom_internal_error_info * error_info,aom_film_grain_table_entry_t * entry)41 static void grain_table_entry_read(FILE *file,
42 struct aom_internal_error_info *error_info,
43 aom_film_grain_table_entry_t *entry) {
44 aom_film_grain_t *pars = &entry->params;
45 int num_read =
46 fscanf(file, "E %" PRId64 " %" PRId64 " %d %hd %d\n", &entry->start_time,
47 &entry->end_time, &pars->apply_grain, &pars->random_seed,
48 &pars->update_parameters);
49 if (num_read == 0 && feof(file)) return;
50 if (num_read != 5) {
51 aom_internal_error(error_info, AOM_CODEC_ERROR,
52 "Unable to read entry header. Read %d != 5", num_read);
53 return;
54 }
55 if (pars->update_parameters) {
56 num_read = fscanf(file, "p %d %d %d %d %d %d %d %d %d %d %d %d\n",
57 &pars->ar_coeff_lag, &pars->ar_coeff_shift,
58 &pars->grain_scale_shift, &pars->scaling_shift,
59 &pars->chroma_scaling_from_luma, &pars->overlap_flag,
60 &pars->cb_mult, &pars->cb_luma_mult, &pars->cb_offset,
61 &pars->cr_mult, &pars->cr_luma_mult, &pars->cr_offset);
62 if (num_read != 12) {
63 aom_internal_error(error_info, AOM_CODEC_ERROR,
64 "Unable to read entry params. Read %d != 12",
65 num_read);
66 return;
67 }
68 if (!fscanf(file, "\tsY %d ", &pars->num_y_points)) {
69 aom_internal_error(error_info, AOM_CODEC_ERROR,
70 "Unable to read num y points");
71 return;
72 }
73 for (int i = 0; i < pars->num_y_points; ++i) {
74 if (2 != fscanf(file, "%d %d", &pars->scaling_points_y[i][0],
75 &pars->scaling_points_y[i][1])) {
76 aom_internal_error(error_info, AOM_CODEC_ERROR,
77 "Unable to read y scaling points");
78 return;
79 }
80 }
81 if (!fscanf(file, "\n\tsCb %d", &pars->num_cb_points)) {
82 aom_internal_error(error_info, AOM_CODEC_ERROR,
83 "Unable to read num cb points");
84 return;
85 }
86 for (int i = 0; i < pars->num_cb_points; ++i) {
87 if (2 != fscanf(file, "%d %d", &pars->scaling_points_cb[i][0],
88 &pars->scaling_points_cb[i][1])) {
89 aom_internal_error(error_info, AOM_CODEC_ERROR,
90 "Unable to read cb scaling points");
91 return;
92 }
93 }
94 if (!fscanf(file, "\n\tsCr %d", &pars->num_cr_points)) {
95 aom_internal_error(error_info, AOM_CODEC_ERROR,
96 "Unable to read num cr points");
97 return;
98 }
99 for (int i = 0; i < pars->num_cr_points; ++i) {
100 if (2 != fscanf(file, "%d %d", &pars->scaling_points_cr[i][0],
101 &pars->scaling_points_cr[i][1])) {
102 aom_internal_error(error_info, AOM_CODEC_ERROR,
103 "Unable to read cr scaling points");
104 return;
105 }
106 }
107
108 fscanf(file, "\n\tcY");
109 const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
110 for (int i = 0; i < n; ++i) {
111 if (1 != fscanf(file, "%d", &pars->ar_coeffs_y[i])) {
112 aom_internal_error(error_info, AOM_CODEC_ERROR,
113 "Unable to read Y coeffs");
114 return;
115 }
116 }
117 fscanf(file, "\n\tcCb");
118 for (int i = 0; i <= n; ++i) {
119 if (1 != fscanf(file, "%d", &pars->ar_coeffs_cb[i])) {
120 aom_internal_error(error_info, AOM_CODEC_ERROR,
121 "Unable to read Cb coeffs");
122 return;
123 }
124 }
125 fscanf(file, "\n\tcCr");
126 for (int i = 0; i <= n; ++i) {
127 if (1 != fscanf(file, "%d", &pars->ar_coeffs_cr[i])) {
128 aom_internal_error(error_info, AOM_CODEC_ERROR,
129 "Unable to read Cr coeffs");
130 return;
131 }
132 }
133 fscanf(file, "\n");
134 }
135 }
136
grain_table_entry_write(FILE * file,aom_film_grain_table_entry_t * entry)137 static void grain_table_entry_write(FILE *file,
138 aom_film_grain_table_entry_t *entry) {
139 const aom_film_grain_t *pars = &entry->params;
140 fprintf(file, "E %" PRId64 " %" PRId64 " %d %d %d\n", entry->start_time,
141 entry->end_time, pars->apply_grain, pars->random_seed,
142 pars->update_parameters);
143 if (pars->update_parameters) {
144 fprintf(file, "\tp %d %d %d %d %d %d %d %d %d %d %d %d\n",
145 pars->ar_coeff_lag, pars->ar_coeff_shift, pars->grain_scale_shift,
146 pars->scaling_shift, pars->chroma_scaling_from_luma,
147 pars->overlap_flag, pars->cb_mult, pars->cb_luma_mult,
148 pars->cb_offset, pars->cr_mult, pars->cr_luma_mult,
149 pars->cr_offset);
150 fprintf(file, "\tsY %d ", pars->num_y_points);
151 for (int i = 0; i < pars->num_y_points; ++i) {
152 fprintf(file, " %d %d", pars->scaling_points_y[i][0],
153 pars->scaling_points_y[i][1]);
154 }
155 fprintf(file, "\n\tsCb %d", pars->num_cb_points);
156 for (int i = 0; i < pars->num_cb_points; ++i) {
157 fprintf(file, " %d %d", pars->scaling_points_cb[i][0],
158 pars->scaling_points_cb[i][1]);
159 }
160 fprintf(file, "\n\tsCr %d", pars->num_cr_points);
161 for (int i = 0; i < pars->num_cr_points; ++i) {
162 fprintf(file, " %d %d", pars->scaling_points_cr[i][0],
163 pars->scaling_points_cr[i][1]);
164 }
165 fprintf(file, "\n\tcY");
166 const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
167 for (int i = 0; i < n; ++i) {
168 fprintf(file, " %d", pars->ar_coeffs_y[i]);
169 }
170 fprintf(file, "\n\tcCb");
171 for (int i = 0; i <= n; ++i) {
172 fprintf(file, " %d", pars->ar_coeffs_cb[i]);
173 }
174 fprintf(file, "\n\tcCr");
175 for (int i = 0; i <= n; ++i) {
176 fprintf(file, " %d", pars->ar_coeffs_cr[i]);
177 }
178 fprintf(file, "\n");
179 }
180 }
181
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)182 void aom_film_grain_table_append(aom_film_grain_table_t *t, int64_t time_stamp,
183 int64_t end_time,
184 const aom_film_grain_t *grain) {
185 if (!t->tail || memcmp(grain, &t->tail->params, sizeof(*grain))) {
186 aom_film_grain_table_entry_t *new_tail = aom_malloc(sizeof(*new_tail));
187 memset(new_tail, 0, sizeof(*new_tail));
188 if (t->tail) t->tail->next = new_tail;
189 if (!t->head) t->head = new_tail;
190 t->tail = new_tail;
191
192 new_tail->start_time = time_stamp;
193 new_tail->end_time = end_time;
194 new_tail->params = *grain;
195 } else {
196 t->tail->end_time = AOMMAX(t->tail->end_time, end_time);
197 t->tail->start_time = AOMMIN(t->tail->start_time, time_stamp);
198 }
199 }
200
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)201 int aom_film_grain_table_lookup(aom_film_grain_table_t *t, int64_t time_stamp,
202 int64_t end_time, int erase,
203 aom_film_grain_t *grain) {
204 aom_film_grain_table_entry_t *entry = t->head;
205 aom_film_grain_table_entry_t *prev_entry = 0;
206 int16_t random_seed = grain ? grain->random_seed : 0;
207 if (grain) memset(grain, 0, sizeof(*grain));
208
209 while (entry) {
210 aom_film_grain_table_entry_t *next = entry->next;
211 if (time_stamp >= entry->start_time && time_stamp < entry->end_time) {
212 if (grain) {
213 *grain = entry->params;
214 if (time_stamp != 0) grain->random_seed = random_seed;
215 }
216 if (!erase) return 1;
217
218 const int64_t entry_end_time = entry->end_time;
219 if (time_stamp <= entry->start_time && end_time >= entry->end_time) {
220 if (t->tail == entry) t->tail = prev_entry;
221 if (prev_entry) {
222 prev_entry->next = entry->next;
223 } else {
224 t->head = entry->next;
225 }
226 aom_free(entry);
227 } else if (time_stamp <= entry->start_time &&
228 end_time < entry->end_time) {
229 entry->start_time = end_time;
230 } else if (time_stamp > entry->start_time &&
231 end_time >= entry->end_time) {
232 entry->end_time = time_stamp;
233 } else {
234 aom_film_grain_table_entry_t *new_entry =
235 aom_malloc(sizeof(*new_entry));
236 new_entry->next = entry->next;
237 new_entry->start_time = end_time;
238 new_entry->end_time = entry->end_time;
239 new_entry->params = entry->params;
240 entry->next = new_entry;
241 entry->end_time = time_stamp;
242 if (t->tail == entry) t->tail = new_entry;
243 }
244 // If segments aren't aligned, delete from the beggining of subsequent
245 // segments
246 if (end_time > entry_end_time) {
247 aom_film_grain_table_lookup(t, entry->end_time, end_time, 1, 0);
248 }
249 return 1;
250 }
251 prev_entry = entry;
252 entry = next;
253 }
254 return 0;
255 }
256
aom_film_grain_table_read(aom_film_grain_table_t * t,const char * filename,struct aom_internal_error_info * error_info)257 aom_codec_err_t aom_film_grain_table_read(
258 aom_film_grain_table_t *t, const char *filename,
259 struct aom_internal_error_info *error_info) {
260 FILE *file = fopen(filename, "rb");
261 if (!file) {
262 aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open %s",
263 filename);
264 return error_info->error_code;
265 }
266 error_info->error_code = AOM_CODEC_OK;
267
268 // Read in one extra character as there should be white space after
269 // the header.
270 char magic[9];
271 if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) {
272 aom_internal_error(error_info, AOM_CODEC_ERROR,
273 "Unable to read (or invalid) file magic");
274 fclose(file);
275 return error_info->error_code;
276 }
277
278 aom_film_grain_table_entry_t *prev_entry = 0;
279 while (!feof(file)) {
280 aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry));
281 memset(entry, 0, sizeof(*entry));
282 grain_table_entry_read(file, error_info, entry);
283 entry->next = 0;
284
285 if (prev_entry) prev_entry->next = entry;
286 if (!t->head) t->head = entry;
287 t->tail = entry;
288 prev_entry = entry;
289
290 if (error_info->error_code != AOM_CODEC_OK) break;
291 }
292
293 fclose(file);
294 return error_info->error_code;
295 }
296
aom_film_grain_table_write(const aom_film_grain_table_t * t,const char * filename,struct aom_internal_error_info * error_info)297 aom_codec_err_t aom_film_grain_table_write(
298 const aom_film_grain_table_t *t, const char *filename,
299 struct aom_internal_error_info *error_info) {
300 error_info->error_code = AOM_CODEC_OK;
301
302 FILE *file = fopen(filename, "wb");
303 if (!file) {
304 aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open file %s",
305 filename);
306 return error_info->error_code;
307 }
308
309 if (!fwrite(kFileMagic, 8, 1, file)) {
310 aom_internal_error(error_info, AOM_CODEC_ERROR,
311 "Unable to write file magic");
312 fclose(file);
313 return error_info->error_code;
314 }
315
316 fprintf(file, "\n");
317 aom_film_grain_table_entry_t *entry = t->head;
318 while (entry) {
319 grain_table_entry_write(file, entry);
320 entry = entry->next;
321 }
322 fclose(file);
323 return error_info->error_code;
324 }
325
aom_film_grain_table_free(aom_film_grain_table_t * t)326 void aom_film_grain_table_free(aom_film_grain_table_t *t) {
327 aom_film_grain_table_entry_t *entry = t->head;
328 while (entry) {
329 aom_film_grain_table_entry_t *next = entry->next;
330 aom_free(entry);
331 entry = next;
332 }
333 memset(t, 0, sizeof(*t));
334 }
335