CodecSwitch gives each player its own source-codec policy. For every source
codec you can choose:
- Pass-through — the plugin keeps the original source format when the player supports it and does not let another target profile hijack the selection; otherwise LMS's normal fallback applies.
- Convert to a target codec — FLAC, WAV, AIFF/raw PCM, MP3, AAC, Ogg Vorbis, Opus or ALAC (ALAC is experimental).
The plugin does not modify LMS core files. It registers per-player
Slim::Player::TranscodingHelper profiles at runtime and wraps
getConvertCommand2 only while the plugin is active.
Tested with lmscommunity/lyrionmusicserver:9.0.0 and :9.1.1.
- Lyrion Music Server 9.0+ (tested with 9.0.0 and 9.1.1).
- The LMS image already contains
flac,soxandlame; 9.1.1 also containsopusenc. Those tools cover FLAC, WAV, AIFF/raw PCM, MP3, Ogg Vorbis and Opus without extra packages. - FFmpeg is optional but recommended. It is required for AAC, ALAC, most exotic source formats (WMA/APE/MPC/M4A/ALAC/DSD), remote-stream target conversion, and bitrate-limited Ogg Vorbis.
make distCopy dist/CodecSwitch to the LMS plugin directory and restart LMS. For the
official Docker image the third-party plugin directory is
/config/cache/Plugins:
docker cp dist/CodecSwitch <container>:/config/cache/Plugins/CodecSwitchor mount it:
-v /path/to/dist/CodecSwitch:/config/cache/Plugins/CodecSwitchThen open Settings → Plugins and enable CodecSwitch. The server page is Settings → CodecSwitch; per-player settings are under the player's settings pages.
- Tool paths — optional absolute paths for
ffmpeg,flac,lame,soxandopusenc. Empty means auto-detect from the LMSBindirectory/PATH. - Encoder backends — for FLAC, WAV, AIFF, MP3, Ogg Vorbis and Opus choose
Auto,FFmpegorNative helper.Autoprefers the LMS helper tools and falls back to FFmpeg. AAC and ALAC always use FFmpeg.
Changes are applied immediately; no LMS restart is needed.
The player page lists every known source codec with the available actions. Targets are only offered when the player reports the matching LMS format and the encoder is available. If a player reports no formats, only pass-through is shown.
Default: every source is pass-through.
Pass-through means "keep the original source codec". If the player supports it, CodecSwitch explicitly selects that format so that another plugin's target profile cannot override it. If the player does not support it, LMS's existing fallback rules still apply.
| Target | LMS format | FFmpeg encoder | Native path | Notes |
|---|---|---|---|---|
| FLAC | flc |
flac |
SoX decode → flac |
lossless |
| WAV | wav |
pcm_s16le |
SoX | 16-bit PCM WAV container |
| AIFF | aif |
pcm_s16be |
SoX raw big-endian | LMS aif stream is raw PCM |
| MP3 | mp3 |
libmp3lame |
SoX decode → lame |
lossy, supports B |
| AAC | aac |
aac |
none | ADTS; FFmpeg required; supports B |
| Ogg Vorbis | ogg |
libvorbis |
SoX | lossy; FFmpeg preferred for B |
| Opus | ops |
libopus |
SoX decode → opusenc |
Ogg Opus; supports B |
| ALAC | alc |
alac |
none | experimental, fragmented MP4 |
The target must be in the player's reported formats.
LMS's per-player Maximum Bitrate setting (maxBitrate) is honored for
lossy targets: MP3, AAC, Ogg Vorbis and Opus. The plugin exposes the LMS B
capability on those profiles. Lossless targets do not expose B and are not
limited.
The target codec is still chosen by the CodecSwitch player mapping; the bitrate setting is an upper bound, not a target-codec selector.
LMS's stock audio-settings page only enables the bitrate selector when LAME is available and describes it as MP3-only. To show the codec-agnostic labels and allow the selector when FFmpeg/opusenc is available without LAME, apply the optional core patch:
make lms-patch-check LMS_ROOT=/path/to/lms
make lms-patch LMS_ROOT=/path/to/lmsThe patch is also shipped in dist/CodecSwitch/patches/lms/. It modifies only
Slim/Web/Settings/Player/Audio.pm and
HTML/EN/settings/player/audio.html. LMS upgrades overwrite it; re-apply after
an upgrade. Without the patch, the bitrate limit still works when LAME is
present; only the UI wording/availability differs.
CodecSwitch does not look for a specific plugin name. It detects whether LMS
already has a usable dff/dsf → flc profile registered by any provider.
The SACD plugin is one such provider; another DSD decoder plugin works the
same way. If a DSD source has a target format in CodecSwitch, the pipeline
becomes:
DST/DFF/DSF -> registered DSD provider PCM/FLAC -> CodecSwitch target encoder -> player
Details:
- For lossy targets, CodecSwitch asks the provider for raw PCM (when supported) and pipes it into the target encoder. This avoids an extra FLAC encode/decode.
- For FLAC targets, the provider's FLAC output is the final stream; no second encode is performed.
- For lossless non-FLAC targets (WAV/AIFF/ALAC), CodecSwitch uses PCM from the provider and encodes the target once.
- The player only receives the final target format.
- When a DSD source has a target mapping, the provider's Native/DoP output
mode is bypassed. To keep Native/DoP, set
dff/dsfto Pass-through. - If no provider has registered a DSD profile, CodecSwitch falls back to its own FFmpeg DFF path when FFmpeg is available.
Local files use the full native/FFmpeg toolchain. HTTP(S) streams get target conversion only through FFmpeg; without FFmpeg they keep LMS's normal behavior.
lib/Plugins/CodecSwitch/
├── Plugin.pm # LMS entry point, client events, startup
├── Config.pm # source/target tables and mapping decisions
├── Capabilities.pm # provider priorities and external-profile discovery
├── Orchestrator.pm # DSD PCM/FLAC path selection and bitrate helper
├── Toolchain.pm # tool discovery, probing, backend selection
├── CommandBuilder.pm # FFmpeg/native command templates
├── Profiles.pm # per-player runtime commandTable profiles
├── Hooks.pm # getConvertCommand2 wrapper
├── Settings.pm # server settings page
└── PlayerSettings.pm # per-player mapping page
Provider priority (smaller wins):
unknown / legacy profiles 0 (keeps original systems working)
DSD provider (e.g. SACD) 50
FFmpeg plugin 100
CodecSwitch native 200
Existing non-CodecSwitch profiles win over CodecSwitch rules. CodecSwitch only fills gaps and handles DSD composition.
Profiles.pm registers local profiles with an F capability and remote
profiles with an R capability (the remote profile uses the -1 duplicate
instance suffix understood by LMS).
The plugin registers its own log category, so it appears in the standard LMS logging page:
Settings → Advanced → Logging → Advanced logging settings
Use that page to set plugin.codecswitch, plugin.sacdplayer,
plugin.ffmpeg and the core player.source category. Use DEBUG only while
diagnosing; it can create large log files. CodecSwitch does not add a separate
logging UI to its own settings page.
make test # unit tests, command generation, real FFmpeg pipeline
make dist # installable dist/CodecSwitch
make verify-dist # verify rendered manifest and modules
make bundle # zip + tar.gz release archives
make smoke # Docker 9.0.0 + 9.1.1 load/settings smoke testThe Docker smoke test only verifies that the server loads the plugin and serves the settings page; it does not create a player or test player-side playback.
VERSION is the single source of truth. install.xml.in and repo.xml.in
contain @VERSION@ / @RELEASE_URL@ placeholders; make dist renders them
into build/.
- ALAC output is experimental; it uses fragmented MP4 for streaming.
- AIFF target is raw big-endian PCM with LMS stream format
aif, not an AIFF container. - AAC/ALAC and exotic source codecs need FFmpeg.
- Pass-through is not a guarantee of bit-perfect output when the player does not support the source codec.
- The optional LMS core patch is outside the plugin and must be re-applied after LMS upgrades.