#c# #ffmpeg #process #ipc
Вопрос:
Поэтому я пытаюсь использовать FFProbe для извлечения мультимедийной информации с помощью API процесса, я нахожусь в сети 5 и хочу оставаться кроссплатформенным. Прежде чем я начал возиться, я попробовал ядро FFMpeg для извлечения метаданных, но оно работает только в Windows и не может обрабатывать многострочные теги, анализируется только последняя строка. Поэтому я попытался вручную передать выходные данные в свое приложение. Первоначально я использовал CliWrap для аккуратной передачи выходных данных в a StringBuilder
, как я обычно делаю в других репозиториях.
var output = new StringBuilder();
var args = new[] { "-i", filePath };
var pResult = await Cli.Wrap(GetFFProbeFilePath())
.WithArguments(args)
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(output))
.ExecuteAsync(cancellationToken);
filePath
: любой медиафайл.GetFFProbeFilePath()
: очищает %ПУТЬ% для ffmpeg, возвращает «C:ProgramDatachocolateybinffprobe.exe».cancellationToken
:None
для тестовых случаев.
Но это не сработало. Вывод всегда остается пустым. Поэтому я выполнил следующую команду, используя не повышенный cmd: C:\ProgramData\chocolatey\bin\ffprobe.exe -i my/lovely/file.mp3
и консоль отображает вывод полностью, без ошибок.
Естественно, это вина CliWrap, потому что я совершенен; вот почему я заменил его простым вызовом API процесса:
var output = new StringBuilder();
using (var p = new Process())
{
p.StartInfo.FileName = "C:\ProgramData\chocolatey\bin\ffprobe.exe";
p.StartInfo.Arguments = String.Join(' ', args);
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
output.Append(await p.StandardOutput.ReadToEndAsync());
await p.WaitForExitAsync(cancellationToken);
}
По-прежнему вывод пуст. Поэтому вместо использования канала, который я записываю в вывод файла:
using var p = new Process();
p.StartInfo.FileName = "C:\ProgramData\chocolatey\bin\ffprobe.exe";
p.StartInfo.Arguments = " -i "" filePath "" 2> " ""C:\temp\metadata.log"";
if (!p.Start())
throw new Exception();
await p.WaitForExitAsync(cancellationToken);
C:temp
: 777
The temporary file is not created, from that I conclude that the process has not exited successfully. Even though no exception is thrown (I tried piping the errors as well: nothing).
Затем я продолжаю выполнять эту команду 20 раз и задаюсь вопросом, почему она работает в ps amp; cmd, но не в C#:
C:ProgramDatachocolateybinffprobe.exe -i my/lovely/file.mp3 2> "C:tempmetadata.log"
Пока я не запустил его с помощью [Win] [R], и вместо завершения процесса я получил графический сбой и сообщение об ошибке Windows, файл не был создан. После некоторых экспериментов я обнаружил, что то же самое поведение для start
команды в cmd.
Почему в имени (вставьте божество предпочтения) есть разница между [Win] [R] и cmd, и почему он выходит из строя при запуске с C#? Как мне преодолеть это странное поведение, сохраняя при этом кросс-платформенную совместимость?
Кстати, все это прекрасно работает в linux.
Изменить: При запуске процесса cmd вручную FFProbe вызывается правильно, но я все еще не получаю никаких данных в выходном конвейере, так cmd.exe
как ничего не записывает, и я не могу передать FFProbe.
var output = new StringBuilder();
_ = await Cli.Wrap("cmd.exe")
.WithArguments("/C ffprobe -i "my/lovely/file.mp3" 2> "C:\temp\metadata.log"")
.WithStandardOutputPipe(PipeTarget.ToStringBuilder(output))
.ExecuteAsync(cancellationToken);
Это фактически создает metadata.log
файл, но без вывода , так как я перенаправляю только вывод cmd.exe
, а не ffprobe
.
Моя текущая мысль заключается в том, что я мог бы открыть a conhost
и вручную передать ffprobe
выходные данные на вход консоли, явно указав конвейер в аргументах команды, которые опять же не будут работать в linux таким же образом, что противоречит цели.
Комментарии:
1. Вы уверены, что FFProbe записывает свои выходные данные в stdout, а не в stderr? Некоторые программы делают это, особенно когда сообщают о прогрессе или какой-либо другой промежуточной информации. Кроме того, в вашем окончательном редактировании, что произойдет, если вы удалите канал из аргументов (
2> ...
детали) и передадите вывод и ошибку непосредственно через CliWrap?