Skip to content

fix(libSceOpusDec): decode narrowband SILK packets - #5193

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
0xRickx:fix/libsceopusdec-narrowband
Oct 14, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
0xRickx:fix/libsceopusdec-narrowband

Conversation

@0xRickx

@0xRickx 0xRickx commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

sceOpusDecDecode throws on narrowband SILK packets (TOC configurations 0-3, RFC 6716 §3.1), and the title aborts with OpusDec: unexpected FFmpeg PCM format.

The bundled FFmpeg (7.1.1) resamples narrowband SILK from 8 kHz and holds back 24 samples of the first narrowband packet after the decoder starts, or after a packet of another bandwidth (opus/dec.c):

  • A 20 ms packet decodes to 936 samples instead of 960.
  • The next wider packet decodes to 984.

sceOpusDecDecode required exactly the packet's duration. libopus always returns exactly the packet's frame size (opus_decoder.c). The PS5 research's direct-Opus ABI returns the PCM bytes produced for one packet, and its general decoder takes SILK and hybrid packets (DECODING.md).

The decoder now keeps FFmpeg's output in a per-decoder queue and returns exactly the packet's duration:

  • The first narrowband packet begins with 24 samples of silence.
  • The held-back samples come out on the following calls, so nothing is dropped and the output stays continuous, 24 samples (0.5 ms) late.

This is recorded in docs/dev/debt/libSceOpusDec.md.

ProsperoRadio (PPSA99001) decodes its Opus stations this way (radio_service.cpp). With #4796 stacked so that curl can connect, a narrowband station aborts the title on main and plays with this change (#5192).

Tested

Windows 11 (26200), WinLibs GCC 15.2.0 r7, Release, main d071124:

  • guest_opus_dec gains a mono decoder fed three libopus 6 kb/s narrowband 20 ms packets, then a wideband packet (FFmpeg's flush case).

    • Each call must return 1920 bytes and leave the guard samples untouched, and the narrowband audio must be non-zero.
    • The packets come from FFmpeg 8.1's libopus encoder (-application voip -b:a 6k -ac 1 -frame_duration 20).
    • On main the test aborts with OpusDec: unexpected FFmpeg PCM format; with the change it passes, as do the existing checks.
  • Full ctest: 1077/1077 passed (8 skipped on this machine). tools/check_conventions.py --base upstream/main passes.

  • ProsperoRadio (PPSA99001, catalog 01.000.010), relinked, on main + fix(libkernel): report SO_ERROR and the other SOL_SOCKET options like FreeBSD #4796. I used its scripted-test hook with a local catalogue of two stations served from 127.0.0.1 (details in ProsperoRadio (PPSA99001) #5192).

    Station main + fix(libkernel): report SO_ERROR and the other SOL_SOCKET options like FreeBSD #4796 + this change
    64 kb/s fullband Opus plays, 48 kHz stereo plays
    6 kb/s narrowband Opus aborts: OpusDec: unexpected FFmpeg PCM format plays: playback=3 rate=48000 channels=1, peak 0.58
  • No other open pull request touches libSceOpusDec (git merge-tree against all 562).

Rebased onto main ffa2a6e without conflicts (head fb41f10), then tested again on main c496fd5:

  • Windows (same toolchain), with my eight other open PRs merged as well: guest_opus_dec passes. Full ctest: 1088/1088 passed (8 skipped on this machine).
  • Linux (Ubuntu 24.04 in WSL, GCC 13.3, Release, the CI's configure options, lavapipe):
    • this branch alone: guest_opus_dec passes. With only this PR's test file on that main, it aborts with OpusDec: unexpected FFmpeg PCM format.
    • full ctest with my eight other open PRs merged as well: 1067/1070 passed. guest_math, agc_driver_depth_compare_copy and guest_socket_read_write fail the same way on main b868383 without any of them in this setup.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: PPSA99001 ProsperoRadio (PPSA99001) #5192
  • AI-assisted: yes

🤖 Generated with Claude Code

@MarcZX10

Copy link
Copy Markdown
Contributor

Linux validation on the exact PR head: GCC 12.2, Release, BUILD_TESTING=ON; built guest_opus_dec_tests, and CTest guest_opus_dec passed (1/1). This validates the focused packet regression on Linux; it does not add Windows or console coverage.

FFmpeg resamples narrowband SILK (TOC configurations 0-3) from 8 kHz and
holds back 24 samples of the first narrowband packet after the decoder
starts or after a packet of another bandwidth: a 20 ms packet decodes to
936 samples and the next wider packet to 984. sceOpusDecDecode required
exactly the packet's duration and threw, so ProsperoRadio aborted on a
narrowband Opus station.

The decoder now keeps FFmpeg's output in a queue and returns exactly the
packet's duration, as libopus does: the first narrowband packet starts
with 24 samples of silence and the held-back samples follow in the next
calls. The new test decodes three libopus 6 kb/s narrowband packets and
a wideband packet after them; it aborts on main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@0xRickx
0xRickx force-pushed the fix/libsceopusdec-narrowband branch from 9aefec7 to fb41f10 Compare October 13, 2026 14:47
@0xRickx

0xRickx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the Linux run. I've validated it on Linux too, and I'll do the same for my other PRs.

Linux (Ubuntu 24.04 in WSL, GCC 13.3, Release, the CI's configure options, lavapipe):

  • Head fb41f10 (this branch rebased onto main ffa2a6e without conflicts; same diff as 9aefec7) merged onto main c496fd5: guest_opus_dec passes.
  • With only this PR's test file on main c496fd5, guest_opus_dec aborts with OpusDec: unexpected FFmpeg PCM format, as on Windows.
  • Full ctest of main c496fd5 with this PR and my other open PRs merged: 1067/1070 passed. The three failures (guest_math, agc_driver_depth_compare_copy, guest_socket_read_write) fail the same way on main b868383 without any PR in this WSL setup.

Windows is in the Tested section: the test aborts on main and passes with the change, and ProsperoRadio plays its narrowband station. There is no console run. The expected length per call comes from libopus, which returns exactly the packet's duration, and from the PS5 decoding notes linked in the description.

AI-assisted (Claude Code).

@oneandonlydean oneandonlydean self-assigned this Oct 14, 2026
@oneandonlydean
oneandonlydean merged commit ac35f59 into boykopovar:main Oct 14, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants