Loseless split is back. Very fast now. Using key frames.

This commit is contained in:
Alexander Shabarshov 2026-08-30 10:16:33 +01:00
parent 77bae6e392
commit 092bc88d58
2 changed files with 66 additions and 3 deletions

View File

@ -79,8 +79,8 @@ public class JobProcessor(ILogger logger) : LoggingBase(logger, 0), IJobProcesso
}
else
{
//processorFactory = i => new PassthroughSplitter(i, _logger);
processorFactory = i => new ReencodeSplitter(i, _logger);
processorFactory = i => new PassthroughSplitter(i, _logger);
//processorFactory = i => new ReencodeSplitter(i, _logger);
}
var segmentsToUse = predefinedSegments;

View File

@ -82,8 +82,15 @@ public sealed class PassthroughSplitter : LoggingBase, ISegmentProcessor
var start = job.SegmentStart;
var length = job.SegmentLength;
// Probe next keyframe packet within a 5-second window
var nextKey = ProbeNextKeyframePacket(inputFile, start);
// Adjust by subtracting X.XX seconds
const double shiftBack = 0.1;
var seekPos = nextKey > shiftBack ? nextKey - shiftBack : 0.0;
var args =
$"-ss {start.ToString(CultureInfo.InvariantCulture)} " +
$"-ss {seekPos.ToString(CultureInfo.InvariantCulture)} " +
$"-i \"{inputFile}\" " +
$"-t {length.ToString(CultureInfo.InvariantCulture)} " +
$"-c copy {string.Join(" ", job.Job.Passthrough)} " +
@ -100,4 +107,60 @@ public sealed class PassthroughSplitter : LoggingBase, ISegmentProcessor
return Process.Start(psi) ?? throw new Exception("Failed to start ffmpeg passthrough.");
}
public static double ProbeNextKeyframePacket(string inputFile, double segmentStart, double window = 11.0)
{
var psi = new ProcessStartInfo
{
FileName = "ffprobe",
Arguments =
"-select_streams v " +
$"-read_intervals \"{(segmentStart-0.1).ToString(CultureInfo.InvariantCulture)}%+{window.ToString(CultureInfo.InvariantCulture)}\" " +
"-skip_frame nokey " +
"-show_packets " +
"-show_entries packet=pts_time,flags " +
"-of compact=p=0 " +
"-v quiet " +
$"\"{inputFile}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var p = Process.Start(psi);
var output = p!.StandardOutput.ReadToEnd();
p.WaitForExit();
double best = double.MaxValue;
foreach (var line in output.Split('\n'))
{
if (string.IsNullOrWhiteSpace(line))
continue;
// Example line:
// pts_time=23.520000|flags=K__
var parts = line.Split('|');
if (parts.Length < 2)
continue;
var ptsPart = parts[0].Trim(); // pts_time=23.520000
var flagsPart = parts[1].Trim(); // flags=K__
if (!flagsPart.Contains("K"))
continue;
var ptsStr = ptsPart.Replace("pts_time=", "");
if (!double.TryParse(ptsStr, NumberStyles.Float, CultureInfo.InvariantCulture, out var pts))
continue;
if ( best == double.MaxValue)
best = pts;
if (pts > segmentStart)
break;
}
return best == double.MaxValue ? segmentStart : best;
}
}