private void runTask(videoTask t, Process process) { this.rsForm.setPercent("0.0"); this.rsForm.setTime(""); this.rsForm.setFps(""); this.rsForm.setEta(""); string fp = t.getFP(); process = new System.Diagnostics.Process(); process.StartInfo.FileName = "cmd"; // 必须禁用操作系统外壳程序 process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardInput = true; process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler); process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); process.Start(); this.PIDs[0] = process.Id; this.rsForm.PID = process.Id; this.rsForm.setStopBtnState(true); // 找到预设存储的文件并提取到taskSetting中 string tsfp = System.Windows.Forms.Application.StartupPath + "\\taskSettings\\" + t.getSetting() + ".json"; taskSetting ts = JsonConvert.DeserializeObject <taskSetting>(File.ReadAllText(tsfp)); // 将encoder信息传给信息显示界面 this.rsForm.encodingProgram = ts.encoder; this.rsForm.HideVideoEncoderSetting(); cmdCode c = new cmdCode(fp, ts, this.outputFolderPath); string cmd; int type = c.taskType(); int checkNum = 0; int beforeProcessCheckTime = 3500; int processCheckInterval = 1000; const int checkF = 20; // 定义一个内部(匿名)方法 // 整个视频转换过程结束时 InnerMethodDelagate afterSuccess = delegate() { this.finishedNum++; this.rsForm.setStatusBarFilesCountLabel(this.finishedNum, this.num); try { process.CancelErrorRead(); process.CancelOutputRead(); } catch (Exception e) { //saveLog2File(); } this.rsForm.setEta(""); this.rsForm.setFps(""); this.rsForm.setTime(""); this.rsForm.setEstKbps(""); this.rsForm.SetTaskStepsLabel(true); Process p = Process.GetProcessById(this.PIDs[0]); p.Kill(); this.rsForm.setStopBtnState(false); this.rsForm.setPercent("-3"); this.isfinished[0] = true; this.reportCount = 0; this.log.Clear(); miniLog += t.getFP() + Environment.NewLine + Environment.NewLine + Environment.NewLine; saveMiniLog2File(); }; // 运行失败时调用 InnerMethodDelagate afterFailed = delegate() { this.isfailed = true; saveLog2File(); this.rsForm.setPercent("-1"); this.rsForm.setTime("发生错误"); this.rsForm.setFps("日志保存在程序目录"); int sleepTime = 5; // 设置失败后继续下一个任务的时间 this.rsForm.setEta(sleepTime.ToString() + "秒后继续运行"); this.rsForm.setStatusBarLabelTextColorRED(); this.finishedNum++; this.rsForm.setStatusBarFilesCountLabel(this.finishedNum, this.num); this.rsForm.HideVideoEncoderSetting(); try { process.CancelErrorRead(); process.CancelOutputRead(); } catch (Exception e) { //saveLog2File(); } Thread.Sleep(sleepTime * 1000); Process p = Process.GetProcessById(this.PIDs[0]); p.Kill(); this.rsForm.setStopBtnState(false); this.rsForm.setPercent("-3"); this.isfinished[0] = true; this.reportCount = 0; this.log.Clear(); }; // 视频编码前更新UI(显示视频总帧数) InnerMethodDelagate DispVideoFrames = delegate() { // MediaInfo读取视频帧数 MediaInfo MI = new MediaInfo(); string duration; string frameRate; string frames; MI.Open(t.getFP()); duration = MI.Get(StreamKind.Video, 0, "Duration"); try { double totalTime = Double.Parse(duration) / 1000.0; frameRate = MI.Get(StreamKind.Video, 0, "FrameRate"); frames = ((int)(totalTime * Double.Parse(frameRate))).ToString(); if (!String.IsNullOrWhiteSpace(frames)) { this.rsForm.setTime("0/" + frames); } } catch (Exception e) { //saveLog2File(); } }; InnerMethodDelagate VideoEncode = delegate() { // 视频编码 this.encoding = true; this.rsForm.setPercent("0.0"); string ext = this.getFileExtName(t.getFP()); if (String.Equals(ext, "avs", StringComparison.CurrentCultureIgnoreCase)) { this.videoType = AVS; } else { this.videoType = NORMAL; DispVideoFrames(); } cmd = c.cmdCodeGenerate(VIDEOENCODE, this.videoType); process.StandardInput.WriteLine(cmd); try { process.BeginErrorReadLine(); process.BeginOutputReadLine(); } catch (Exception e) { //saveLog2File(); } checkNum = 0; this.reportCount = 0; int cpx2 = this.checkPattern + this.checkPattern; for (int i = 0; i < cpx2; i++) { checkFrame[i] = 0; } for (int i = 0; i < cpx2; i++) { this.fps[i] = 0; } Thread.Sleep(beforeProcessCheckTime); Process p; switch (videoType) { case NORMAL: p = GetSubTaskProcess(ts.encoder); if (p != null) { this.subTaskPID = p.Id; } else { this.subTaskPID = -1; } break; case AVS: Process avsP = GetSubTaskProcess("avs4x265.exe"); int avsId = avsP.Id; if (avsP != null) { bool hasFound = false; // 等待视频编码进程启动,最长等待1小时 for (int i = 0; i < 7200; i++) { // 确认avs进程仍在运行 try { Process.GetProcessById(avsId); } catch (Exception e) { if (this.encoding == true || ConfirmFailed()) { afterFailed(); } return; } // 每隔500ms寻找视频编码进程 p = GetSubTaskProcess(ts.encoder, avsId); if (p != null) { this.subTaskPID = p.Id; hasFound = true; break; } else { Thread.Sleep(500); } } if (!hasFound) { this.subTaskPID = -1; } } else { this.subTaskPID = -1; } break; default: break; } this.rsForm.ShowVideoEncoderSetting(); while (this.encoding == true || this.postProcessing == true) { try { Process.GetProcessById(this.subTaskPID); } catch (Exception e) { if (this.encoding == true || ConfirmFailed()) { afterFailed(); } return; } Thread.Sleep(processCheckInterval); } try { process.CancelErrorRead(); process.CancelOutputRead(); } catch (Exception e) { //saveLog2File(); } this.rsForm.HideVideoEncoderSetting(); }; InnerMethodDelagate Mux = delegate() { // muxer this.muxing = true; int stepIdx = type == ONLYVIDEO ? 2 : 3; this.rsForm.SetTaskStepsLabel(false, stepIdx, stepIdx, MUXER); cmd = c.cmdCodeGenerate(MUXER); process.StandardInput.WriteLine(cmd); try { process.BeginErrorReadLine(); process.BeginOutputReadLine(); } catch (Exception e) { //saveLog2File(); } checkNum = 0; Thread.Sleep(beforeProcessCheckTime); // 有些超短的视频(1-2M),如果不加这句就会直接判定为任务已失败,疑似原因:没等判断完进程就已经结束 string muxerProcessName = ""; if (String.Equals("mp4", ts.outputFormat)) { muxerProcessName = "mp4Box"; } else if (String.Equals("mkv", ts.outputFormat)) { muxerProcessName = "mkvmerge"; } Process p = GetSubTaskProcess(muxerProcessName); if (p != null) { this.subTaskPID = p.Id; } else { this.subTaskPID = -1; } while (this.muxing == true) { try { Process.GetProcessById(this.subTaskPID); } catch (Exception e) { Thread.Sleep(1000); if (this.muxing == true) { afterFailed(); } return; } Thread.Sleep(processCheckInterval); //checkNum = checkCmdRunning(checkNum, checkF); //if (checkNum == -1) //{ // return; //} } afterSuccess(); string tempVideoFp = c.cmdCodeGenerate(DELETEVIDEOTEMP); string tempAudioFp = c.cmdCodeGenerate(DELETEAUDIOTEMP); try { File.Delete(tempVideoFp); File.Delete(tempAudioFp); } catch (System.IO.IOException ex) { this.log.AppendLine("出现异常:" + ex); saveLog2File(); } }; // 音频编码或复制开始前更新UI(显示音频总时长) InnerMethodDelagate DispAudioDuration = delegate() { // MediaInfo读取音频时长 MediaInfo MI = new MediaInfo(); string duration; MI.Open(c.getAudioSource()); duration = MI.Get(StreamKind.Audio, 0, 69); if (!String.IsNullOrWhiteSpace(duration)) { this.rsForm.setTime("0/" + duration); } this.rsForm.setPercent("0.0", AUDIOENCODE); }; InnerMethodDelagate ClearUIAfterAudioProcessing = delegate() { this.rsForm.setPercent("0.0"); this.rsForm.setTime(""); this.rsForm.setFps(""); }; switch (type) { case ONLYVIDEO: this.rsForm.SetTaskStepsLabel(false, 1, 2, VIDEOENCODE); VideoEncode(); // 一个子任务失败了意味着这个文件的编码任务失败,所以在所有子任务开始时都要检查checkNum是否为-1 if (isfailed) { return; } //afterSuccess(); Mux(); break; case COPYAUDIO: // 复制音频 cmd = c.cmdCodeGenerate(AUDIOCOPY); process.StandardInput.WriteLine(cmd); process.BeginErrorReadLine(); process.BeginOutputReadLine(); this.audioProcessing = true; this.rsForm.SetTaskStepsLabel(false, 1, 3, AUDIOCOPY); DispAudioDuration(); checkNum = 0; Thread.Sleep(beforeProcessCheckTime); Process p = GetSubTaskProcess("ffmpeg"); if (p != null) { this.subTaskPID = p.Id; } else { this.subTaskPID = -1; } while (this.audioProcessing == true) { try { Process.GetProcessById(this.subTaskPID); } catch (Exception e) { Thread.Sleep(1000); if (this.audioProcessing == true) { afterFailed(); } return; } Thread.Sleep(processCheckInterval); //checkNum = checkCmdRunning(checkNum, checkF); //if (checkNum == -1) //{ // return; //} } ClearUIAfterAudioProcessing(); process.CancelErrorRead(); process.CancelOutputRead(); this.rsForm.SetTaskStepsLabel(false, 2, 3, VIDEOENCODE); // 一个子任务失败了意味着这个文件的编码任务失败,所以在所有子任务开始时都要检查checkNum是否为-1 if (isfailed) { return; } VideoEncode(); // 一个子任务失败了意味着这个文件的编码任务失败,所以在所有子任务开始时都要检查checkNum是否为-1 if (isfailed) { return; } Mux(); break; case SUPPRESSAUDIO: // 音频编码 cmd = c.cmdCodeGenerate(AUDIOENCODE); process.StandardInput.WriteLine(cmd); process.BeginErrorReadLine(); process.BeginOutputReadLine(); this.audioProcessing = true; this.rsForm.SetTaskStepsLabel(false, 1, 3, AUDIOENCODE); DispAudioDuration(); checkNum = 0; Thread.Sleep(beforeProcessCheckTime); Process p2 = GetSubTaskProcess(ts.audioEncoder); if (p2 != null) { this.subTaskPID = p2.Id; } else { this.subTaskPID = -1; } while (this.audioProcessing == true) { try { Process.GetProcessById(this.subTaskPID); } catch (Exception e) { Thread.Sleep(1000); if (this.audioProcessing == true) { afterFailed(); } return; } Thread.Sleep(processCheckInterval); //checkNum = checkCmdRunning(checkNum, checkF); //if (checkNum == -1) //{ // return; //} } ClearUIAfterAudioProcessing(); process.CancelErrorRead(); process.CancelOutputRead(); this.rsForm.SetTaskStepsLabel(false, 2, 3, VIDEOENCODE); // 一个子任务失败了意味着这个文件的编码任务失败,所以在所有子任务开始时都要检查checkNum是否为-1 if (isfailed) { return; } VideoEncode(); // 一个子任务失败了意味着这个文件的编码任务失败,所以在所有子任务开始时都要检查checkNum是否为-1 if (isfailed) { return; } Mux(); break; default: cmd = ""; break; } //MessageBox.Show(cmd); }
public void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { this.outPutCmdCount++; if (!String.IsNullOrEmpty(outLine.Data) && this.num != this.finishedNum) { string o = outLine.Data.ToString(); log.AppendLine(o); // 音频编码解析 if (this.audioProcessing == true) { string audioCopyFinishFlag = "video:0kB audio:"; string audioEncodeFinishFlag = "Optimizing...done"; this.rsForm.setPercent("-1"); string time = ""; string totalTime = ""; InnerMethodDelagate CalculateAndDispProgress = delegate() { string[] splitTemp = totalTime.Split(new char[] { ':' }); string HH = "0"; string MM = "0"; string SSMMM = "0"; if (splitTemp.Length == 3) { HH = splitTemp[0]; MM = splitTemp[1]; SSMMM = splitTemp[2]; } else if (splitTemp.Length == 3) { HH = "00"; MM = splitTemp[0]; SSMMM = splitTemp[1]; } int hr = Convert.ToInt32(HH); int min = Convert.ToInt32(MM); double sec = Convert.ToDouble(SSMMM); double totalTimeValue = hr * 3600 + min * 60 + sec; splitTemp = time.Split(new char[] { ':' }); if (splitTemp.Length == 2) { HH = "00"; MM = splitTemp[0]; SSMMM = splitTemp[1]; } else if (splitTemp.Length == 3) { HH = splitTemp[0]; MM = splitTemp[1]; SSMMM = splitTemp[2]; } hr = Convert.ToInt32(HH); min = Convert.ToInt32(MM); sec = Convert.ToDouble(SSMMM); double currentTimeValue = hr * 3600 + min * 60 + sec; double percent = currentTimeValue / totalTimeValue; this.rsForm.setPercent(percent.ToString("0.0"), AUDIOENCODE); //这一模式需要保留一位小数 }; if (o.Contains(audioCopyFinishFlag) || o.Contains(audioEncodeFinishFlag)) { this.audioProcessing = false; } else { if (o.Contains("time=")) // 表示是音频复制模式 { int timeIndex = o.IndexOf("time=") + 5; int timeLength = 11; time = o.Substring(timeIndex, timeLength); string currentTimeText = this.rsForm.getCurrentTimeText(); int splitIndex = currentTimeText.IndexOf("/"); totalTime = currentTimeText.Substring(splitIndex + 1, currentTimeText.Length - splitIndex - 1); if (String.IsNullOrWhiteSpace(totalTime)) { totalTime = time; } this.rsForm.setTime(time + "/" + totalTime); CalculateAndDispProgress(); } else // 音频编码模式 { // 通过ffmpeg读取送到qaac转码 // o 的格式 = 时间(速度x) 特征是x) int endIndex = o.IndexOf("x)"); int startIndex = o.IndexOf("("); // 速度 xxx.x 最多5位字符,间隔不应大于5 if (startIndex != -1 && endIndex != -1 && endIndex - startIndex < 6 && endIndex - startIndex > 0) { string speed = o.Substring(startIndex + 1, endIndex - startIndex - 1); // 第二个参数是子串的长度 this.rsForm.setFps(speed + "x"); // 从UI获取音频时长 string lastCurrentTimeText = this.rsForm.getCurrentTimeText(); int totalTimeStartIndex = lastCurrentTimeText.IndexOf("/") + 1; int totalTimeLength = lastCurrentTimeText.Length - totalTimeStartIndex; totalTime = lastCurrentTimeText.Substring(totalTimeStartIndex, totalTimeLength); time = o.Substring(0, startIndex - 1); if (String.IsNullOrWhiteSpace(totalTime)) { totalTime = time; } this.rsForm.setTime(time + "/" + totalTime); CalculateAndDispProgress(); } } } return; } // postProcessing解析 if (this.postProcessing == true) { string ppfinishFlag01 = "encoded "; string ppfinishFlag02 = " frames"; this.rsForm.setPercent("-2"); if (o.Contains(ppfinishFlag01) && o.Contains(ppfinishFlag02)) { this.postProcessing = false; miniLog += o + Environment.NewLine; } } // muxer解析 if (this.muxing == true) { string muxingFinishFlag_mp401 = "ISO File Writing: "; string muxingFinishFlag_mp402 = "| (99/100)"; string muxingFinishFlag_mkv = "Muxing took "; this.rsForm.setPercent("-2"); if ((o.Contains(muxingFinishFlag_mp401) && o.Contains(muxingFinishFlag_mp402)) || o.Contains(muxingFinishFlag_mkv)) { this.muxing = false; } return; } // 视频编码解析 string finishFlag01 = "x264 [info]: kb/s:"; string finishFlag02 = "x265 [info]: consecutive B-frames:"; //string finishFlag02 = " frames"; if (this.encoding) { if (this.startTime == 0) // 仅在开始时运行,编码结束后要重置为0 { this.startTime = System.Environment.TickCount; } if (o.Contains(finishFlag01) || o.Contains(finishFlag02)) // 视频编码结束了 { miniLog += o + Environment.NewLine; this.endTime = System.Environment.TickCount; int timeElapsed = this.endTime - this.startTime; this.startTime = 0; this.rsForm.setPercent("100"); this.rsForm.setFps(""); this.rsForm.setEta(""); this.rsForm.setTime(""); // 视频编码结束后,x264可能先对视频流单独混流一次 this.postProcessing = true; this.encoding = false; } else // 编码未结束更新进度 { if (!o.Contains("[info]") && !o.Contains("q=-0.0 size=")) { int checkIndex = this.reportCount % (checkPattern + checkPattern); checkTime[checkIndex] = System.Environment.TickCount; string encodedFrames = ""; string totalFrames = ""; int kbpsStartIndex, kbpsEndIndex; switch (this.videoType) { case NORMAL: // o 的格式 = xx frames: xx.xx fps, xxxx kb/s kbpsStartIndex = o.IndexOf("fps, ") + 5; kbpsEndIndex = o.IndexOf("kb/s") + 4; if (kbpsStartIndex != -1 && kbpsEndIndex != -1 && kbpsEndIndex - kbpsStartIndex > 6 && kbpsEndIndex - kbpsStartIndex < 14) { string estKbps = o.Substring(kbpsStartIndex, kbpsEndIndex - kbpsStartIndex); this.rsForm.setEstKbps(estKbps); } // 从UI获取视频总帧数 string lastCurrentTimeText = this.rsForm.getCurrentTimeText(); int totalTimeStartIndex = lastCurrentTimeText.IndexOf("/") + 1; int totalTimeLength = lastCurrentTimeText.Length - totalTimeStartIndex; totalFrames = lastCurrentTimeText.Substring(totalTimeStartIndex, totalTimeLength); // 从cmd中读取已经编码的帧数 int framesIndex = o.IndexOf("frames:"); // 帧数小于7位数 if (framesIndex != -1 && framesIndex <= 8) { string frames = ""; try { frames = o.Substring(0, framesIndex - 1); } catch (Exception e) { saveLog2File(); } // endIndex = -1 this.rsForm.setTime(frames + "/" + totalFrames); //string[] splitTemp = frames.Split(new char[] { '/' }); encodedFrames = frames; //totalFrames = splitTemp[1]; checkFrame[checkIndex] = Convert.ToInt32(encodedFrames); this.reportCount++; try { double percent = (double)(checkFrame[checkIndex]) / (Double.Parse(totalFrames)) * 100.0; this.rsForm.setPercent(percent.ToString()); if (percent >= 99.9) { miniLog += o + Environment.NewLine; } } catch (Exception e) { //saveLog2File(); } } break; case AVS: // o 的格式 = [xx.x%] 已完成帧数/总帧数, ... int endIndex = o.IndexOf("%]"); int startIndex = o.IndexOf("["); // 进度 xx.x 最多4位字符,间隔不应大于4 (index之间差值不得大于5) if (startIndex != -1 && endIndex != -1 && endIndex - startIndex < 6 && endIndex - startIndex > 0) { string progress = o.Substring(startIndex + 1, endIndex - startIndex - 1); // 第二个参数是子串的长度 this.rsForm.setPercent(progress); if (Convert.ToDouble(progress) >= 99.9) { miniLog += o + Environment.NewLine; } } // 从cmd中读取已经编码的帧数 int framesIndexCaseAVS = o.IndexOf(" frames"); if (framesIndexCaseAVS != -1) { // frames 格式: xxx/xxxxx (a/b, a的位数小于等于b的位数) string frames = o.Substring(endIndex + 3, framesIndexCaseAVS - endIndex - 3); this.rsForm.setTime(frames); string[] splitTemp = frames.Split(new char[] { '/' }); encodedFrames = splitTemp[0]; totalFrames = splitTemp[1]; checkFrame[checkIndex] = Convert.ToInt32(encodedFrames); this.reportCount++; } kbpsStartIndex = o.IndexOf("fps, ") + 5; kbpsEndIndex = o.IndexOf("kb/s") + 4; if (kbpsStartIndex != -1 && kbpsEndIndex != -1 && kbpsEndIndex - kbpsStartIndex > 6 && kbpsEndIndex - kbpsStartIndex < 14) { string estKbps = o.Substring(kbpsStartIndex, kbpsEndIndex - kbpsStartIndex); this.rsForm.setEstKbps(estKbps); } break; default: break; } if (this.reportCount >= checkPattern) { // 定义一个内部(匿名)方法 // 分析并计算fps和ETA InnerMethodDelagate processFpsAndETA = delegate() { // 取两位小数的方法 System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo(); nfi.NumberDecimalDigits = 2; this.rsForm.setFps(fps[checkIndex].ToString("N", nfi) + " fps"); // 计算优化后的ETA double avgFps = NonZeroAverage(this.fps); double eta = (double)(Convert.ToInt32(totalFrames) - checkFrame[checkIndex]) / avgFps / 60.0; double i = Math.Floor(eta); // 去除小数部分 double d = eta - i; double sec = d * 60.0; d = i / 60.0; double h = Math.Floor(d); double min = i - h * 60; string etaStr = ((int)h).ToString("00") + ":" + ((int)min).ToString("00") + ":" + ((int)sec).ToString("00"); this.rsForm.setEta(etaStr); }; if (checkIndex >= checkPattern) { // 注意:下两句和另一部分是不一样的,不能复制 int timeIntetval = checkTime[checkIndex] - checkTime[checkIndex - checkPattern]; int framesInteval = checkFrame[checkIndex] - checkFrame[checkIndex - checkPattern]; fps[checkIndex] = (double)framesInteval / (double)timeIntetval * 1000; if (fps[checkIndex] > 0) { processFpsAndETA(); } } else { // 注意:下两句和另一部分是不一样的,不能复制 int timeIntetval = checkTime[checkIndex] - checkTime[checkIndex + checkPattern]; int framesInteval = checkFrame[checkIndex] - checkFrame[checkIndex + checkPattern]; fps[checkIndex] = (double)framesInteval / (double)timeIntetval * 1000; if (fps[checkIndex] > 0) { processFpsAndETA(); } } } else { this.rsForm.setEta("正在计算..."); this.rsForm.setFps("正在统计..."); } } else { //miniLog += o + Environment.NewLine; //this.rsForm.setPercent("0.0"); //this.rsForm.setTime("索引中"); } } } } }