mirror of
https://github.com/unclshura/ABStemPlayer.git
synced 2026-08-07 00:43:38 +00:00
123 lines
3.7 KiB
C#
123 lines
3.7 KiB
C#
using System.Diagnostics;
|
|
|
|
namespace AudioCore.Impl;
|
|
|
|
public sealed class FfmpegProcess : IDisposable
|
|
{
|
|
public Process? Proc { get; private set; }
|
|
public Stream? Stdout { get; private set; }
|
|
public Stream? Stdin { get; private set; }
|
|
|
|
private string _name;
|
|
private string _commandLine;
|
|
private bool _redirectOutput;
|
|
private bool _redirectInput;
|
|
|
|
public FfmpegProcess(string name, string commandLine, bool redirectOutput = true, bool redirectInput = true)
|
|
{
|
|
_name = name;
|
|
_commandLine = commandLine;
|
|
_redirectOutput = redirectOutput;
|
|
_redirectInput = redirectInput;
|
|
}
|
|
|
|
public void StartProcess()
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "ffmpeg",
|
|
Arguments = _commandLine,
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = _redirectOutput,
|
|
RedirectStandardError = true,
|
|
RedirectStandardInput = _redirectInput,
|
|
CreateNoWindow = true,
|
|
WindowStyle = ProcessWindowStyle.Hidden,
|
|
ErrorDialog = false
|
|
};
|
|
|
|
Proc = Process.Start(psi) ?? throw new InvalidOperationException("Failed to start ffmpeg process");
|
|
|
|
if ( _redirectInput)
|
|
Stdin = Proc.StandardInput.BaseStream;
|
|
if (_redirectOutput)
|
|
Stdout = Proc.StandardOutput.BaseStream;
|
|
|
|
// Start draining stderr immediately
|
|
_ = Task.Run(() => DrainStderr(Proc));
|
|
}
|
|
|
|
private void DrainStderr(Process proc)
|
|
{
|
|
try
|
|
{
|
|
var reader = proc.StandardError;
|
|
|
|
// ffmpeg writes short lines, so ReadLine is fine
|
|
// If you want zero allocations, use ReadAsync into a rented buffer.
|
|
string? line;
|
|
while ((line = reader.ReadLine()) != null)
|
|
{
|
|
Debug.WriteLine($"{_name}: {line}");
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignore exceptions during stderr drain, as the process may have exited
|
|
}
|
|
}
|
|
|
|
public int Read(float[] buffer, int offset, int count)
|
|
{
|
|
var bytesNeeded = count * sizeof(float);
|
|
var tmp = new byte[bytesNeeded];
|
|
|
|
var readBytes = Stdout!.Read(tmp, 0, bytesNeeded);
|
|
if (readBytes <= 0)
|
|
return 0;
|
|
|
|
Buffer.BlockCopy(tmp, 0, buffer, offset * sizeof(float), readBytes);
|
|
|
|
return readBytes / sizeof(float);
|
|
}
|
|
|
|
public static long ProbeTotalSamples(string path, int sampleRate)
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "ffprobe",
|
|
Arguments = $"-v error -show_entries format=duration -of csv=p=0 \"{path}\"",
|
|
RedirectStandardOutput = true,
|
|
CreateNoWindow = true,
|
|
WindowStyle = ProcessWindowStyle.Hidden,
|
|
UseShellExecute = false
|
|
};
|
|
|
|
using var p = Process.Start(psi);
|
|
var s = p!.StandardOutput.ReadToEnd();
|
|
p.WaitForExit();
|
|
|
|
if (double.TryParse(s, System.Globalization.NumberStyles.Float,
|
|
System.Globalization.CultureInfo.InvariantCulture, out var seconds))
|
|
{
|
|
return (long)(seconds * sampleRate);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private void DisposeProcessOnly()
|
|
{
|
|
try { Stdout?.Dispose(); } catch { }
|
|
try { Stdin?.Dispose(); } catch { }
|
|
try { Proc?.StandardError.BaseStream?.Dispose(); } catch { }
|
|
try { if (Proc != null && !Proc.HasExited) Proc.Kill(); } catch { }
|
|
try { Proc?.Dispose(); } catch { }
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
DisposeProcessOnly();
|
|
}
|
|
}
|