Fix G.711 codec algorithms, resample edge cases, and conditional __all__#41
Fix G.711 codec algorithms, resample edge cases, and conditional __all__#41
__all__#41Conversation
Co-authored-by: codingjoe <[email protected]>
Co-authored-by: codingjoe <[email protected]>
__all__
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## codev #41 +/- ##
==========================================
+ Coverage 94.10% 94.13% +0.03%
==========================================
Files 24 24
Lines 1713 1723 +10
==========================================
+ Hits 1612 1622 +10
Misses 101 101 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
voip/codecs/pcma.py
Outdated
| Both encode and decode are pure-NumPy and require no PyAV dependency. | ||
| Both encode and decode implement the standard G.711 segmented algorithm | ||
| (ITU-T G.711 Annex A) and interoperate bit-exactly with real RTP PCMA | ||
| streams. No PyAV dependency is required. |
There was a problem hiding this comment.
Pretty verbose, keep docstrings short and to an outside perspective. We don't care about internal implementation in docstrings.
| streams. No PyAV dependency is required. | |
| streams. |
There was a problem hiding this comment.
Done in f8fb750 — shortened to "Both encode and decode interoperate bit-exactly with real RTP PCMA streams."
…tring Co-authored-by: codingjoe <[email protected]>
Fixed in |
There was a problem hiding this comment.
Pull request overview
This PR fixes correctness issues in the pure-NumPy G.711 codecs (PCMU/PCMA), hardens RTPCodec.resample() against edge inputs, and makes voip.codecs.__all__ conditional on PyAV availability to avoid exporting undefined names.
Changes:
- Correct G.711 µ-law/A-law decode/encode behavior (including honoring
input_rate_hzas the resampling source rate). - Make
RTPCodec.resample()handle empty input and extreme downsampling safely. - Fix
voip.codecs.__all__so PyAV-backed names are exported only when import succeeds; add/adjust tests accordingly.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
voip/codecs/pcmu.py |
Updates µ-law decode magnitude formula and uses input_rate_hz as resample source. |
voip/codecs/pcma.py |
Replaces A-law companding approximation with segmented G.711 algorithm; adds vectorized segment selection. |
voip/codecs/base.py |
Makes resample() return empty output for empty input and clamps output length to ≥ 1 for non-empty input. |
voip/codecs/__init__.py |
Makes __all__ include PyAV-backed classes only when imports succeed. |
tests/codecs/test_pcm.py |
Adds golden codeword tests and verifies input_rate_hz affects resampling. |
tests/codecs/test_codecs.py |
Adds __all__/registry tests for “no PyAV” scenario and refactors module-key removal. |
tests/codecs/test_base.py |
Adds resample() edge-case tests (empty input, heavy downsample). |
pyproject.toml |
Adds av to the ai optional dependency group. |
Comments suppressed due to low confidence (1)
tests/codecs/test_codecs.py:101
test_all__excludes_pyav_names_when_av_unavailablemutatessys.modules(including removingvoip.codecsand settingsys.modules["av"]=None) but does not reload/restore the originalvoip.codecsmodule at the end. This can leave thevoippackage’scodecsattribute pointing at the temporaryfreshmodule (or leave theav=Nonesentinel behind ifavwasn’t previously imported), which can cause cross-test contamination. Mirror the cleanup used intest_registry__excludes_pyav_codecs_when_av_unavailable(restoreavproperly and reload the original module).
def test_all__excludes_pyav_names_when_av_unavailable(self):
"""__all__ excludes G722, Opus, and PyAVCodec when av is not importable."""
keys_to_remove = [k for k in sys.modules if k in _PYAV_MODULE_KEYS]
saved = {k: sys.modules.pop(k) for k in keys_to_remove}
sys.modules["av"] = None # causes ImportError on `import av`
try:
import voip.codecs as fresh # noqa: PLC0415
for name in ("G722", "Opus", "PyAVCodec"):
assert name not in fresh.__all__, (
f"{name!r} must not be in __all__ without av"
)
finally:
for k in list(sys.modules):
if k in _PYAV_MODULE_KEYS:
sys.modules.pop(k, None)
sys.modules.update(saved)
You can also share your feedback on Copilot code review. Take the survey.
| keys_to_remove = [k for k in sys.modules if k in _PYAV_MODULE_KEYS] | ||
| saved = {k: sys.modules.pop(k) for k in keys_to_remove} | ||
| sys.modules["av"] = None # causes ImportError on `import av` | ||
|
|
| linear = ((biased - _MU_LAW_BIAS) / 32768.0).astype(np.float32) | ||
| # ITU-T G.711 §A: magnitude = (((mantissa << 3) + BIAS) << exp) - BIAS | ||
| magnitude = (((mantissa << 3) + _MU_LAW_BIAS) << exp) - _MU_LAW_BIAS | ||
| linear = (magnitude.astype(np.float32) / 32768.0).astype(np.float32) |
Addresses several correctness issues in the G.711 codec layer introduced during the codec base-class refactor: wrong µ-law/A-law decode formulas,
input_rate_hzignored as source rate,__all__exporting undefined names without PyAV, andresample()crashing on edge inputs.PCMU (µ-law)
Decode formula: Replaced
(mantissa | 0x10) << (exp + 3)with the canonical ITU-T G.711 expansion:Silence codeword
0x7Fnow decodes to exactly0.0instead of a small non-zero value.PCMA (A-law)
Segmented algorithm: Replaced the continuous companding approximation (
log/exp) with the standard ITU-T G.711 segmented codec (Sun g711.c, 16-bit scale). Codeword0xAAnow decodes to exactly0.984375per spec, enabling bit-exact interop with real RTP streams.Vectorized segment lookup: Segment index determination uses
np.searchsortedinstead of an 8-step Python loop.Both PCMA and PCMU
input_rate_hzas source rate:decode()now usesinput_rate_hz(when provided) as the resampling source rate, correctly handling non-standard G.711 clock rates advertised in SDP (e.g.PCMA/16000).voip/codecs/__init__.pyConditional
__all__:G722,Opus,PyAVCodecare now appended to__all__only when thetryimport succeeds, preventingfrom voip.codecs import *from raisingAttributeErrorin environments without PyAV.RTPCodec.resample()Edge cases: Returns an empty
float32array for empty input; clampsn_outto a minimum of1for non-empty input, avoidingnumpy.interpfailures during aggressive downsampling.Tests
PCMU 0x7F → 0.0,PCMA 0xAA → 0.984375,PCMA 1.0 → 0xAA,PCMA 0.0 → 0xD5input_rate_hz-as-source-rate tests for both codecsresample()empty-input and single-sample heavy-downsample edge cases__all__conditional test (no-PyAV scenario)📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.