navidrome/core/stream
Deluan 404837799b fix(subsonic): don't re-encode a source already in the player's forced format
When a player has a forced transcoding format, ClientInfo.ForceFormat cleared
DirectPlayProfiles unconditionally. A FLAC source on a player configured to
transcode to FLAC was therefore re-encoded to FLAC, wasting CPU and bandwidth
for no gain. Worse, the transcoder pipes ffmpeg output to stdout, so the
resulting FLAC has total_samples=0 and no seek table -- an offline copy of it
can never be seeked. Reported against getTranscodeDecision by the Symfonium
author.

ForceFormat now rebuilds DirectPlayProfiles from the matching transcoding
profiles instead of dropping them: a client declaring a transcoding profile for
a format is proof it can consume that format, so a source already in it is
served as-is. Container and codec come from resolveTargetFormat, so a legacy
"oga" target_format yields an ogg/opus profile, and the profile's
MaxAudioChannels is carried across.

DirectPlayProfile has no bitrate field, so restoring direct play needs a
ceiling to keep an over-bitrate source out of it. GetTranscodeDecision now
seeds that ceiling from the transcoding row's DefaultBitRate when a format was
successfully forced, with the player's own MaxBitRate still taking precedence.
This also closes a gap where the new endpoint ignored DefaultBitRate entirely:
an mp3 320 source on a player forced to mp3@192 was served at 320, while the
legacy /rest/stream path correctly gave 192.

Applied via CapBitrate, which only ever lowers, so a client declaring a
stricter limit keeps it. The legacy path (applyServerOverride) is untouched --
ForceFormat has no other callers.
2026-09-07 14:56:41 -04:00
..
aliases.go fix: use ADTS for AAC transcoding, temporarily exclude AAC from transcode decisions (#5167) 2026-03-11 09:26:32 -04:00
aliases_test.go fix: use ADTS for AAC transcoding, temporarily exclude AAC from transcode decisions (#5167) 2026-03-11 09:26:32 -04:00
codec.go fix(subsonic): omit bit depth for lossy targets in transcode decision (#5768) 2026-07-12 13:27:01 -04:00
codec_test.go fix(transcoding): clamp target channels to codec limit (#5336) (#5345) 2026-04-11 23:15:07 -04:00
decider.go fix(subsonic): omit bit depth for lossy targets in transcode decision (#5768) 2026-07-12 13:27:01 -04:00
decider_test.go fix(subsonic): don't re-encode a source already in the player's forced format 2026-09-07 14:56:41 -04:00
legacy_client.go fix(transcoding): enforce server-side player MaxBitRate on /rest/stream (#5611) 2026-06-14 16:52:01 -04:00
legacy_client_test.go fix(transcoding): enforce server-side player MaxBitRate on /rest/stream (#5611) 2026-06-14 16:52:01 -04:00
limitations.go refactor: rename core/transcode to core/stream, simplify MediaStreamer (#5166) 2026-03-09 22:22:58 -04:00
limiter.go fix(transcoding): cap concurrent transcodes to prevent ffmpeg DoS (#5522) 2026-05-24 00:24:30 -03:00
limiter_test.go fix(transcoding): cap concurrent transcodes to prevent ffmpeg DoS (#5522) 2026-05-24 00:24:30 -03:00
media_streamer.go fix(stream): abort the response when a transcoded stream is truncated (#6035) 2026-08-25 18:48:43 -04:00
media_streamer_test.go fix(stream): abort the response when a transcoded stream is truncated (#6035) 2026-08-25 18:48:43 -04:00
stream_suite_test.go refactor: rename core/transcode to core/stream, simplify MediaStreamer (#5166) 2026-03-09 22:22:58 -04:00
token.go refactor(stream): remove dead type branches from getIntClaim (#5594) 2026-06-10 23:15:58 -04:00
token_test.go feat(jellyfin): non-expiring, audience-scoped tokens revocable by password change (#6013) 2026-08-22 20:36:24 -04:00
types.go fix(subsonic): don't re-encode a source already in the player's forced format 2026-09-07 14:56:41 -04:00
types_test.go fix(subsonic): don't re-encode a source already in the player's forced format 2026-09-07 14:56:41 -04:00