Skip to content

Feat/stereo saving - #204

Merged
thomas-vilte merged 1 commit into
pion:mainfrom
thomas-vilte:feat/stereo-saving
Aug 13, 2026
Merged

Feat/stereo saving#204
thomas-vilte merged 1 commit into
pion:mainfrom
thomas-vilte:feat/stereo-saving

Conversation

@thomas-vilte

Copy link
Copy Markdown
Member

Description

The stereo-saving term from compute_vbr was missing. Bands encoded with intensity stereo carry the shape of a single channel, so the frame needs fewer bits as the two channels become more redundant (celt_encoder.c):

coded_stereo_bands = IMIN(intensity, coded_bands);
coded_stereo_dof = (eBands[coded_stereo_bands]<<LM)-coded_stereo_bands;
max_frac = DIV32_16(MULT16_16(QCONST16(0.8f, 15), coded_stereo_dof), coded_bins);
stereo_saving = MIN16(stereo_saving, QCONST16(1.f, 8));
target -= (opus_int32)MIN32(MULT16_32_Q15(max_frac,target),
                SHR32(MULT16_16(stereo_saving-QCONST16(0.1f,8),(coded_stereo_dof<<BITRES)),8));

The value comes from alloc_trim_analysis, which already calculates inter-channel correlation for the lower 8 bands. What was missing was minXC — the weakest correlation across the bands coded with intensity stereo — and the accumulated state:

minXC = sum;
for (i=8;i<intensity;i++)
   minXC = MIN16(minXC, ABS16(...inner_prod(banda i)...));
logXC2 = MAX16(HALF16(logXC), celt_log2(QCONST32(1.001f, 20)-MULT16_16(minXC, minXC)));
*stereo_saving = MIN16(*stereo_saving + QCONST16(0.25f, 8), -HALF16(logXC2));

A single decorrelated band in that range is enough to prevent mid/side from saving bits, regardless of how redundant the lower bands are.

I also reordered the calculations to match the reference: libopus determines the intensity band before the trim and VBR target, since both depend on it (celt_encoder.c:2404). pion was calculating it after both, and in VBR it was therefore derived from the post-VBR frame size instead of the nominal bitrate. That happened to produce the same result in CBR, but not in VBR.

Measurement

Average packet size over music, compared against libopus at the same requested bitrate:

bitrate before after libopus
24000 56.99 (+6.5%) 52.82 (−1.3%) 53.50
48000 118.80 (+4.5%) 114.30 (+0.5%) 113.71
96000 242.56 (−0.6%) 245.84 (+0.7%) 244.03

The important part here is that without this term, pion was spending 4–6% more bits than the reference at the lower bitrates. That means the VBR quality comparisons we were making before were at different actual bitrates and therefore favored pion unfairly.

With the rates matched, round-trip SNR against libopus is:

bitrate before (different rates) after (matched rates)
24000 +0.286 −0.207
48000 −0.055 −0.218
96000 −0.292 −0.138

So the SNR number gets worse at two of the three rates, but the behavior is correct: the encoder is now actually delivering the bitrate the user requested. The −0.14 to −0.22 dB is the real gap we had been hiding with the extra bits.

CBR is unchanged, verified at 24, 48, and 96 kb/s: +0.014 / +0.015 / −0.002 dB, identical to before this change.

What is still missing from VBR

With the actual rates now matched, the remaining gap is around −0.14 to −0.22 dB.

The remaining compute_vbr terms are:

Tonality boost, which requires analysis.c. temporal_vbr, which is portable. Its factor is 0.0000031 * max(0, min(32000, 96000-bitrate)), so it is zero at 96 kb/s and can only affect the lower bitrates.

Reference issue

Part of #34.

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.08%. Comparing base (31598ed) to head (2231abb).

Files with missing lines Patch % Lines
internal/celt/allocation.go 89.47% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #204   +/-   ##
=======================================
  Coverage   93.08%   93.08%           
=======================================
  Files          58       58           
  Lines       10337    10366   +29     
=======================================
+ Hits         9622     9649   +27     
- Misses        504      505    +1     
- Partials      211      212    +1     
Flag Coverage Δ
go 93.08% <95.34%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thomas-vilte
thomas-vilte merged commit e670259 into pion:main Aug 13, 2026
20 checks passed
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