1 | // Copyright (c) 2022-2022, The rav1e contributors. All rights reserved |
2 | // |
3 | // This source code is subject to the terms of the BSD 2 Clause License and |
4 | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
5 | // was not distributed with this source code in the LICENSE file, you can |
6 | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
7 | // Media Patent License 1.0 was not distributed with this source code in the |
8 | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
9 | |
10 | use std::ops::{Range, RangeFrom, RangeTo}; |
11 | |
12 | use arrayvec::ArrayVec; |
13 | use nom::{ |
14 | branch::alt, |
15 | bytes::complete::tag, |
16 | character::complete::{char, digit1, line_ending, multispace0, multispace1, space0, space1}, |
17 | combinator::{eof, map_res, opt, recognize}, |
18 | error::{Error as NomError, ErrorKind, FromExternalError, ParseError}, |
19 | multi::{many1, separated_list0, separated_list1}, |
20 | sequence::{delimited, preceded}, |
21 | AsChar, Compare, Err as NomErr, IResult, InputIter, InputLength, InputTakeAtPosition, Parser, |
22 | Slice, |
23 | }; |
24 | |
25 | use crate::{GrainTableSegment, NUM_UV_COEFFS, NUM_UV_POINTS, NUM_Y_COEFFS, NUM_Y_POINTS}; |
26 | |
27 | /// This file has the implementation details of the grain table. |
28 | /// |
29 | /// The file format is an ascii representation for readability and |
30 | /// editability. Array parameters are separated from the non-array |
31 | /// parameters and prefixed with a few characters to make for easy |
32 | /// localization with a parameter set. Each entry is prefixed with "E" |
33 | /// and the other parameters are only specified if "apply-grain" is |
34 | /// non-zero. |
35 | /// |
36 | /// ```text |
37 | /// filmgrn1 |
38 | /// E <start-time> <end-time> <apply-grain> <random-seed> <dynamic-grain> |
39 | /// p <ar_coeff_lag> <ar_coeff_shift> <grain_scale_shift> ... |
40 | /// sY <num_y_points> <point_0_x> <point_0_y> ... |
41 | /// sCb <num_cb_points> <point_0_x> <point_0_y> ... |
42 | /// sCr <num_cr_points> <point_0_x> <point_0_y> ... |
43 | /// cY <ar_coeff_y_0> .... |
44 | /// cCb <ar_coeff_cb_0> .... |
45 | /// cCr <ar_coeff_cr_0> .... |
46 | /// E <start-time> ... |
47 | /// ``` |
48 | /// |
49 | /// # Errors |
50 | /// |
51 | /// - If the file cannot be opened |
52 | /// - If the file does not contain a properly formatted film grain table |
53 | pub fn parse_grain_table(input: &str) -> anyhow::Result<Vec<GrainTableSegment>> { |
54 | let (input: &str, _) = grain_table_header(input).map_err(|e: Err>| anyhow::anyhow!(e.to_string()))?; |
55 | let (_, segments: Vec) = |
56 | many1(grain_table_segment)(input).map_err(|e: Err>| anyhow::anyhow!(e.to_string()))?; |
57 | Ok(segments.into_iter().flatten().collect()) |
58 | } |
59 | |
60 | fn grain_table_header(input: &str) -> IResult<&str, ()> { |
61 | let (input: &str, _) = delimited(first:multispace0, second:tag("filmgrn1" ), third:line_ending)(input)?; |
62 | Ok((input, ())) |
63 | } |
64 | |
65 | // FIXME: Clippy false positive |
66 | #[allow (clippy::trait_duplication_in_bounds)] |
67 | fn line<I, O, E: ParseError<I>, F>(parser: F) -> impl FnMut(I) -> IResult<I, O, E> |
68 | where |
69 | I: InputTakeAtPosition |
70 | + Clone |
71 | + Slice<Range<usize>> |
72 | + Slice<RangeFrom<usize>> |
73 | + Slice<RangeTo<usize>> |
74 | + InputIter |
75 | + InputLength |
76 | + Compare<&'static str>, |
77 | <I as InputTakeAtPosition>::Item: AsChar + Clone, |
78 | F: Parser<I, O, E>, |
79 | { |
80 | delimited(first:multispace0, second:parser, third:alt((line_ending, eof))) |
81 | } |
82 | |
83 | fn grain_table_segment(input: &str) -> IResult<&str, Option<GrainTableSegment>> { |
84 | let (input, e_params) = e_params(input)?; |
85 | if !e_params.apply { |
86 | // I'm not sure *why* there's even an option to generate a film grain config |
87 | // that doesn't apply film grain. But, well, I didn't make this format. |
88 | return Ok((input, None)); |
89 | } |
90 | |
91 | let (input, p_params) = p_params(input)?; |
92 | let (input, s_y_params) = s_y_params(input)?; |
93 | let (input, s_cb_params) = s_cb_params(input)?; |
94 | let (input, s_cr_params) = s_cr_params(input)?; |
95 | let coeff_count = (2 * p_params.ar_coeff_lag * (p_params.ar_coeff_lag + 1)) as usize; |
96 | let (input, c_y_params) = c_y_params(input, coeff_count)?; |
97 | let (input, c_cb_params) = c_cb_params(input, coeff_count)?; |
98 | let (input, c_cr_params) = c_cr_params(input, coeff_count)?; |
99 | |
100 | Ok(( |
101 | input, |
102 | Some(GrainTableSegment { |
103 | start_time: e_params.start, |
104 | end_time: e_params.end, |
105 | scaling_points_y: s_y_params, |
106 | scaling_points_cb: s_cb_params, |
107 | scaling_points_cr: s_cr_params, |
108 | scaling_shift: p_params.scaling_shift, |
109 | ar_coeff_lag: p_params.ar_coeff_lag, |
110 | ar_coeffs_y: c_y_params, |
111 | ar_coeffs_cb: c_cb_params, |
112 | ar_coeffs_cr: c_cr_params, |
113 | ar_coeff_shift: p_params.ar_coeff_shift, |
114 | cb_mult: p_params.cb_mult, |
115 | cb_luma_mult: p_params.cb_luma_mult, |
116 | cb_offset: p_params.cb_offset, |
117 | cr_mult: p_params.cr_mult, |
118 | cr_luma_mult: p_params.cr_luma_mult, |
119 | cr_offset: p_params.cr_offset, |
120 | overlap_flag: p_params.overlap_flag, |
121 | chroma_scaling_from_luma: p_params.chroma_scaling_from_luma, |
122 | grain_scale_shift: p_params.grain_scale_shift, |
123 | random_seed: e_params.seed, |
124 | }), |
125 | )) |
126 | } |
127 | |
128 | #[derive (Debug, Clone, Copy)] |
129 | struct EParams { |
130 | pub start: u64, |
131 | pub end: u64, |
132 | pub apply: bool, |
133 | pub seed: u16, |
134 | } |
135 | |
136 | fn e_params(input: &str) -> IResult<&str, EParams> { |
137 | let (input, params) = map_res( |
138 | line(preceded( |
139 | tag("E" ), |
140 | preceded(space1, separated_list1(space1, digit1)), |
141 | )), |
142 | |items: Vec<&str>| { |
143 | if items.len() != 5 { |
144 | return Err(NomErr::Failure(NomError::from_external_error( |
145 | input, |
146 | ErrorKind::Verify, |
147 | "Expected 5 values on E line" , |
148 | ))); |
149 | } |
150 | let parsed = EParams { |
151 | start: items[0].parse().map_err(|_e| { |
152 | NomErr::Failure(NomError::from_external_error( |
153 | input, |
154 | ErrorKind::Digit, |
155 | "Failed to parse start_time" , |
156 | )) |
157 | })?, |
158 | end: items[1].parse().map_err(|_e| { |
159 | NomErr::Failure(NomError::from_external_error( |
160 | input, |
161 | ErrorKind::Digit, |
162 | "Failed to parse end_time" , |
163 | )) |
164 | })?, |
165 | apply: items[2].parse::<u8>().map_err(|_e| { |
166 | NomErr::Failure(NomError::from_external_error( |
167 | input, |
168 | ErrorKind::Digit, |
169 | "Failed to parse apply_grain" , |
170 | )) |
171 | })? > 0, |
172 | seed: items[3].parse().map_err(|_e| { |
173 | NomErr::Failure(NomError::from_external_error( |
174 | input, |
175 | ErrorKind::Digit, |
176 | "Failed to parse random_seed" , |
177 | )) |
178 | })?, |
179 | }; |
180 | Ok(parsed) |
181 | }, |
182 | )(input)?; |
183 | |
184 | if params.end < params.start { |
185 | return Err(NomErr::Failure(NomError::from_external_error( |
186 | input, |
187 | ErrorKind::Verify, |
188 | "Start time must be before end time" , |
189 | ))); |
190 | } |
191 | |
192 | Ok((input, params)) |
193 | } |
194 | |
195 | #[derive (Debug, Clone, Copy)] |
196 | struct PParams { |
197 | ar_coeff_lag: u8, |
198 | ar_coeff_shift: u8, |
199 | grain_scale_shift: u8, |
200 | scaling_shift: u8, |
201 | chroma_scaling_from_luma: bool, |
202 | overlap_flag: bool, |
203 | cb_mult: u8, |
204 | cb_luma_mult: u8, |
205 | cb_offset: u16, |
206 | cr_mult: u8, |
207 | cr_luma_mult: u8, |
208 | cr_offset: u16, |
209 | } |
210 | |
211 | #[allow (clippy::too_many_lines)] |
212 | fn p_params(input: &str) -> IResult<&str, PParams> { |
213 | let (input, params) = map_res( |
214 | line(preceded( |
215 | tag("p" ), |
216 | preceded(space1, separated_list1(space1, digit1)), |
217 | )), |
218 | |items: Vec<&str>| { |
219 | if items.len() != 12 { |
220 | return Err(NomErr::Failure(NomError::from_external_error( |
221 | input, |
222 | ErrorKind::Verify, |
223 | "Expected 12 values on p line" , |
224 | ))); |
225 | } |
226 | |
227 | let parsed = PParams { |
228 | ar_coeff_lag: items[0].parse().map_err(|_e| { |
229 | NomErr::Failure(NomError::from_external_error( |
230 | input, |
231 | ErrorKind::Digit, |
232 | "Failed to parse ar_coeff_lag" , |
233 | )) |
234 | })?, |
235 | ar_coeff_shift: items[1].parse().map_err(|_e| { |
236 | NomErr::Failure(NomError::from_external_error( |
237 | input, |
238 | ErrorKind::Digit, |
239 | "Failed to parse ar_coeff_shift" , |
240 | )) |
241 | })?, |
242 | grain_scale_shift: items[2].parse().map_err(|_e| { |
243 | NomErr::Failure(NomError::from_external_error( |
244 | input, |
245 | ErrorKind::Digit, |
246 | "Failed to parse grain_scale_shift" , |
247 | )) |
248 | })?, |
249 | scaling_shift: items[3].parse().map_err(|_e| { |
250 | NomErr::Failure(NomError::from_external_error( |
251 | input, |
252 | ErrorKind::Digit, |
253 | "Failed to parse scaling_shift" , |
254 | )) |
255 | })?, |
256 | chroma_scaling_from_luma: items[4].parse::<u8>().map_err(|_e| { |
257 | NomErr::Failure(NomError::from_external_error( |
258 | input, |
259 | ErrorKind::Digit, |
260 | "Failed to parse chroma_scaling_from_luma" , |
261 | )) |
262 | })? > 0, |
263 | overlap_flag: items[5].parse::<u8>().map_err(|_e| { |
264 | NomErr::Failure(NomError::from_external_error( |
265 | input, |
266 | ErrorKind::Digit, |
267 | "Failed to parse overlap_flag" , |
268 | )) |
269 | })? > 0, |
270 | cb_mult: items[6].parse().map_err(|_e| { |
271 | NomErr::Failure(NomError::from_external_error( |
272 | input, |
273 | ErrorKind::Digit, |
274 | "Failed to parse cb_mult" , |
275 | )) |
276 | })?, |
277 | cb_luma_mult: items[7].parse().map_err(|_e| { |
278 | NomErr::Failure(NomError::from_external_error( |
279 | input, |
280 | ErrorKind::Digit, |
281 | "Failed to parse cb_luma_mult" , |
282 | )) |
283 | })?, |
284 | cb_offset: items[8].parse().map_err(|_e| { |
285 | NomErr::Failure(NomError::from_external_error( |
286 | input, |
287 | ErrorKind::Digit, |
288 | "Failed to parse cb_offset" , |
289 | )) |
290 | })?, |
291 | cr_mult: items[9].parse().map_err(|_e| { |
292 | NomErr::Failure(NomError::from_external_error( |
293 | input, |
294 | ErrorKind::Digit, |
295 | "Failed to parse cr_mult" , |
296 | )) |
297 | })?, |
298 | cr_luma_mult: items[10].parse().map_err(|_e| { |
299 | NomErr::Failure(NomError::from_external_error( |
300 | input, |
301 | ErrorKind::Digit, |
302 | "Failed to parse cr_luma_mult" , |
303 | )) |
304 | })?, |
305 | cr_offset: items[11].parse().map_err(|_e| { |
306 | NomErr::Failure(NomError::from_external_error( |
307 | input, |
308 | ErrorKind::Digit, |
309 | "Failed to parse cr_offset" , |
310 | )) |
311 | })?, |
312 | }; |
313 | Ok(parsed) |
314 | }, |
315 | )(input)?; |
316 | |
317 | if params.scaling_shift < 8 || params.scaling_shift > 11 { |
318 | return Err(NomErr::Failure(NomError::from_external_error( |
319 | input, |
320 | ErrorKind::Verify, |
321 | "scaling_shift must be between 8 and 11" , |
322 | ))); |
323 | } |
324 | if params.ar_coeff_lag > 3 { |
325 | return Err(NomErr::Failure(NomError::from_external_error( |
326 | input, |
327 | ErrorKind::Verify, |
328 | "ar_coeff_lag must be between 0 and 3" , |
329 | ))); |
330 | } |
331 | if params.ar_coeff_shift < 6 || params.ar_coeff_shift > 9 { |
332 | return Err(NomErr::Failure(NomError::from_external_error( |
333 | input, |
334 | ErrorKind::Verify, |
335 | "ar_coeff_shift must be between 6 and 9" , |
336 | ))); |
337 | } |
338 | |
339 | Ok((input, params)) |
340 | } |
341 | |
342 | fn s_y_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_Y_POINTS>> { |
343 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
344 | line(preceded( |
345 | tag("sY" ), |
346 | preceded(space1, separated_list1(space1, digit1)), |
347 | )), |
348 | |items: Vec<&str>| { |
349 | let mut parsed = Vec::with_capacity(items.len()); |
350 | for item in items { |
351 | parsed.push(item.parse::<u8>().map_err(|_e| { |
352 | NomErr::Failure(NomError::from_external_error( |
353 | input, |
354 | ErrorKind::Digit, |
355 | "Failed to parse Y-plane points" , |
356 | )) |
357 | })?); |
358 | } |
359 | Ok(parsed) |
360 | }, |
361 | )(input)?; |
362 | |
363 | let len = values[0] as usize; |
364 | if values.len() != len * 2 + 1 { |
365 | return Err(NomErr::Failure(NomError::from_external_error( |
366 | input, |
367 | ErrorKind::Verify, |
368 | format!( |
369 | "Expected {} Y-plane points, got {}" , |
370 | len * 2, |
371 | values.len() - 1 |
372 | ), |
373 | ))); |
374 | } |
375 | |
376 | Ok(( |
377 | input, |
378 | values[1..] |
379 | .chunks_exact(2) |
380 | .map(|chunk| [chunk[0], chunk[1]]) |
381 | .collect(), |
382 | )) |
383 | } |
384 | |
385 | fn s_cb_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> { |
386 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
387 | line(preceded( |
388 | tag("sCb" ), |
389 | preceded(space1, separated_list1(space1, digit1)), |
390 | )), |
391 | |items: Vec<&str>| { |
392 | let mut parsed = Vec::with_capacity(items.len()); |
393 | for item in items { |
394 | parsed.push(item.parse::<u8>().map_err(|_e| { |
395 | NomErr::Failure(NomError::from_external_error( |
396 | input, |
397 | ErrorKind::Digit, |
398 | "Failed to parse Cb-plane points" , |
399 | )) |
400 | })?); |
401 | } |
402 | Ok(parsed) |
403 | }, |
404 | )(input)?; |
405 | |
406 | let len = values[0] as usize; |
407 | if values.len() != len * 2 + 1 { |
408 | return Err(NomErr::Failure(NomError::from_external_error( |
409 | input, |
410 | ErrorKind::Verify, |
411 | format!( |
412 | "Expected {} Cb-plane points, got {}" , |
413 | len * 2, |
414 | values.len() - 1 |
415 | ), |
416 | ))); |
417 | } |
418 | |
419 | Ok(( |
420 | input, |
421 | values[1..] |
422 | .chunks_exact(2) |
423 | .map(|chunk| [chunk[0], chunk[1]]) |
424 | .collect(), |
425 | )) |
426 | } |
427 | |
428 | fn s_cr_params(input: &str) -> IResult<&str, ArrayVec<[u8; 2], NUM_UV_POINTS>> { |
429 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
430 | line(preceded( |
431 | tag("sCr" ), |
432 | preceded(space1, separated_list1(space1, digit1)), |
433 | )), |
434 | |items: Vec<&str>| { |
435 | let mut parsed = Vec::with_capacity(items.len()); |
436 | for item in items { |
437 | parsed.push(item.parse::<u8>().map_err(|_e| { |
438 | NomErr::Failure(NomError::from_external_error( |
439 | input, |
440 | ErrorKind::Digit, |
441 | "Failed to parse Cr-plane points" , |
442 | )) |
443 | })?); |
444 | } |
445 | Ok(parsed) |
446 | }, |
447 | )(input)?; |
448 | |
449 | let len = values[0] as usize; |
450 | if values.len() != len * 2 + 1 { |
451 | return Err(NomErr::Failure(NomError::from_external_error( |
452 | input, |
453 | ErrorKind::Verify, |
454 | format!( |
455 | "Expected {} Cr-plane points, got {}" , |
456 | len * 2, |
457 | values.len() - 1 |
458 | ), |
459 | ))); |
460 | } |
461 | |
462 | Ok(( |
463 | input, |
464 | values[1..] |
465 | .chunks_exact(2) |
466 | .map(|chunk| [chunk[0], chunk[1]]) |
467 | .collect(), |
468 | )) |
469 | } |
470 | |
471 | fn integer(input: &str) -> IResult<&str, &str> { |
472 | recognize(parser:preceded(first:opt(char('-' )), second:digit1))(input) |
473 | } |
474 | |
475 | fn c_y_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_Y_COEFFS>> { |
476 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
477 | line(preceded( |
478 | tag("cY" ), |
479 | preceded(space0, separated_list0(multispace1, integer)), |
480 | )), |
481 | |items: Vec<&str>| { |
482 | let mut parsed = Vec::with_capacity(items.len()); |
483 | for item in items { |
484 | parsed.push(item.parse::<i8>().map_err(|_e| { |
485 | NomErr::Failure(NomError::from_external_error( |
486 | input, |
487 | ErrorKind::Digit, |
488 | "Failed to parse Y-plane coeffs" , |
489 | )) |
490 | })?); |
491 | } |
492 | Ok(parsed) |
493 | }, |
494 | )(input)?; |
495 | |
496 | if values.len() != count { |
497 | return Err(NomErr::Failure(NomError::from_external_error( |
498 | input, |
499 | ErrorKind::Verify, |
500 | format!("Expected {} Y-plane coeffs, got {}" , count, values.len()), |
501 | ))); |
502 | } |
503 | |
504 | Ok((input, values.into_iter().collect())) |
505 | } |
506 | |
507 | fn c_cb_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> { |
508 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
509 | line(preceded( |
510 | tag("cCb" ), |
511 | preceded(space1, separated_list1(multispace1, integer)), |
512 | )), |
513 | |items: Vec<&str>| { |
514 | let mut parsed = Vec::with_capacity(items.len()); |
515 | for item in items { |
516 | parsed.push(item.parse::<i8>().map_err(|_e| { |
517 | NomErr::Failure(NomError::from_external_error( |
518 | input, |
519 | ErrorKind::Digit, |
520 | "Failed to parse Cb-plane coeffs" , |
521 | )) |
522 | })?); |
523 | } |
524 | Ok(parsed) |
525 | }, |
526 | )(input)?; |
527 | |
528 | if values.len() != count + 1 { |
529 | return Err(NomErr::Failure(NomError::from_external_error( |
530 | input, |
531 | ErrorKind::Verify, |
532 | format!( |
533 | "Expected {} Cb-plane coeffs, got {}" , |
534 | count + 1, |
535 | values.len() |
536 | ), |
537 | ))); |
538 | } |
539 | |
540 | Ok((input, values.into_iter().collect())) |
541 | } |
542 | |
543 | fn c_cr_params(input: &str, count: usize) -> IResult<&str, ArrayVec<i8, NUM_UV_COEFFS>> { |
544 | let (input, values) = map_res::<_, _, _, _, NomErr<NomError<&str>>, _, _>( |
545 | line(preceded( |
546 | tag("cCr" ), |
547 | preceded(space1, separated_list1(multispace1, integer)), |
548 | )), |
549 | |items: Vec<&str>| { |
550 | let mut parsed = Vec::with_capacity(items.len()); |
551 | for item in items { |
552 | parsed.push(item.parse::<i8>().map_err(|_e| { |
553 | NomErr::Failure(NomError::from_external_error( |
554 | input, |
555 | ErrorKind::Digit, |
556 | "Failed to parse Cr-plane coeffs" , |
557 | )) |
558 | })?); |
559 | } |
560 | Ok(parsed) |
561 | }, |
562 | )(input)?; |
563 | |
564 | if values.len() != count + 1 { |
565 | return Err(NomErr::Failure(NomError::from_external_error( |
566 | input, |
567 | ErrorKind::Verify, |
568 | format!( |
569 | "Expected {} Cr-plane coeffs, got {}" , |
570 | count + 1, |
571 | values.len() |
572 | ), |
573 | ))); |
574 | } |
575 | |
576 | Ok((input, values.into_iter().collect())) |
577 | } |
578 | |
579 | #[test ] |
580 | fn parse_luma_only_table() { |
581 | // This is the luma-only table format generated by |
582 | // both aomenc's photon noise utility and by av1an. |
583 | let input = r#"filmgrn1 |
584 | E 0 9223372036854775807 1 7391 1 |
585 | p 0 6 0 8 0 1 0 0 0 0 0 0 |
586 | sY 14 0 20 20 5 39 4 59 3 78 3 98 3 118 3 137 3 157 3 177 3 196 3 216 4 235 4 255 4 |
587 | sCb 0 |
588 | sCr 0 |
589 | cY |
590 | cCb 0 |
591 | cCr 0 |
592 | "# ; |
593 | let expected = GrainTableSegment { |
594 | start_time: 0, |
595 | end_time: 9_223_372_036_854_775_807, |
596 | scaling_points_y: ArrayVec::from([ |
597 | [0, 20], |
598 | [20, 5], |
599 | [39, 4], |
600 | [59, 3], |
601 | [78, 3], |
602 | [98, 3], |
603 | [118, 3], |
604 | [137, 3], |
605 | [157, 3], |
606 | [177, 3], |
607 | [196, 3], |
608 | [216, 4], |
609 | [235, 4], |
610 | [255, 4], |
611 | ]), |
612 | scaling_points_cb: ArrayVec::new(), |
613 | scaling_points_cr: ArrayVec::new(), |
614 | scaling_shift: 8, |
615 | ar_coeff_lag: 0, |
616 | ar_coeffs_y: ArrayVec::new(), |
617 | ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity" ), |
618 | ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity" ), |
619 | ar_coeff_shift: 6, |
620 | cb_mult: 0, |
621 | cb_luma_mult: 0, |
622 | cb_offset: 0, |
623 | cr_mult: 0, |
624 | cr_luma_mult: 0, |
625 | cr_offset: 0, |
626 | overlap_flag: true, |
627 | chroma_scaling_from_luma: false, |
628 | grain_scale_shift: 0, |
629 | random_seed: 7391, |
630 | }; |
631 | let output = parse_grain_table(input).expect("Test failed" ); |
632 | assert_eq!(vec![expected], output); |
633 | } |
634 | |
635 | #[test ] |
636 | fn parse_luma_chroma_table() { |
637 | // This is the luma+chroma table format generated by |
638 | // both aomenc's photon noise utility and by av1an. |
639 | let input = r#"filmgrn1 |
640 | E 0 9223372036854775807 1 7391 1 |
641 | p 0 6 0 8 0 1 128 192 256 128 192 256 |
642 | sY 14 0 0 20 4 39 3 59 3 78 3 98 3 118 4 137 4 157 4 177 4 196 4 216 5 235 5 255 5 |
643 | sCb 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1 |
644 | sCr 10 0 0 28 0 57 0 85 0 113 0 142 0 170 0 198 0 227 0 255 1 |
645 | cY |
646 | cCb 0 |
647 | cCr 0 |
648 | "# ; |
649 | let expected = GrainTableSegment { |
650 | start_time: 0, |
651 | end_time: 9_223_372_036_854_775_807, |
652 | scaling_points_y: ArrayVec::from([ |
653 | [0, 0], |
654 | [20, 4], |
655 | [39, 3], |
656 | [59, 3], |
657 | [78, 3], |
658 | [98, 3], |
659 | [118, 4], |
660 | [137, 4], |
661 | [157, 4], |
662 | [177, 4], |
663 | [196, 4], |
664 | [216, 5], |
665 | [235, 5], |
666 | [255, 5], |
667 | ]), |
668 | scaling_points_cb: ArrayVec::from([ |
669 | [0, 0], |
670 | [28, 0], |
671 | [57, 0], |
672 | [85, 0], |
673 | [113, 0], |
674 | [142, 0], |
675 | [170, 0], |
676 | [198, 0], |
677 | [227, 0], |
678 | [255, 1], |
679 | ]), |
680 | scaling_points_cr: ArrayVec::from([ |
681 | [0, 0], |
682 | [28, 0], |
683 | [57, 0], |
684 | [85, 0], |
685 | [113, 0], |
686 | [142, 0], |
687 | [170, 0], |
688 | [198, 0], |
689 | [227, 0], |
690 | [255, 1], |
691 | ]), |
692 | scaling_shift: 8, |
693 | ar_coeff_lag: 0, |
694 | ar_coeffs_y: ArrayVec::new(), |
695 | ar_coeffs_cb: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity" ), |
696 | ar_coeffs_cr: ArrayVec::try_from([0].as_slice()).expect("Arrayvec has capacity" ), |
697 | ar_coeff_shift: 6, |
698 | cb_mult: 128, |
699 | cb_luma_mult: 192, |
700 | cb_offset: 256, |
701 | cr_mult: 128, |
702 | cr_luma_mult: 192, |
703 | cr_offset: 256, |
704 | overlap_flag: true, |
705 | chroma_scaling_from_luma: false, |
706 | grain_scale_shift: 0, |
707 | random_seed: 7391, |
708 | }; |
709 | let output = parse_grain_table(input).expect("Test failed" ); |
710 | assert_eq!(vec![expected], output); |
711 | } |
712 | |
713 | #[test ] |
714 | fn parse_complex_table() { |
715 | let input = r#"filmgrn1 |
716 | E 0 417083 1 7391 1 |
717 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
718 | sY 6 0 53 13 53 40 64 94 49 121 46 255 46 |
719 | sCb 2 0 14 255 13 |
720 | sCr 2 0 12 255 14 |
721 | cY 1 -4 1 4 8 3 -2 -6 9 14 -27 -25 -2 4 5 15 -80 94 28 -3 -2 6 -47 121 |
722 | cCb -3 1 -4 6 -1 2 -2 1 11 -10 -2 -16 -1 3 -2 -14 -26 65 19 -3 -5 2 -6 75 -1 |
723 | cCr 0 0 -4 8 -1 0 1 2 -1 -9 4 -7 -5 -2 -5 -14 0 45 18 3 -3 4 8 49 5 |
724 | E 417083 7090416 1 0 1 |
725 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
726 | sY 4 0 46 40 54 108 39 255 38 |
727 | sCb 2 0 14 255 14 |
728 | sCr 2 0 12 255 14 |
729 | cY 1 -4 1 5 8 4 -2 -6 9 13 -28 -28 -5 5 5 13 -76 91 32 -1 -3 7 -50 124 |
730 | cCb -2 1 -3 3 -2 1 -1 2 8 -10 0 -12 -2 2 -1 -14 -20 61 18 -1 -4 -2 -1 70 -1 |
731 | cCr 0 0 -3 6 -1 -1 0 1 -2 -8 6 -4 -5 -2 -6 -12 4 41 17 4 -2 3 13 44 5 |
732 | E 7090416 7507500 1 0 1 |
733 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
734 | sY 4 0 54 40 64 108 46 255 44 |
735 | sCb 2 0 14 255 13 |
736 | sCr 2 0 12 255 14 |
737 | cY 1 -4 2 3 7 3 -2 -6 9 14 -26 -25 -3 5 6 15 -81 95 27 -3 -3 5 -46 121 |
738 | cCb -2 1 -4 4 -2 1 -1 2 9 -12 3 -13 -1 2 -2 -16 -26 66 17 -2 -5 -1 1 73 0 |
739 | cCr 1 -1 -5 8 -1 -1 1 1 -3 -9 9 -5 -6 -2 -7 -14 1 44 17 3 -3 5 15 46 4 |
740 | E 7507500 10010000 1 0 1 |
741 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
742 | sY 4 0 49 40 59 108 43 255 41 |
743 | sCb 2 0 14 255 14 |
744 | sCr 2 0 13 255 15 |
745 | cY 1 -4 0 6 8 3 -2 -5 8 14 -29 -26 -3 4 3 15 -76 92 29 -2 -3 8 -49 121 |
746 | cCb -3 0 -3 6 0 1 -2 1 10 -9 -4 -15 -1 2 -1 -13 -22 62 20 -3 -4 2 -7 73 -1 |
747 | cCr -1 0 -3 6 0 0 0 2 0 -9 2 -7 -5 -1 -4 -14 0 45 19 2 -2 3 7 50 4 |
748 | E 10010000 13346666 1 0 1 |
749 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
750 | sY 6 0 33 27 39 40 53 54 55 108 52 255 52 |
751 | sCb 2 0 16 255 14 |
752 | sCr 2 0 11 255 12 |
753 | cY 1 -4 1 5 9 4 -2 -7 12 11 -27 -30 -5 5 6 10 -73 89 35 -1 -3 6 -49 124 |
754 | cCb -2 0 -2 1 -2 1 -2 0 9 -9 -2 -14 -1 2 0 -11 -26 65 18 -2 -4 -2 -8 75 -5 |
755 | cCr 0 0 -4 5 -2 0 1 3 -1 -9 6 -5 -5 -1 -6 -14 1 43 18 4 -3 3 13 49 3 |
756 | E 13346666 16683333 1 0 1 |
757 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
758 | sY 6 0 36 27 42 40 58 54 60 108 57 255 57 |
759 | sCb 2 0 15 255 14 |
760 | sCr 4 0 11 40 17 94 13 255 13 |
761 | cY 1 -4 1 5 8 3 -2 -6 10 12 -27 -27 -4 4 5 12 -73 90 32 -2 -3 6 -47 121 |
762 | cCb -2 0 -3 4 -1 1 -2 0 10 -9 -2 -14 1 3 -1 -10 -24 62 16 -2 -4 0 -6 72 -7 |
763 | cCr 0 0 -3 6 -1 0 1 3 1 -9 3 -7 -5 -1 -5 -14 -2 46 19 2 -3 3 7 54 3 |
764 | E 16683333 17100416 1 0 1 |
765 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
766 | sY 7 0 41 13 41 27 49 40 66 54 68 108 65 255 65 |
767 | sCb 2 0 18 255 14 |
768 | sCr 4 0 11 40 18 67 14 255 13 |
769 | cY 0 -3 1 4 7 3 -2 -5 7 13 -27 -23 -3 4 5 15 -79 94 26 -3 -2 5 -45 120 |
770 | cCb -1 -2 -1 1 0 0 -3 -2 12 -6 -3 -15 3 2 2 -8 -42 75 12 -3 -4 -2 -8 82 -3 |
771 | cCr 0 0 -5 7 -2 0 1 3 0 -11 6 -7 -5 -1 -6 -15 -5 48 18 2 -3 3 10 55 2 |
772 | E 17100416 20020000 1 0 1 |
773 | p 3 7 0 11 0 1 128 192 256 128 192 256 |
774 | sY 6 0 37 27 44 40 61 54 63 108 60 255 60 |
775 | sCb 2 0 14 255 14 |
776 | sCr 4 0 11 40 18 94 13 255 13 |
777 | cY 1 -3 0 6 7 2 -1 -5 7 13 -28 -25 -2 3 3 13 -73 91 29 -2 -2 7 -47 119 |
778 | cCb -2 -1 -3 4 0 1 -2 -1 11 -7 -6 -15 1 2 -1 -9 -25 63 16 -3 -4 2 -11 73 -8 |
779 | cCr -1 1 -2 6 0 1 0 2 3 -9 -2 -10 -4 0 -3 -14 -6 50 20 0 -3 3 -1 59 3 |
780 | E 20020000 9223372036854775807 1 0 1 |
781 | p 3 6 0 11 0 1 128 192 256 128 192 256 |
782 | sY 6 0 32 27 37 40 50 54 52 121 49 255 49 |
783 | sCb 4 0 21 40 23 81 17 255 15 |
784 | sCr 2 0 11 255 12 |
785 | cY 1 -3 1 2 5 3 -2 -6 8 6 -12 -18 -2 3 5 7 -42 44 21 -3 -1 4 -29 67 |
786 | cCb -1 0 1 0 -1 0 -1 0 5 -4 -3 -9 1 1 2 -4 -21 39 10 -2 -3 -2 -7 44 1 |
787 | cCr 1 0 -3 2 -3 -1 0 1 -1 -4 5 -2 -1 -1 -5 -6 3 20 10 4 -2 0 9 23 -1"# ; |
788 | let output = parse_grain_table(input); |
789 | assert!(output.is_ok()); |
790 | } |
791 | |