Fix audio sample rate forced to 48 kHz for non-Opus codecs

GetProgressiveAudioFullCommandLine applied the libopus-only sample rate
quantization to every codec except Opus, inverting the intended guard.
A requested rate such as 44100 Hz was therefore snapped to 48000 Hz for
AAC/MP3/FLAC, while Opus (which actually requires the quantization) was
skipped entirely.

Apply the quantization only when the output codec is Opus, and pass the
requested sample rate through unchanged for all other codecs.

Fixes #17026

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
danne
2026-06-13 22:44:44 +02:00
parent 3741d71965
commit e4383493a9
2 changed files with 53 additions and 7 deletions

View File

@@ -7870,13 +7870,14 @@ namespace MediaBrowser.Controller.MediaEncoding
audioTranscodeParams.Add("-ar " + state.BaseRequest.AudioBitRate);
}
if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase))
var sampleRate = state.OutputAudioSampleRate;
if (sampleRate.HasValue)
{
// opus only supports specific sampling rates
var sampleRate = state.OutputAudioSampleRate;
if (sampleRate.HasValue)
var sampleRateValue = sampleRate.Value;
if (string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase))
{
var sampleRateValue = sampleRate.Value switch
// opus only supports specific sampling rates
sampleRateValue = sampleRate.Value switch
{
<= 8000 => 8000,
<= 12000 => 12000,
@@ -7884,9 +7885,9 @@ namespace MediaBrowser.Controller.MediaEncoding
<= 24000 => 24000,
_ => 48000
};
audioTranscodeParams.Add("-ar " + sampleRateValue.ToString(CultureInfo.InvariantCulture));
}
audioTranscodeParams.Add("-ar " + sampleRateValue.ToString(CultureInfo.InvariantCulture));
}
// Copy the movflags from GetProgressiveVideoFullCommandLine

View File

@@ -11,6 +11,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using Moq;
using Xunit;
@@ -203,6 +204,50 @@ public class EncodingHelperTests
}
}
[Theory]
[InlineData("aac", 44100, 44100)] // non-opus: requested rate must be preserved (issue #17026)
[InlineData("aac", 48000, 48000)]
[InlineData("mp3", 22050, 22050)]
[InlineData("flac", 96000, 96000)]
[InlineData("opus", 44100, 48000)] // opus: must snap to a libopus-supported rate
[InlineData("opus", 22050, 24000)]
[InlineData("opus", 8000, 8000)]
public void GetProgressiveAudioFullCommandLine_SampleRate_OnlyClampedForOpus(
string audioCodec,
int requestedSampleRate,
int expectedSampleRate)
{
var state = BuildAudioState(audioCodec, requestedSampleRate);
var args = CreateHelper().GetProgressiveAudioFullCommandLine(state, new EncodingOptions(), "/tmp/out");
Assert.Contains("-ar " + expectedSampleRate, args, StringComparison.Ordinal);
}
private static EncodingJobInfo BuildAudioState(string audioCodec, int requestedSampleRate)
{
var audio = new MediaStream { Index = 0, Type = MediaStreamType.Audio, Codec = "flac", SampleRate = 96000 };
return new EncodingJobInfo(TranscodingJobType.Progressive)
{
MediaSource = new MediaSourceInfo
{
Container = "flac",
MediaStreams = new List<MediaStream> { audio },
Path = "/media/track.flac",
Protocol = MediaProtocol.File,
},
AudioStream = audio,
OutputAudioCodec = audioCodec,
BaseRequest = new VideoRequestDto
{
AudioCodec = audioCodec,
AudioSampleRate = requestedSampleRate,
},
IsVideoRequest = false,
IsInputVideo = false,
};
}
private static EncodingJobInfo BuildState(
MediaStream? subtitle,
SubtitleDeliveryMethod? deliveryMethod,