-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parse scaling lists properly. #90
Parse scaling lists properly. #90
Conversation
Bencher Report
Click to view all benchmark results
|
706891f
to
ccfc26e
Compare
@dholroyd I really don't want to be annoying, but any chance of looking at this at some point? It's ok if the answer is no, I'd just like to know if it makes sense to wait for this. Once again, I really don't want to be annoying or bother you, sorry. |
ccfc26e
to
54d98a7
Compare
src/nal/sps.rs
Outdated
pub enum ScalingList<const S: usize> { | ||
NotPresent, | ||
UseDefault, | ||
List(Box<[i32; S]>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the possible bounds of scalingList[ j ]
are [1, 256)
; is that right? If so, perhaps a NonZeroU8
would be appropriate rather than an i32
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've had a look at "Scaling list syntax" in the spec, and agree it seems to make sense to use NonZeroU8
here 👍
src/nal/pps.rs
Outdated
@@ -142,7 +142,8 @@ impl SliceGroup { | |||
|
|||
#[derive(Debug, Clone)] | |||
pub struct PicScalingMatrix { | |||
// TODO | |||
pub scaling_list4x4: Vec<ScalingList<16>>, | |||
pub scaling_list8x8: Vec<ScalingList<64>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it'd be more clear to make this Option<Vec<ScalingList<64>>
based on the transform_8x8_mode_flag
, as opposed to representing this by scaling_list8x8.empty()
? I could go either way; probably should have a comment in the latter case though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some thinking, representing it as Option<...>
makes more sense, it seems more intuitive if someone wants to extract the flag.
let mut scaling_list = vec![]; | ||
|
||
impl<const S: usize> ScalingList<S> { | ||
pub fn read<R: BitRead>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding this comment in your PR description:
I hope the API with const-sized arrays is ok, it seemed whoever implemented skipping the lists wanted to use Vecs initially. I found the generic const more appealing, but can change back to Vecs if needed.
I think the only concern I have about the const-size array approach is doubling the code size of this read
method, in combination with the existing multiplicative factor of the BitRead
monomorphization. What would you say about avoiding that via delegation, something like the following:
/// Returns `use_default_scaling_matrix_flag`
fn fill_scaling_list<R: BitRead>(r: &mut R, scaling_list: &mut [NonZeroU8]) -> Result<bool, ScalingMatrixError> {
// ...
}
impl<const S: usize> ScalingList<S> {
pub fn read<R: BitRead>(r: &mut R, present: bool) -> Result<ScalingList<S>, ScalingMatrixError> {
if !present { /* ... */ }
let mut scaling_list = Box::new([0; S]);
let use_default_scaling_matrix_flag = fill_scaling_list(r, &mut scaling_list[..])?;
if use_default_scaling_matrix_flag {
Ok(ScalingList::UseDefault)
} else {
Ok(ScalingList::List(scaling_list))
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's a very nice catch. Super cool you noticed that.
ScalingList::NotPresent, | ||
ScalingList::NotPresent, | ||
] | ||
}), | ||
..ChromaInfo::default() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be nice to have a test case that exercises the new code paths more fully.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modified the test case a bit, are there things I forgot to include there that you wish were covered?
pub struct ScalingList { | ||
// TODO | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum ScalingList<const S: usize> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had another thought about representation, and @dholroyd I'd love to get your take also.
Is it important to convey exactly how the file specified the scaling matrices, or are the callers always going to want to apply the relevant rules to come up with a matrix? The rules appear to be something like the following:
seq_scaling_matrix_present_flag == 0
=> useFlat_...
seq_scaling_list_present_flag[ i ] == 0
=> use fall-back rule set Apic_scaling_matrix_present_flag == 0
=> match the spspic_scaling_list_present_flag[ i ] == 0
=> use fall-back rule set BUseDefaultScalingMatrix...Flag[ i ] == 0
=> useDefault_..._Intra
If the latter, I think it's better to just represent a dense matrix of integers, rather than a vec of enums to these defaults or a box of integers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always been my ambition to use this code to produce a diagnostic dump of the bitstream content, so I lean toward the former, bitstream-oriented representation. I think there is also a place for utility functions to produce the latter representation on demand, so that callers don't all duplicate the logic you've outlined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks; that makes the existing structure clearly the right way to go.
@jerzywilczek thanks for this, bardzo ładne! Also, sorry for being slow getting to the PR. |
41010bb
to
f988174
Compare
Bencher Report
Click to view all benchmark results
|
f988174
to
0e25ac8
Compare
@dholroyd No problem! Thank you for the library. |
I am happy with the state of this - any further comments @scottlamb ? |
One more thing - can we have a new release after merging this? |
src/nal/sps.rs
Outdated
} else { | ||
next_scale | ||
}; | ||
// unwrap is ok here, because: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with the argument in this comment, but I think it's possible to avoid the .unwrap()
altogether:
- keep
last_scale
as aNonZeroU8
, starting fromconst { NonZeroU8::new(8).unwrap() }
(msrp 1.83, the alternative ofmatch { ..., None => panic!() }
works a few versions before) - update via
new_value = NonZeroU8::new(next_scale as u8).unwrap_or(last_value)
0e25ac8
to
e964a4a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good
Scaling lists were skipped previously, I implemented proper parsing, which was mostly coming up with the types to store them in.
I hope the API with const-sized arrays is ok, it seemed whoever implemented skipping the lists wanted to use
Vec
s initially. I found the generic const more appealing, but can change back toVec
s if needed.