xref: /aosp_15_r20/external/libaom/aom_dsp/grain_table.c (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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