Coverage Report

Created: 2025-06-22 08:04

/src/aom/aom_dsp/grain_table.c
Line
Count
Source (jump to first uncovered line)
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
41
static void grain_table_entry_read(FILE *file,
42
                                   struct aom_internal_error_info *error_info,
43
0
                                   aom_film_grain_table_entry_t *entry) {
44
0
  aom_film_grain_t *pars = &entry->params;
45
0
  int num_read =
46
0
      fscanf(file, "E %" PRId64 " %" PRId64 " %d %hd %d\n", &entry->start_time,
47
0
             &entry->end_time, &pars->apply_grain, &pars->random_seed,
48
0
             &pars->update_parameters);
49
0
  if (num_read == 0 && feof(file)) return;
50
0
  if (num_read != 5) {
51
0
    aom_internal_error(error_info, AOM_CODEC_ERROR,
52
0
                       "Unable to read entry header. Read %d != 5", num_read);
53
0
    return;
54
0
  }
55
0
  if (pars->update_parameters) {
56
0
    num_read = fscanf(file, "p %d %d %d %d %d %d %d %d %d %d %d %d\n",
57
0
                      &pars->ar_coeff_lag, &pars->ar_coeff_shift,
58
0
                      &pars->grain_scale_shift, &pars->scaling_shift,
59
0
                      &pars->chroma_scaling_from_luma, &pars->overlap_flag,
60
0
                      &pars->cb_mult, &pars->cb_luma_mult, &pars->cb_offset,
61
0
                      &pars->cr_mult, &pars->cr_luma_mult, &pars->cr_offset);
62
0
    if (num_read != 12) {
63
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
64
0
                         "Unable to read entry params. Read %d != 12",
65
0
                         num_read);
66
0
      return;
67
0
    }
68
0
    if (!fscanf(file, "\tsY %d ", &pars->num_y_points)) {
69
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
70
0
                         "Unable to read num y points");
71
0
      return;
72
0
    }
73
0
    for (int i = 0; i < pars->num_y_points; ++i) {
74
0
      if (2 != fscanf(file, "%d %d", &pars->scaling_points_y[i][0],
75
0
                      &pars->scaling_points_y[i][1])) {
76
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
77
0
                           "Unable to read y scaling points");
78
0
        return;
79
0
      }
80
0
    }
81
0
    if (!fscanf(file, "\n\tsCb %d", &pars->num_cb_points)) {
82
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
83
0
                         "Unable to read num cb points");
84
0
      return;
85
0
    }
86
0
    for (int i = 0; i < pars->num_cb_points; ++i) {
87
0
      if (2 != fscanf(file, "%d %d", &pars->scaling_points_cb[i][0],
88
0
                      &pars->scaling_points_cb[i][1])) {
89
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
90
0
                           "Unable to read cb scaling points");
91
0
        return;
92
0
      }
93
0
    }
94
0
    if (!fscanf(file, "\n\tsCr %d", &pars->num_cr_points)) {
95
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
96
0
                         "Unable to read num cr points");
97
0
      return;
98
0
    }
99
0
    for (int i = 0; i < pars->num_cr_points; ++i) {
100
0
      if (2 != fscanf(file, "%d %d", &pars->scaling_points_cr[i][0],
101
0
                      &pars->scaling_points_cr[i][1])) {
102
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
103
0
                           "Unable to read cr scaling points");
104
0
        return;
105
0
      }
106
0
    }
107
108
0
    if (fscanf(file, "\n\tcY")) {
109
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
110
0
                         "Unable to read Y coeffs header (cY)");
111
0
      return;
112
0
    }
113
0
    const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
114
0
    for (int i = 0; i < n; ++i) {
115
0
      if (1 != fscanf(file, "%d", &pars->ar_coeffs_y[i])) {
116
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
117
0
                           "Unable to read Y coeffs");
118
0
        return;
119
0
      }
120
0
    }
121
0
    if (fscanf(file, "\n\tcCb")) {
122
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
123
0
                         "Unable to read Cb coeffs header (cCb)");
124
0
      return;
125
0
    }
126
0
    for (int i = 0; i <= n; ++i) {
127
0
      if (1 != fscanf(file, "%d", &pars->ar_coeffs_cb[i])) {
128
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
129
0
                           "Unable to read Cb coeffs");
130
0
        return;
131
0
      }
132
0
    }
133
0
    if (fscanf(file, "\n\tcCr")) {
134
0
      aom_internal_error(error_info, AOM_CODEC_ERROR,
135
0
                         "Unable read to Cr coeffs header (cCr)");
136
0
      return;
137
0
    }
138
0
    for (int i = 0; i <= n; ++i) {
139
0
      if (1 != fscanf(file, "%d", &pars->ar_coeffs_cr[i])) {
140
0
        aom_internal_error(error_info, AOM_CODEC_ERROR,
141
0
                           "Unable to read Cr coeffs");
142
0
        return;
143
0
      }
144
0
    }
145
0
    (void)fscanf(file, "\n");
146
0
  }
147
0
}
148
149
static void grain_table_entry_write(FILE *file,
150
0
                                    aom_film_grain_table_entry_t *entry) {
151
0
  const aom_film_grain_t *pars = &entry->params;
152
0
  fprintf(file, "E %" PRId64 " %" PRId64 " %d %d %d\n", entry->start_time,
153
0
          entry->end_time, pars->apply_grain, pars->random_seed,
154
0
          pars->update_parameters);
155
0
  if (pars->update_parameters) {
156
0
    fprintf(file, "\tp %d %d %d %d %d %d %d %d %d %d %d %d\n",
157
0
            pars->ar_coeff_lag, pars->ar_coeff_shift, pars->grain_scale_shift,
158
0
            pars->scaling_shift, pars->chroma_scaling_from_luma,
159
0
            pars->overlap_flag, pars->cb_mult, pars->cb_luma_mult,
160
0
            pars->cb_offset, pars->cr_mult, pars->cr_luma_mult,
161
0
            pars->cr_offset);
162
0
    fprintf(file, "\tsY %d ", pars->num_y_points);
163
0
    for (int i = 0; i < pars->num_y_points; ++i) {
164
0
      fprintf(file, " %d %d", pars->scaling_points_y[i][0],
165
0
              pars->scaling_points_y[i][1]);
166
0
    }
167
0
    fprintf(file, "\n\tsCb %d", pars->num_cb_points);
168
0
    for (int i = 0; i < pars->num_cb_points; ++i) {
169
0
      fprintf(file, " %d %d", pars->scaling_points_cb[i][0],
170
0
              pars->scaling_points_cb[i][1]);
171
0
    }
172
0
    fprintf(file, "\n\tsCr %d", pars->num_cr_points);
173
0
    for (int i = 0; i < pars->num_cr_points; ++i) {
174
0
      fprintf(file, " %d %d", pars->scaling_points_cr[i][0],
175
0
              pars->scaling_points_cr[i][1]);
176
0
    }
177
0
    fprintf(file, "\n\tcY");
178
0
    const int n = 2 * pars->ar_coeff_lag * (pars->ar_coeff_lag + 1);
179
0
    for (int i = 0; i < n; ++i) {
180
0
      fprintf(file, " %d", pars->ar_coeffs_y[i]);
181
0
    }
182
0
    fprintf(file, "\n\tcCb");
183
0
    for (int i = 0; i <= n; ++i) {
184
0
      fprintf(file, " %d", pars->ar_coeffs_cb[i]);
185
0
    }
186
0
    fprintf(file, "\n\tcCr");
187
0
    for (int i = 0; i <= n; ++i) {
188
0
      fprintf(file, " %d", pars->ar_coeffs_cr[i]);
189
0
    }
190
0
    fprintf(file, "\n");
191
0
  }
192
0
}
193
194
// TODO(https://crbug.com/aomedia/3228): Update this function to return an
195
// integer status.
196
void aom_film_grain_table_append(aom_film_grain_table_t *t, int64_t time_stamp,
197
                                 int64_t end_time,
198
0
                                 const aom_film_grain_t *grain) {
199
0
  if (!t->tail || memcmp(grain, &t->tail->params, sizeof(*grain))) {
200
0
    aom_film_grain_table_entry_t *new_tail = aom_malloc(sizeof(*new_tail));
201
0
    if (!new_tail) return;
202
0
    memset(new_tail, 0, sizeof(*new_tail));
203
0
    if (t->tail) t->tail->next = new_tail;
204
0
    if (!t->head) t->head = new_tail;
205
0
    t->tail = new_tail;
206
207
0
    new_tail->start_time = time_stamp;
208
0
    new_tail->end_time = end_time;
209
0
    new_tail->params = *grain;
210
0
  } else {
211
0
    t->tail->end_time = AOMMAX(t->tail->end_time, end_time);
212
0
    t->tail->start_time = AOMMIN(t->tail->start_time, time_stamp);
213
0
  }
214
0
}
215
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
0
                                aom_film_grain_t *grain) {
219
0
  aom_film_grain_table_entry_t *entry = t->head;
220
0
  aom_film_grain_table_entry_t *prev_entry = NULL;
221
0
  uint16_t random_seed = grain ? grain->random_seed : 0;
222
0
  if (grain) memset(grain, 0, sizeof(*grain));
223
224
0
  while (entry) {
225
0
    aom_film_grain_table_entry_t *next = entry->next;
226
0
    if (time_stamp >= entry->start_time && time_stamp < entry->end_time) {
227
0
      if (grain) {
228
0
        *grain = entry->params;
229
0
        if (time_stamp != 0) grain->random_seed = random_seed;
230
0
      }
231
0
      if (!erase) return 1;
232
233
0
      const int64_t entry_end_time = entry->end_time;
234
0
      if (time_stamp <= entry->start_time && end_time >= entry->end_time) {
235
0
        if (t->tail == entry) t->tail = prev_entry;
236
0
        if (prev_entry) {
237
0
          prev_entry->next = entry->next;
238
0
        } else {
239
0
          t->head = entry->next;
240
0
        }
241
0
        aom_free(entry);
242
0
      } else if (time_stamp <= entry->start_time &&
243
0
                 end_time < entry->end_time) {
244
0
        entry->start_time = end_time;
245
0
      } else if (time_stamp > entry->start_time &&
246
0
                 end_time >= entry->end_time) {
247
0
        entry->end_time = time_stamp;
248
0
      } else {
249
0
        aom_film_grain_table_entry_t *new_entry =
250
0
            aom_malloc(sizeof(*new_entry));
251
0
        if (!new_entry) return 0;
252
0
        new_entry->next = entry->next;
253
0
        new_entry->start_time = end_time;
254
0
        new_entry->end_time = entry->end_time;
255
0
        new_entry->params = entry->params;
256
0
        entry->next = new_entry;
257
0
        entry->end_time = time_stamp;
258
0
        if (t->tail == entry) t->tail = new_entry;
259
0
      }
260
      // If segments aren't aligned, delete from the beginning of subsequent
261
      // segments
262
0
      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
0
        aom_film_grain_table_lookup(t, entry_end_time, end_time, /*erase=*/1,
266
0
                                    NULL);
267
0
      }
268
0
      return 1;
269
0
    }
270
0
    prev_entry = entry;
271
0
    entry = next;
272
0
  }
273
0
  return 0;
274
0
}
275
276
aom_codec_err_t aom_film_grain_table_read(
277
    aom_film_grain_table_t *t, const char *filename,
278
0
    struct aom_internal_error_info *error_info) {
279
0
  FILE *file = fopen(filename, "rb");
280
0
  if (!file) {
281
0
    aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open %s",
282
0
                       filename);
283
0
    return error_info->error_code;
284
0
  }
285
0
  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
0
  char magic[9];
290
0
  if (!fread(magic, 9, 1, file) || memcmp(magic, kFileMagic, 8)) {
291
0
    aom_internal_error(error_info, AOM_CODEC_ERROR,
292
0
                       "Unable to read (or invalid) file magic");
293
0
    fclose(file);
294
0
    return error_info->error_code;
295
0
  }
296
297
0
  aom_film_grain_table_entry_t *prev_entry = NULL;
298
0
  while (!feof(file)) {
299
0
    aom_film_grain_table_entry_t *entry = aom_malloc(sizeof(*entry));
300
0
    if (!entry) {
301
0
      aom_internal_error(error_info, AOM_CODEC_MEM_ERROR,
302
0
                         "Unable to allocate grain table entry");
303
0
      break;
304
0
    }
305
0
    memset(entry, 0, sizeof(*entry));
306
0
    grain_table_entry_read(file, error_info, entry);
307
0
    entry->next = NULL;
308
309
0
    if (prev_entry) prev_entry->next = entry;
310
0
    if (!t->head) t->head = entry;
311
0
    t->tail = entry;
312
0
    prev_entry = entry;
313
314
0
    if (error_info->error_code != AOM_CODEC_OK) break;
315
0
  }
316
317
0
  fclose(file);
318
0
  return error_info->error_code;
319
0
}
320
321
aom_codec_err_t aom_film_grain_table_write(
322
    const aom_film_grain_table_t *t, const char *filename,
323
0
    struct aom_internal_error_info *error_info) {
324
0
  error_info->error_code = AOM_CODEC_OK;
325
326
0
  FILE *file = fopen(filename, "wb");
327
0
  if (!file) {
328
0
    aom_internal_error(error_info, AOM_CODEC_ERROR, "Unable to open file %s",
329
0
                       filename);
330
0
    return error_info->error_code;
331
0
  }
332
333
0
  if (!fwrite(kFileMagic, 8, 1, file)) {
334
0
    aom_internal_error(error_info, AOM_CODEC_ERROR,
335
0
                       "Unable to write file magic");
336
0
    fclose(file);
337
0
    return error_info->error_code;
338
0
  }
339
340
0
  fprintf(file, "\n");
341
0
  aom_film_grain_table_entry_t *entry = t->head;
342
0
  while (entry) {
343
0
    grain_table_entry_write(file, entry);
344
0
    entry = entry->next;
345
0
  }
346
0
  fclose(file);
347
0
  return error_info->error_code;
348
0
}
349
350
0
void aom_film_grain_table_free(aom_film_grain_table_t *t) {
351
0
  aom_film_grain_table_entry_t *entry = t->head;
352
0
  while (entry) {
353
0
    aom_film_grain_table_entry_t *next = entry->next;
354
0
    aom_free(entry);
355
0
    entry = next;
356
0
  }
357
0
  memset(t, 0, sizeof(*t));
358
0
}