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 if (fscanf(file, "\n\tcY")) {
109 aom_internal_error(error_info, AOM_CODEC_ERROR,
110 "Unable to read Y coeffs header (cY)");
111 return;
112 }
113 const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
114 for (int i = 0; i < n; ++i) {
115 if (1 != fscanf(file, "%d", &pars->ar_coeffs_y[i])) {
116 aom_internal_error(error_info, AOM_CODEC_ERROR,
117 "Unable to read Y coeffs");
118 return;
119 }
120 }
121 if (fscanf(file, "\n\tcCb")) {
122 aom_internal_error(error_info, AOM_CODEC_ERROR,
123 "Unable to read Cb coeffs header (cCb)");
124 return;
125 }
126 for (int i = 0; i <= n; ++i) {
127 if (1 != fscanf(file, "%d", &pars->ar_coeffs_cb[i])) {
128 aom_internal_error(error_info, AOM_CODEC_ERROR,
129 "Unable to read Cb coeffs");
130 return;
131 }
132 }
133 if (fscanf(file, "\n\tcCr")) {
134 aom_internal_error(error_info, AOM_CODEC_ERROR,
135 "Unable read to Cr coeffs header (cCr)");
136 return;
137 }
138 for (int i = 0; i <= n; ++i) {
139 if (1 != fscanf(file, "%d", &pars->ar_coeffs_cr[i])) {
140 aom_internal_error(error_info, AOM_CODEC_ERROR,
141 "Unable to read Cr coeffs");
142 return;
143 }
144 }
145 (void)fscanf(file, "\n");
146 }
147 }
148
grain_table_entry_write(FILE * file,aom_film_grain_table_entry_t * entry)149 static void grain_table_entry_write(FILE *file,
150 aom_film_grain_table_entry_t *entry) {
151 const aom_film_grain_t *pars = &entry->params;
152 fprintf(file, "E %" PRId64 " %" PRId64 " %d %d %d\n", entry->start_time,
153 entry->end_time, pars->apply_grain, pars->random_seed,
154 pars->update_parameters);
155 if (pars->update_parameters) {
156 fprintf(file, "\tp %d %d %d %d %d %d %d %d %d %d %d %d\n",
157 pars->ar_coeff_lag, pars->ar_coeff_shift, pars->grain_scale_shift,
158 pars->scaling_shift, pars->chroma_scaling_from_luma,
159 pars->overlap_flag, pars->cb_mult, pars->cb_luma_mult,
160 pars->cb_offset, pars->cr_mult, pars->cr_luma_mult,
161 pars->cr_offset);
162 fprintf(file, "\tsY %d ", pars->num_y_points);
163 for (int i = 0; i < pars->num_y_points; ++i) {
164 fprintf(file, " %d %d", pars->scaling_points_y[i][0],
165 pars->scaling_points_y[i][1]);
166 }
167 fprintf(file, "\n\tsCb %d", pars->num_cb_points);
168 for (int i = 0; i < pars->num_cb_points; ++i) {
169 fprintf(file, " %d %d", pars->scaling_points_cb[i][0],
170 pars->scaling_points_cb[i][1]);
171 }
172 fprintf(file, "\n\tsCr %d", pars->num_cr_points);
173 for (int i = 0; i < pars->num_cr_points; ++i) {
174 fprintf(file, " %d %d", pars->scaling_points_cr[i][0],
175 pars->scaling_points_cr[i][1]);
176 }
177 fprintf(file, "\n\tcY");
178 const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
179 for (int i = 0; i < n; ++i) {
180 fprintf(file, " %d", pars->ar_coeffs_y[i]);
181 }
182 fprintf(file, "\n\tcCb");
183 for (int i = 0; i <= n; ++i) {
184 fprintf(file, " %d", pars->ar_coeffs_cb[i]);
185 }
186 fprintf(file, "\n\tcCr");
187 for (int i = 0; i <= n; ++i) {
188 fprintf(file, " %d", pars->ar_coeffs_cr[i]);
189 }
190 fprintf(file, "\n");
191 }
192 }
193
194 // TODO(https://crbug.com/aomedia/3228): Update this function to return an
195 // integer status.
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)196 void aom_film_grain_table_append(aom_film_grain_table_t *t, int64_t time_stamp,
197 int64_t end_time,
198 const aom_film_grain_t *grain) {
199 if (!t->tail || memcmp(grain, &t->tail->params, sizeof(*grain))) {
200 aom_film_grain_table_entry_t *new_tail = aom_malloc(sizeof(*new_tail));
201 if (!new_tail) return;
202 memset(new_tail, 0, sizeof(*new_tail));
203 if (t->tail) t->tail->next = new_tail;
204 if (!t->head) t->head = new_tail;
205 t->tail = new_tail;
206
207 new_tail->start_time = time_stamp;
208 new_tail->end_time = end_time;
209 new_tail->params = *grain;
210 } else {
211 t->tail->end_time = AOMMAX(t->tail->end_time, end_time);
212 t->tail->start_time = AOMMIN(t->tail->start_time, time_stamp);
213 }
214 }
215
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)216 int aom_film_grain_table_lookup(aom_film_grain_table_t *t, int64_t time_stamp,
217 int64_t end_time, int erase,
218 aom_film_grain_t *grain) {
219 aom_film_grain_table_entry_t *entry = t->head;
220 aom_film_grain_table_entry_t *prev_entry = NULL;
221 uint16_t random_seed = grain ? grain->random_seed : 0;
222 if (grain) memset(grain, 0, sizeof(*grain));
223
224 while (entry) {
225 aom_film_grain_table_entry_t *next = entry->next;
226 if (time_stamp >= entry->start_time && time_stamp < entry->end_time) {
227 if (grain) {
228 *grain = entry->params;
229 if (time_stamp != 0) grain->random_seed = random_seed;
230 }
231 if (!erase) return 1;
232
233 const int64_t entry_end_time = entry->end_time;
234 if (time_stamp <= entry->start_time && end_time >= entry->end_time) {
235 if (t->tail == entry) t->tail = prev_entry;
236 if (prev_entry) {
237 prev_entry->next = entry->next;
238 } else {
239 t->head = entry->next;
240 }
241 aom_free(entry);
242 } else if (time_stamp <= entry->start_time &&
243 end_time < entry->end_time) {
244 entry->start_time = end_time;
245 } else if (time_stamp > entry->start_time &&
246 end_time >= entry->end_time) {
247 entry->end_time = time_stamp;
248 } else {
249 aom_film_grain_table_entry_t *new_entry =
250 aom_malloc(sizeof(*new_entry));
251 if (!new_entry) return 0;
252 new_entry->next = entry->next;
253 new_entry->start_time = end_time;
254 new_entry->end_time = entry->end_time;
255 new_entry->params = entry->params;
256 entry->next = new_entry;
257 entry->end_time = time_stamp;
258 if (t->tail == entry) t->tail = new_entry;
259 }
260 // If segments aren't aligned, delete from the beginning of subsequent
261 // segments
262 if (end_time > entry_end_time) {
263 // Ignoring the return value here is safe since we're erasing from the
264 // beginning of subsequent entries.
265 aom_film_grain_table_lookup(t, entry_end_time, end_time, /*erase=*/1,
266 NULL);
267 }
268 return 1;
269 }
270 prev_entry = entry;
271 entry = next;
272 }
273 return 0;
274 }
275
aom_film_grain_table_read(aom_film_grain_table_t * t,const char * filename,struct aom_internal_error_info * error_info)276 aom_codec_err_t aom_film_grain_table_read(
277 aom_film_grain_table_t *t, const char *filename,
278 struct aom_internal_error_info *error_info) {
279 FILE *file = fopen(filename, "rb");
280 if (!file) {
281 aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open %s",
282 filename);
283 return error_info->error_code;
284 }
285 error_info->error_code = AOM_CODEC_OK;
286
287 // Read in one extra character as there should be white space after
288 // the header.
289 char magic[9];
290 if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) {
291 aom_internal_error(error_info, AOM_CODEC_ERROR,
292 "Unable to read (or invalid) file magic");
293 fclose(file);
294 return error_info->error_code;
295 }
296
297 aom_film_grain_table_entry_t *prev_entry = NULL;
298 while (!feof(file)) {
299 aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry));
300 if (!entry) {
301 aom_internal_error(error_info, AOM_CODEC_MEM_ERROR,
302 "Unable to allocate grain table entry");
303 break;
304 }
305 memset(entry, 0, sizeof(*entry));
306 grain_table_entry_read(file, error_info, entry);
307 entry->next = NULL;
308
309 if (prev_entry) prev_entry->next = entry;
310 if (!t->head) t->head = entry;
311 t->tail = entry;
312 prev_entry = entry;
313
314 if (error_info->error_code != AOM_CODEC_OK) break;
315 }
316
317 fclose(file);
318 return error_info->error_code;
319 }
320
aom_film_grain_table_write(const aom_film_grain_table_t * t,const char * filename,struct aom_internal_error_info * error_info)321 aom_codec_err_t aom_film_grain_table_write(
322 const aom_film_grain_table_t *t, const char *filename,
323 struct aom_internal_error_info *error_info) {
324 error_info->error_code = AOM_CODEC_OK;
325
326 FILE *file = fopen(filename, "wb");
327 if (!file) {
328 aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open file %s",
329 filename);
330 return error_info->error_code;
331 }
332
333 if (!fwrite(kFileMagic, 8, 1, file)) {
334 aom_internal_error(error_info, AOM_CODEC_ERROR,
335 "Unable to write file magic");
336 fclose(file);
337 return error_info->error_code;
338 }
339
340 fprintf(file, "\n");
341 aom_film_grain_table_entry_t *entry = t->head;
342 while (entry) {
343 grain_table_entry_write(file, entry);
344 entry = entry->next;
345 }
346 fclose(file);
347 return error_info->error_code;
348 }
349
aom_film_grain_table_free(aom_film_grain_table_t * t)350 void aom_film_grain_table_free(aom_film_grain_table_t *t) {
351 aom_film_grain_table_entry_t *entry = t->head;
352 while (entry) {
353 aom_film_grain_table_entry_t *next = entry->next;
354 aom_free(entry);
355 entry = next;
356 }
357 memset(t, 0, sizeof(*t));
358 }
359