fix: restore native FLV reconnect with curl fallback

This commit is contained in:
2026-08-12 12:14:43 +08:00
parent e6068d93e0
commit e0bd001541
5 changed files with 233 additions and 20 deletions
@@ -114,12 +114,100 @@ public sealed class FfmpegFailureClassificationTests
}
[Theory]
[InlineData("https://example.test/live.flv", "flv", true)]
[InlineData("https://example.test/live.m3u8", "hls", false)]
[InlineData("https://example.test/playlist", "hls", false)]
public void HttpInput_UsesCurlExceptForHls(string url, string protocol, bool expected)
[InlineData("https://example.test/live.flv", "flv", false, false)]
[InlineData("https://example.test/live.flv", "flv", true, true)]
[InlineData("https://example.test/live.m3u8", "hls", true, false)]
[InlineData("https://example.test/playlist", "hls", true, false)]
public void CurlPipe_IsOnlyUsedForExplicitFlvFallback(
string url,
string protocol,
bool useCurlFallback,
bool expected)
{
Assert.Equal(expected, FfmpegService.ShouldUseCurlPipe(url, protocol));
Assert.Equal(expected, FfmpegService.ShouldUseCurlPipe(url, protocol, useCurlFallback));
}
[Fact]
public void NativeFlvInput_UsesFfmpegReconnectByDefault()
{
var arguments = FfmpegService.BuildArgumentList(
"https://cdn.example.test/live.flv",
"/tmp/output.ts",
RecordOutputFormat.Ts,
RecordSaveMode.SingleFile,
1,
RecordingTemplateType.StreamCopy,
true,
10,
30_000_000,
30,
null,
"flv",
"h264",
FfmpegService.FfmpegInputOptionProfile.Baseline);
Assert.Contains("https://cdn.example.test/live.flv", arguments);
Assert.DoesNotContain("pipe:0", arguments);
Assert.Contains("-reconnect", arguments);
Assert.Contains("-reconnect_streamed", arguments);
Assert.Contains("-reconnect_at_eof", arguments);
}
[Theory]
[InlineData("flv", true, false, true)]
[InlineData("flv", true, true, false)]
[InlineData("flv", false, false, false)]
[InlineData("hls", true, false, false)]
public void OverlongHeaders_UsesCurlFallbackAtMostOnce(
string protocol,
bool hasOverlongHeadersFailure,
bool hasTriedCurlFallback,
bool expected)
{
Assert.Equal(
expected,
FfmpegService.ShouldFallbackToCurl(protocol, hasOverlongHeadersFailure, hasTriedCurlFallback));
}
[Fact]
public void CurlFallback_UsesPipeWithFiniteRetriesAndPreservesHeaders()
{
var headers = new StreamInputHeaders(
"RecorderTest/1.0",
"https://live.example.test/room",
"session=abc",
new Dictionary<string, string> { ["X-Test"] = "yes" });
var ffmpegArguments = FfmpegService.BuildArgumentList(
"https://cdn.example.test/live.flv",
"/tmp/output.ts",
RecordOutputFormat.Ts,
RecordSaveMode.SingleFile,
1,
RecordingTemplateType.StreamCopy,
true,
10,
30_000_000,
30,
headers,
"flv",
"h264",
FfmpegService.FfmpegInputOptionProfile.Baseline,
useCurlFallback: true);
var curlArguments = FfmpegService.BuildCurlArgumentList(
"https://cdn.example.test/live.flv",
headers);
Assert.Contains("pipe:0", ffmpegArguments);
Assert.DoesNotContain("-reconnect", ffmpegArguments);
Assert.Contains("--show-error", curlArguments);
Assert.Contains("--fail", curlArguments);
Assert.Contains("--retry", curlArguments);
Assert.Contains("--retry-all-errors", curlArguments);
Assert.Contains("--retry-max-time", curlArguments);
Assert.Contains("RecorderTest/1.0", curlArguments);
Assert.Contains("https://live.example.test/room", curlArguments);
Assert.Contains("session=abc", curlArguments);
Assert.Contains("X-Test: yes", curlArguments);
}
[Fact]
@@ -145,7 +233,8 @@ public sealed class FfmpegFailureClassificationTests
headers,
"hls",
"h264",
FfmpegService.FfmpegInputOptionProfile.Baseline);
FfmpegService.FfmpegInputOptionProfile.Baseline,
useCurlFallback: true);
Assert.Contains("https://cdn.example.test/live/index.m3u8", arguments);
Assert.DoesNotContain("pipe:0", arguments);
@@ -182,6 +271,27 @@ public sealed class FfmpegFailureClassificationTests
Assert.Contains("-reset_timestamps", arguments);
}
[Fact]
public void RecoveryContext_LeavesCurlAfterExitWithoutResettingOneShotBudget()
{
var curlContext = FfmpegService.InitialRecoveryContext with
{
UseCurlFallback = true,
HasTriedCurlFallback = true
};
var nextContext = FfmpegService.AdvanceRecoveryContext(
curlContext,
curlContext.InputOptionProfile,
"flv",
"flv",
refreshedStream: true);
Assert.False(nextContext.UseCurlFallback);
Assert.True(nextContext.HasTriedCurlFallback);
Assert.True(nextContext.HasRetriedWithRefreshedStream);
Assert.False(FfmpegService.ShouldFallbackToCurl("flv", true, nextContext.HasTriedCurlFallback));
}
[Fact]
public void RecoveryContext_PreservesBudgetAcrossHlsFallbackAndReachesTimestampTranscode()
{