Merge pull request #17537 from vdatanet/fix/pcm-wav-transcode
Fix PCM audio transcoding to wav returning HTTP 500 and headerless output
This commit is contained in:
@@ -198,11 +198,6 @@ public static class StreamingHelpers
|
||||
state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream, state.OutputAudioChannels) ?? 0;
|
||||
}
|
||||
|
||||
if (outputAudioCodec.StartsWith("pcm_", StringComparison.Ordinal))
|
||||
{
|
||||
containerInternal = ".pcm";
|
||||
}
|
||||
|
||||
if (state.VideoRequest is not null)
|
||||
{
|
||||
state.OutputVideoCodec = state.Request.VideoCodec;
|
||||
|
||||
@@ -7864,10 +7864,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state));
|
||||
}
|
||||
|
||||
if (GetAudioEncoder(state).StartsWith("pcm_", StringComparison.Ordinal))
|
||||
// The pcm_* encoders emit raw samples that carry no header of their own, so the header
|
||||
// has to come from the muxer. Only force the matching raw muxer when the client actually
|
||||
// asked for a raw container (added in #10321 for I2S/MCU clients): applying it to every
|
||||
// pcm_* codec also strips the RIFF header from a `stream.wav` request, which then serves
|
||||
// headerless PCM behind an audio/wav content type.
|
||||
var audioEncoder = GetAudioEncoder(state);
|
||||
if (audioEncoder.StartsWith("pcm_", StringComparison.Ordinal)
|
||||
&& string.Equals(state.OutputContainer, "pcm", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
audioTranscodeParams.Add(string.Concat("-f ", GetAudioEncoder(state).AsSpan(4)));
|
||||
audioTranscodeParams.Add("-ar " + state.BaseRequest.AudioBitRate);
|
||||
audioTranscodeParams.Add(string.Concat("-f ", audioEncoder.AsSpan(4)));
|
||||
}
|
||||
|
||||
var sampleRate = state.OutputAudioSampleRate;
|
||||
|
||||
@@ -223,12 +223,51 @@ public class EncodingHelperTests
|
||||
Assert.Contains("-ar " + expectedSampleRate, args, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static EncodingJobInfo BuildAudioState(string audioCodec, int requestedSampleRate)
|
||||
[Theory]
|
||||
[InlineData("wav")]
|
||||
[InlineData("flac")]
|
||||
[InlineData("mp3")]
|
||||
public void GetProgressiveAudioFullCommandLine_PcmInRealContainer_KeepsContainerMuxer(string outputContainer)
|
||||
{
|
||||
// A pcm_* encoder must not drag the raw muxer into a container that writes its own header,
|
||||
// or the client gets headerless PCM behind the container's content type.
|
||||
var state = BuildAudioState("pcm_s16le", 48000, outputContainer);
|
||||
var args = CreateHelper().GetProgressiveAudioFullCommandLine(state, new EncodingOptions(), "/tmp/out");
|
||||
|
||||
Assert.DoesNotContain("-f s16le", args, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProgressiveAudioFullCommandLine_PcmInPcmContainer_ForcesRawMuxer()
|
||||
{
|
||||
// The raw-PCM route added in #10321 for I2S/MCU clients must keep working.
|
||||
var state = BuildAudioState("pcm_s16le", 48000, "pcm");
|
||||
var args = CreateHelper().GetProgressiveAudioFullCommandLine(state, new EncodingOptions(), "/tmp/out");
|
||||
|
||||
Assert.Contains("-f s16le", args, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetProgressiveAudioFullCommandLine_PcmWithoutBitrate_EmitsNoEmptySampleRate()
|
||||
{
|
||||
// AudioBitRate is optional; it used to be emitted as `-ar <null>`, producing a bare `-ar`
|
||||
// that made ffmpeg abort with "Expected number for ar" and the request fail with HTTP 500.
|
||||
var state = BuildAudioState("pcm_s16le", 48000, "wav");
|
||||
state.BaseRequest.AudioBitRate = null;
|
||||
var args = CreateHelper().GetProgressiveAudioFullCommandLine(state, new EncodingOptions(), "/tmp/out");
|
||||
|
||||
Assert.DoesNotContain("-ar -", args, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("-ar ", args, StringComparison.Ordinal);
|
||||
Assert.Contains("-ar 48000", args, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static EncodingJobInfo BuildAudioState(string audioCodec, int requestedSampleRate, string? outputContainer = null)
|
||||
{
|
||||
var audio = new MediaStream { Index = 0, Type = MediaStreamType.Audio, Codec = "flac", SampleRate = 96000 };
|
||||
|
||||
return new EncodingJobInfo(TranscodingJobType.Progressive)
|
||||
{
|
||||
OutputContainer = outputContainer,
|
||||
MediaSource = new MediaSourceInfo
|
||||
{
|
||||
Container = "flac",
|
||||
|
||||
Reference in New Issue
Block a user