我知道有很多关于stackoverflow的信息,但是没有找到解决我问题的方法。
我制作了一个程序,将ffmpeg用于一些视频文件。这个过程可能需要几分钟,因此,我试图在另一个表单上创建一个进度条。
基本上,当我点击主表单(FormSync
)上的按钮时,会显示一个新表单。此表单只有一个进度条和一个取消按钮(我们可以调用FormProgress
)。
要执行ffmpeg,我使用另一个类(VideoProcessing
)来创建一个新进程,执行ffmpeg,并监视stderror(ffmpeg显示stderror的进度)。每次ffmpeg显示进度时,此类将解析输出,计算进度并引发事件(OnMergeProgress
)。
基本上,这是代码:
FormSync :
public partial class FormSync : Form
{
// this form show the progress of job
private FormProgress _formProgress;
// start the ffmpeg when click on button
private void mergeButton_click(object sender, EventArgs e)
{
var files = new List<string>() {"file1.mp4", "file2.mp4"};
MergeFiles(files);
}
// join all video files on a single file (using ffmpeg)
private void MergeFiles(IEnumerable<string> videoFiles)
{
// instantiate the class that execute ffmpeg
VideoProcessing videoProcessing = new VideoProcessing();
// this class has a a event to show current progress
// seconds = total seconds of video (sum length of all video files)
// currentSeconds = current progress
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Invoke((MethodInvoker) delegate()
{
// Instantiate the form of progress if not visible
if (_formProgress = null)
{
// define the minimum and maximum value of progressbar on constructor
_formProgress = new FormProgress(0, seconds);
_formProgress.ShowDialog(this);
}
// update the progress bar value
_formProgress.SetProgress(currentSeconds);
}
}
}
}
FormProgress :
public partial class FormProgress : Form
{
public FormProgress(int min, int max)
{
InitializeComponent();
progressBar.Minimum = min;
progressBar.Maximum = max;
}
public void SetProgress(int value)
{
value = (value <= progressBar.Minimum)
? progressBar.Minimum
: (value >= progressBar.Maximum) ? progressBar.Maximum : value;
progressBar.Value = value;
Refresh();
}
}
视频处理:
internal class VideoProcessing
{
// Events
public delegate void MergeProgressHandler(int totalSeconds, int currentSeconds);
public event MergeProgressHandler OnMergeProgress;
private int _totalTimeVideos;
public void MergeFiles(string[] videoFiles)
{
// calculate total time of all videos
_totalTimeVideos = SomeFunctionToCalculateTotalTime();
// create the Process object to execute FFMPEG (with stdout and stderr redirection)
_process = CreateFFMPEGProcess(videoFiles);
}
// capture the stdout and stderr of ffmpeg
private void MergeOutputHandler(object sendingProcess, DataReceivedEventArgs outline)
{
// capture the current progress
// here will go a regex, and some other code to parse the info from ffmpeg
// Raise the event
OnMergeProgress?.Invoke(_totalTimeVideos, progressSeconds);
}
}
基本上,FFMPEG执行和捕获过程使用以下代码: C# execute external program and capture (stream) the output
当我尝试执行代码时会出现问题。
当我点击que按钮时,会显示FormProgress,但在此之后,进度条&#34;冻结&#34;。该程序运行良好,没有挂起,但没有进度条更新。
如果在FormSync
上InvokeMethod
,我用以下内容替换原始代码,我可以看到ffmpeg正在运行,我的活动也在运作:
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Debug.WriteLine($"{currentSeconds}/{seconds}");
}
所以,问题不是ffmpeg或我的视频类,而是更新UI的东西。
如果我再次更改Invoke
,但这次使用Debug
,如下面的代码,Debug
仅打印第一次更新,仅此而已:
videoProcessing.OnMergeProgress += (seconds, currentSeconds) =>
{
Invoke((MethodInvoker) delegate() {
if (_formProgress == null) {
_formProgress = new FormProgress(Resources.merging_video_files, 0, seconds);
_formProgress.ShowDialog(this);
}
_formProgress.SetProgress(currentSeconds);
});
Debug.WriteLine($"{currentSeconds}/{seconds}");
}
答案 0 :(得分:1)
_formProgress.ShowDialog(this);
错误位于此处。在窗口关闭之前,ShowDialog()不会返回。不清楚何时发生,但与bug无关。由于它没有返回,Invoke()调用死锁并无法完成。这反过来导致工作线程挂起。
部分问题是代码使用Invoke()而不是Begininvoke(),如果你使用后一种方法,你就不会注意到同样的错误。并不是说这很漂亮,但它会掩盖这个问题。请注意,您不需要Invoke(),您不需要返回值,并且BeginInvoke()可以正常工作。
您遇到此错误的最终原因是您需要初始化ProgressBar.Maximum属性。不要这样做,100是一个很好的最大值。只需要一点点数学,现在进度(100 * currentSeconds)/秒。
你仍然需要调用ShowDialog(),这有点尴尬,你可以使用Load事件来调用MergeFiles()。