Skip to content

Commit f37c13c

Browse files
xiphmontbarrbrain
authored andcommitted
Fix for #2212; Crash when using 4 tiles for 1080p 4:2:2 input
When doing loop filter RDO inline with the rest of the tile coding, LRUs must align to tile boundaries. An unexpected corner case means that chroma LRUs must have an even superblock width in 4:2:2 video, as LRUs must always be square. As a result, that means tiles must also have an even superblock width. As tile width must be adjusted in this case, it also means we can't use the spec's 'tile uniform spacing' mode, which would produce odd superblock width tiles in, eg, 1080p 4:2:2 video. This patch also implements explicit per-tile sizing the the frame OBU header.
1 parent 8e56324 commit f37c13c

File tree

3 files changed

+101
-17
lines changed

3 files changed

+101
-17
lines changed

src/encoder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ impl<T: Pixel> FrameInvariants<T> {
609609
frame_rate,
610610
TilingInfo::tile_log2(1, config.tile_cols).unwrap(),
611611
TilingInfo::tile_log2(1, config.tile_rows).unwrap(),
612+
sequence.chroma_sampling == ChromaSampling::Cs422,
612613
);
613614

614615
if config.tiles > 0 {
@@ -624,6 +625,7 @@ impl<T: Pixel> FrameInvariants<T> {
624625
frame_rate,
625626
tile_cols_log2,
626627
tile_rows_log2,
628+
sequence.chroma_sampling == ChromaSampling::Cs422,
627629
);
628630

629631
if tiling.rows * tiling.cols >= config.tiles {
@@ -2626,7 +2628,6 @@ fn encode_partition_topdown<T: Pixel, W: Writer>(
26262628
let w: &mut W = if cw.bc.cdef_coded { w_post_cdef } else { w_pre_cdef };
26272629
cw.write_partition(w, tile_bo, partition, bsize);
26282630
}
2629-
26302631
match partition {
26312632
PartitionType::PARTITION_NONE => {
26322633
let part_decision = if !rdo_output.part_modes.is_empty() {

src/header.rs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use crate::context::*;
1212
use crate::ec::*;
1313
use crate::lrf::*;
1414
use crate::partition::*;
15+
use crate::tiling::MAX_TILE_WIDTH;
16+
use crate::util::Fixed;
1517
use crate::util::Pixel;
1618

1719
use crate::DeblockState;
@@ -670,25 +672,70 @@ impl<W: io::Write> UncompressedHeader for BitWriter<W, BigEndian> {
670672
self.write_bit(fi.disable_frame_end_update_cdf)?;
671673
}
672674

673-
// tile <https://aomediacodec.github.io/av1-spec/#tile-info-syntax>
674-
self.write_bit(true)?; // uniform_tile_spacing_flag
675+
// tile
676+
// <https://aomediacodec.github.io/av1-spec/#tile-info-syntax>
675677

678+
// Can we use the uniform spacing tile syntax? 'Uniform spacing'
679+
// is a slight misnomer; it's more constrained than just a uniform
680+
// spacing.
676681
let ti = &fi.tiling;
677682

678-
let cols_ones = ti.tile_cols_log2 - ti.min_tile_cols_log2;
679-
for _ in 0..cols_ones {
680-
self.write_bit(true);
681-
}
682-
if ti.tile_cols_log2 < ti.max_tile_cols_log2 {
683-
self.write_bit(false);
684-
}
683+
if fi.sb_width.align_power_of_two_and_shift(ti.tile_cols_log2)
684+
== ti.tile_width_sb
685+
&& fi.sb_height.align_power_of_two_and_shift(ti.tile_rows_log2)
686+
== ti.tile_height_sb
687+
{
688+
// yes; our actual tile width/height setting (which is always
689+
// currently uniform) also matches the constrained width/height
690+
// calculation implicit in the uniform spacing flag.
685691

686-
let rows_ones = ti.tile_rows_log2 - ti.min_tile_rows_log2;
687-
for _ in 0..rows_ones {
688-
self.write_bit(true);
689-
}
690-
if ti.tile_rows_log2 < ti.max_tile_rows_log2 {
691-
self.write_bit(false);
692+
self.write_bit(true)?; // uniform_tile_spacing_flag
693+
694+
let cols_ones = ti.tile_cols_log2 - ti.min_tile_cols_log2;
695+
for _ in 0..cols_ones {
696+
self.write_bit(true);
697+
}
698+
if ti.tile_cols_log2 < ti.max_tile_cols_log2 {
699+
self.write_bit(false);
700+
}
701+
702+
let rows_ones = ti.tile_rows_log2 - ti.min_tile_rows_log2;
703+
for _ in 0..rows_ones {
704+
self.write_bit(true);
705+
}
706+
if ti.tile_rows_log2 < ti.max_tile_rows_log2 {
707+
self.write_bit(false);
708+
}
709+
} else {
710+
self.write_bit(false)?; // uniform_tile_spacing_flag
711+
let mut sofar = 0;
712+
let mut widest_tile_sb = 0;
713+
for _ in 0..ti.cols {
714+
let max = (MAX_TILE_WIDTH
715+
>> if fi.sequence.use_128x128_superblock { 7 } else { 6 })
716+
.min(fi.sb_width - sofar) as u16;
717+
let this_sb_width = ti.tile_width_sb.min(fi.sb_width - sofar);
718+
self.write_quniform(max, (this_sb_width - 1) as u16);
719+
sofar += this_sb_width;
720+
widest_tile_sb = widest_tile_sb.max(this_sb_width);
721+
}
722+
723+
let max_tile_area_sb = if ti.min_tiles_log2 > 0 {
724+
(fi.sb_height * fi.sb_width) >> (ti.min_tiles_log2 + 1)
725+
} else {
726+
fi.sb_height * fi.sb_width
727+
};
728+
729+
let max_tile_height_sb = (max_tile_area_sb / widest_tile_sb).max(1);
730+
731+
sofar = 0;
732+
for i in 0..ti.rows {
733+
let max = max_tile_height_sb.min(fi.sb_height - sofar) as u16;
734+
let this_sb_height = ti.tile_height_sb.min(fi.sb_height - sofar);
735+
736+
self.write_quniform(max, (this_sb_height - 1) as u16);
737+
sofar += this_sb_height;
738+
}
692739
}
693740

694741
let tiles_log2 = ti.tile_cols_log2 + ti.tile_rows_log2;

src/tiling/tiler.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,14 @@ pub struct TilingInfo {
4444
pub min_tile_rows_log2: usize,
4545
pub max_tile_rows_log2: usize,
4646
pub sb_size_log2: usize,
47+
pub min_tiles_log2: usize,
4748
}
4849

4950
impl TilingInfo {
5051
pub fn from_target_tiles(
5152
sb_size_log2: usize, frame_width: usize, frame_height: usize,
5253
frame_rate: f64, tile_cols_log2: usize, tile_rows_log2: usize,
54+
is_422_p: bool,
5355
) -> Self {
5456
// <https://aomediacodec.github.io/av1-spec/#tile-info-syntax>
5557

@@ -87,7 +89,26 @@ impl TilingInfo {
8789

8890
let tile_cols_log2 =
8991
tile_cols_log2.max(min_tile_cols_log2).min(max_tile_cols_log2);
90-
let tile_width_sb = sb_cols.align_power_of_two_and_shift(tile_cols_log2);
92+
let tile_width_sb_pre =
93+
sb_cols.align_power_of_two_and_shift(tile_cols_log2);
94+
95+
// If this is 4:2:2, our UV horizontal is subsampled but not our
96+
// vertical. Loop Restoration Units must be square, so they
97+
// will always have an even number of horizontal superblocks. For
98+
// tiles and LRUs to align, tile_width_sb must be even in 4:2:2
99+
// video.
100+
101+
// This is only relevant when doing loop restoration RDO inline
102+
// with block/superblock encoding, that is, where tiles are
103+
// relevant. If (when) we introduce optionally delaying loop-filter
104+
// encode to after the partitioning loop, we won't need to make
105+
// any 4:2:2 adjustment.
106+
107+
let tile_width_sb = if is_422_p {
108+
(tile_width_sb_pre + 1) >> 1 << 1
109+
} else {
110+
tile_width_sb_pre
111+
};
91112

92113
let min_tile_rows_log2 = if min_tiles_log2 > tile_cols_log2 {
93114
min_tiles_log2 - tile_cols_log2
@@ -123,6 +144,7 @@ impl TilingInfo {
123144
min_tile_rows_log2,
124145
max_tile_rows_log2,
125146
sb_size_log2,
147+
min_tiles_log2,
126148
}
127149
}
128150

@@ -240,6 +262,7 @@ pub mod test {
240262
frame_rate,
241263
0,
242264
0,
265+
false,
243266
);
244267
assert_eq!(1, ti.cols);
245268
assert_eq!(1, ti.rows);
@@ -253,6 +276,7 @@ pub mod test {
253276
frame_rate,
254277
1,
255278
1,
279+
false,
256280
);
257281
assert_eq!(2, ti.cols);
258282
assert_eq!(2, ti.rows);
@@ -266,6 +290,7 @@ pub mod test {
266290
frame_rate,
267291
2,
268292
2,
293+
false,
269294
);
270295
assert_eq!(3, ti.cols);
271296
assert_eq!(3, ti.rows);
@@ -280,6 +305,7 @@ pub mod test {
280305
frame_rate,
281306
10,
282307
8,
308+
false,
283309
);
284310
assert_eq!(3, ti.cols);
285311
assert_eq!(3, ti.rows);
@@ -293,6 +319,7 @@ pub mod test {
293319
frame_rate,
294320
0,
295321
0,
322+
false,
296323
);
297324
assert_eq!(1, ti.cols);
298325
assert_eq!(1, ti.rows);
@@ -336,6 +363,7 @@ pub mod test {
336363
frame_rate,
337364
1,
338365
1,
366+
false,
339367
);
340368
let mut iter = ti.tile_iter_mut(&mut fs, &mut fb);
341369
assert_eq!(4, iter.len());
@@ -359,6 +387,7 @@ pub mod test {
359387
frame_rate,
360388
2,
361389
2,
390+
false,
362391
);
363392
let mut iter = ti.tile_iter_mut(&mut fs, &mut fb);
364393
assert_eq!(9, iter.len());
@@ -406,6 +435,7 @@ pub mod test {
406435
fi.config.frame_rate(),
407436
2,
408437
2,
438+
false,
409439
);
410440
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
411441
let tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
@@ -484,6 +514,7 @@ pub mod test {
484514
fi.config.frame_rate(),
485515
2,
486516
2,
517+
false,
487518
);
488519
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
489520
let tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>();
@@ -524,6 +555,7 @@ pub mod test {
524555
fi.config.frame_rate(),
525556
2,
526557
2,
558+
false,
527559
);
528560
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
529561
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
@@ -588,6 +620,7 @@ pub mod test {
588620
fi.config.frame_rate(),
589621
2,
590622
2,
623+
false,
591624
);
592625
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
593626
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
@@ -628,6 +661,7 @@ pub mod test {
628661
fi.config.frame_rate(),
629662
1,
630663
1,
664+
false,
631665
);
632666
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
633667
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
@@ -690,6 +724,7 @@ pub mod test {
690724
fi.config.frame_rate(),
691725
2,
692726
2,
727+
false,
693728
);
694729
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
695730
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
@@ -734,6 +769,7 @@ pub mod test {
734769
fi.config.frame_rate(),
735770
2,
736771
2,
772+
false,
737773
);
738774
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
739775
let mut tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>();

0 commit comments

Comments
 (0)