Well, since I had no incentive to work on NihAV and recently the weather is not very encouraging for any kind of intellectual activity there was almost no progress. And yet now I have something to write about: NihAV has finally managed to decode non-trivial (i.e not fully black) RealVideo 3 I-frame properly (i.e. without any visible distortions). Loop filter is still missing but it’s a start. And it’s not a small feat considering one has to implement both coefficients decoding and intra prediction. So essentially it’s just motion vector juggling and motion compensation are all the things that are missing for P- and B-frames support. Maybe it will go faster from here (but most likely not).
And since doing that involved rewriting some C code into Rust here are some notes on how oxidising went:
match
is a nice replacement for the cases when you have to partly remap values—in my case I had to adjust intra prediction directions in case top or left or bottom reference were missing and that means changing three or four values into other values,match
looks more compact than several} else if (itype == FOO) {
and does not lose readability either;- while in C
foo = bar = 42;
is a common thing, Rust does not allow this (I can understand why) and I’m surprised I ran into it only now (with intra prediction functions that assign the same calculated value to several output pixels at once); - loops in Rust are fine for basic use but when you need to deal with something more complex like
for (i = 0; i < block_size; i += 4)
orfor (i = 99; i > 0; i--)
you need either to write a simpler loop and remap indices inside or to remember it's Rust and permute range in less intuitive ways likefor i in (0..block_size).filter(|x| x&3 == 0)
andfor i in (1..99+1).rev()
. While this works and even somewhat conveys the meaning it's a bit unwieldy IMO; - and it might be a bit too esoteric but looks like I cannot easily write
fn clip_u8
that would take any primitive numeric type as input, do comparisons inside and return value either clipped to converted to(val: N) -> u8 u8
. The best answer on how to do it I found was "you can't, it's against Rust practices". I don't need it much and I care even less, so I'll just mark it as a neutral language feature and forget about it.
And now the small but constantly irritating thing: arrays. While slices are nice and easy to use (including extracting sub-slices), in my area I often need a slice with arbitrary start and end bounds. To clarify my use case: quite often you need a piece of memory that's addressable with both positive and negative indices and those make sense on certain interval.
One of such common arrays is clipping array which essentially takes input index and returns it clipped usually to 0-255 range. So you have part [-255..-1] filled with zeroes, [0..255] filled with values in the same range and [256..511] filled with 255. I repeat, such clipping table is very common and useful thing that's currently not easy to implement in Rust.
Another less common case is the block of pixels we process may require information from its top, left and top-left neighbours—and those are addressed as src[-stride + i]
, src[-1 + stride*i]
and src[-stride - 1]
. Or a whole frame of GDI-related codec (no, not from Westwood) or even simple BMP/DIB that stores lines upside-down so after you process line 0 you have to move to line -1.
I currently deal with it by keeping an additional variable pointing to the current position in array that I use as a reference and from which I can subtract other numbers if needed, but it's a bit clunky and error-prone. Since Rust checks indices on slice access I wonder if extending it to work with e.g. negative indices is possible. IIRC FORTRAN and Pascal allowed you to define an array starting with arbitrary index, it might be possible in Rust too.
Oh well, I'll just keep using my approach meanwhile and waiting to see what rust-av
does in this regard.