Skip to content
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

aac: refactor approximate frame count, fuzzy fixes #343

Merged
merged 2 commits into from
Feb 20, 2025

Conversation

sscobici
Copy link

refactored aac aproximate_frame_count() method. It had some issues which causes panic. Should fix cases from https://github.com/qarmin/Automated-Fuzzer/actions/runs/12516290494 (REPORTS___SYMPHONIA*)

  1. renamed total_len to remaining_len because this is not the length of the whole file, but it was considered such in some places.
  2. first sample point to start from the beginning, file can contain a single frame
  3. check step to be bigger than 0 to avoid panic
  4. skip the rest of the frame after the header is read to avoid looking for sync word in the audio data
  5. break if reading 100 frames overlap next sample point

I haven't tested on VBR aac to observe any improvements, It was #196 (comment) approximate method is not perfect there

Comment on lines 454 to 459
if source.seek(SeekFrom::Start(cur_pos)).is_err() {
break;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch for an optimization! However, I think ignore_bytes will work even better since the MSS will decide if it should just consume from it's internal ring buffer or resort to an actual seek. In other words, you can just ignore_bytes(header.frame_len).

@@ -428,34 +428,48 @@ fn approximate_frame_count(mut source: &mut MediaSourceStream<'_>) -> Result<Opt
else {
// The number of points to sample within the stream.
const NUM_SAMPLE_POINTS: u64 = 4;
const NUM_FRAMES: u32 = 100;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A slightly more descriptive name.

Suggested change
const NUM_FRAMES: u32 = 100;
const NUM_FRAMES_PER_SAMPLE: u32 = 100;

@sscobici
Copy link
Author

Thanks for suggestion.
ignore_bytes should ignore only the frame.payload because sync word and header were already read.
added another exit point in a loop to break if searching within the sample point overflow the next sample point. Can happen when no sync word found in a very broken file.

Comment on lines 465 to 469
// Reading frames from the next sample point could lead to reprocessing already calculated frames.
// Also, if no sync word was found, better to avoid redundant searches.
if next_frame_pos > new_pos + step {
break;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Remove this since it wouldn't be required anymore.

Comment on lines 449 to 454
next_frame_pos = source.pos() + header.payload_len() as u64;

for _ in 0..=100 {
let header = match AdtsHeader::read(&mut source) {
Ok(header) => header,
_ => break,
};
// if reading NUM_FRAMES_PER_SAMPLE_POINT frames overflow the next sample point position then break
if next_frame_pos > new_pos + step {
break;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Remove this since it wouldn't be required anymore.

// file can be small enough and not have enough NUM_FRAMES_PER_SAMPLE_POINT, but we can still read at least one frame
if step > 0 {
for new_pos in (original_pos..(original_pos + remaining_len)).step_by(step as usize) {
if source.seek(SeekFrom::Start(new_pos)).is_err() {
Copy link
Owner

@pdeljanov pdeljanov Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add a test here before the seek to make sure we never go backwards or continue from the current position.

// Skip the current sample point if a previous sample read past it.
if new_pos <= source.pos() {
    continue;
}

@pdeljanov
Copy link
Owner

Thanks for suggestion. ignore_bytes should ignore only the frame.payload because sync word and header were already read. added another exit point in a loop to break if searching within the sample point overflow the next sample point. Can happen when no sync word found in a very broken file.

I added some additional suggestions that could help clean-up the implementation after these changes. Let me know what you think.

@pdeljanov pdeljanov merged commit c7efbcb into pdeljanov:dev-0.6 Feb 20, 2025
11 checks passed
@pdeljanov
Copy link
Owner

pdeljanov commented Feb 20, 2025

Thanks, merged!

@sscobici sscobici deleted the aac-fix branch February 20, 2025 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants