如何在循环中逐个执行Process进程,单进程调用ffmpeg处理视频文件
问题描述
在for循环中向Process传入文件路径,让ffmpeg.exe每次仅读取一个视频文件的格式,但文本框显示第一个视频的内容后程序就停止了。尝试使用process.close()和process.hasExited,但无法阻止程序启动多个进程,导致委托输出大量涌入文本框(输出顺序混乱,无法区分对应视频,还存在输出缺失或重复的情况)。需要实现循环中每次仅执行一个进程,完成后再启动下一个进程。
用户原代码:
foreach (String file in inputList) { if (flag == true) { flag = false; //textBox1.Text += file + Environment.NewLine; checkvideoFormat(file); } } } catch (Exception err) { MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void checkvideoFormat(String filePath) { Process process1 = new Process(); process1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; process1.StartInfo.CreateNoWindow = true; process1.StartInfo.UseShellExecute = false; process1.StartInfo.FileName = ".\\ffmpeg.exe"; process1.StartInfo.WorkingDirectory = ".\\"; process1.EnableRaisingEvents = true; process1.StartInfo.RedirectStandardOutput = true; //if this is true, UseShellExecute must be false. true if output should be written to StandardOutput process1.StartInfo.RedirectStandardError = true; //indicates that the associated Process has written a line that's terminated with a newline process1.ErrorDataReceived += new DataReceivedEventHandler(inputHandler); process1.Exited += (ending, p) => { flag = true; Console.WriteLine(flag); //textBox1.Text += "Hey " + file + Environment.NewLine; //process1.CancelOutputRead(); //process1.CancelErrorRead();// }; process1.StartInfo.Arguments = "-i " + " \"" + filePath + "\""; Console.WriteLine(process1.StartInfo.Arguments); process1.Start(); process1.BeginOutputReadLine(); process1.BeginErrorReadLine(); //process1.Close(); //process1.WaitForExit(); /* if(process1.HasExited == true) { //process1.Refresh(); flag = true; //process1.Close(); //process1.Kill(); Thread.Sleep(1000); } */ } int test_123 = 0; private void inputHandler(object sender, DataReceivedEventArgs l) { cba.Append(test_123 + l.Data + "\n"); videoInput = test_123 + l.Data + Environment.NewLine; //Console.WriteLine(cba); //Process p = sender as Process; Console.WriteLine(videoInput); this.Invoke(new MethodInvoker(() => { if (!String.IsNullOrEmpty(videoInput)) { textBox1.Text += videoInput; } })); test_123++; }
解决方案
核心问题是原代码用flag异步控制循环,但UI线程会直接跑完循环逻辑,不会等待Exited事件触发。以下两种方式可实现逐个执行进程:
方式一:后台线程同步等待(简单直接)
把循环放到后台线程,用WaitForExit()等待当前进程结束后再处理下一个文件,避免阻塞UI:
// 示例:按钮点击触发处理 private void btnStart_Click(object sender, EventArgs e) { // 启动后台线程执行循环,不卡UI Task.Run(() => { foreach (string file in inputList) { ProcessSingleFile(file); // 给UI更新处理完成状态 this.Invoke(() => textBox1.Text += $"✅ 完成处理:{Path.GetFileName(file)}{Environment.NewLine}"); } }); } private void ProcessSingleFile(string filePath) { // using自动释放进程资源 using (Process ffmpegProcess = new Process()) { ffmpegProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; ffmpegProcess.StartInfo.CreateNoWindow = true; ffmpegProcess.StartInfo.UseShellExecute = false; ffmpegProcess.StartInfo.FileName = ".\\ffmpeg.exe"; ffmpegProcess.StartInfo.WorkingDirectory = ".\\"; ffmpegProcess.StartInfo.RedirectStandardError = true; // 绑定错误输出处理,关联当前文件名,避免输出混乱 ffmpegProcess.ErrorDataReceived += (sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { string output = $"[{Path.GetFileName(filePath)}] {args.Data}{Environment.NewLine}"; this.Invoke(() => textBox1.Text += output); } }; ffmpegProcess.StartInfo.Arguments = $"-i \"{filePath}\""; ffmpegProcess.Start(); ffmpegProcess.BeginErrorReadLine(); // 等待进程执行完毕 ffmpegProcess.WaitForExit(); // 确保所有输出都被读取 ffmpegProcess.CancelErrorRead(); } }
方式二:队列+事件驱动(非阻塞UI)
用队列存储待处理文件,当前进程结束后通过Exited事件触发下一个文件的处理,全程保持UI响应:
private Queue<string> _pendingFiles; // 示例:按钮点击触发处理 private void btnStart_Click(object sender, EventArgs e) { _pendingFiles = new Queue<string>(inputList); // 启动第一个文件的处理 ProcessNextFile(); } private void ProcessNextFile() { if (_pendingFiles.Count == 0) { this.Invoke(() => textBox1.Text += "🎉 所有文件处理完成!" + Environment.NewLine); return; } string currentFile = _pendingFiles.Dequeue(); using (Process ffmpegProcess = new Process()) { ffmpegProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; ffmpegProcess.StartInfo.CreateNoWindow = true; ffmpegProcess.StartInfo.UseShellExecute = false; ffmpegProcess.StartInfo.FileName = ".\\ffmpeg.exe"; ffmpegProcess.StartInfo.WorkingDirectory = ".\\"; ffmpegProcess.EnableRaisingEvents = true; ffmpegProcess.StartInfo.RedirectStandardError = true; // 绑定输出处理,关联当前文件名 ffmpegProcess.ErrorDataReceived += (sender, args) => { if (!string.IsNullOrEmpty(args.Data)) { string output = $"[{Path.GetFileName(currentFile)}] {args.Data}{Environment.NewLine}"; this.Invoke(() => textBox1.Text += output); } }; // 进程结束后自动启动下一个 ffmpegProcess.Exited += (sender, args) => { ffmpegProcess.CancelErrorRead(); this.Invoke(ProcessNextFile); }; ffmpegProcess.StartInfo.Arguments = $"-i \"{currentFile}\""; ffmpegProcess.Start(); ffmpegProcess.BeginErrorReadLine(); } }
关键修复点
- 输出区分:每个进程的输出绑定当前文件名,解决顺序混乱、无法区分的问题。
- 流程控制:要么用后台线程同步等待,要么用队列+事件驱动,确保一个进程结束后再启动下一个。
- 资源释放:用
using包裹Process对象,自动释放进程资源,避免泄漏。 - 移除共享计数器:原代码的
test_123会被所有进程共享,导致计数混乱,改为按文件区分输出更清晰。
内容的提问来源于stack exchange,提问作者TSLee
相关产品推荐
相关产品推荐

