public void CreatePositionList(List <CarController> cars) { DestroyAll(); float height = cars.Count * positionsInfoPrefab.GetComponent <RectTransform>().rect.height; float width = GetComponent <RectTransform>().rect.width; GetComponent <RectTransform>().sizeDelta = new Vector2(GetComponent <RectTransform>().rect.width, height); float bestTime = 0; bool setFinishedCanvas = false; for (int i = 0; i < cars.Count; i++) { for (int j = 0; j < cars.Count; j++) { if (cars[j].GetPosition() == (i + 1)) { if (i == 0) { bestTime = cars[j].GetFinishTime(); } if (cars[j].humanPlayer != null) { setFinishedCanvas = true; RacingCanvasController.racingCanvas.FinishedCanvas(cars[j]); } GameObject obj = Instantiate(positionsInfoPrefab, transform, true); curObjs.Add(obj); obj.SetActive(true); PositionBar posBar = obj.GetComponent <PositionBar>(); posBar.positionText.text = (i + 1).ToString(); if (cars[j].humanPlayer != null) { if (cars[j].humanPlayer.name != "") { posBar.nameText.text = cars[j].humanPlayer.name; } else { posBar.nameText.text = "Player"; } } else { posBar.nameText.text = cars[j].aIPlayer.name; } int timeM = (int)(cars[j].GetFinishTime() / 60); int timeS = (int)(cars[j].GetFinishTime() - timeM * 60); int timeMS = (int)((cars[j].GetFinishTime() - timeM * 60 - timeS) * 100); posBar.totalTimeText.text = timeM.ToString("D2") + ":" + timeS.ToString("D2") + ":" + timeMS.ToString("D2"); if (i > 0) { float diffTime = cars[j].GetFinishTime() - bestTime; timeM = (int)(diffTime / 60); timeS = (int)(diffTime - timeM * 60); timeMS = (int)((diffTime - timeM * 60 - timeS) * 100); if (timeM != 0) { posBar.diffText.text = "+ " + timeM.ToString() + ":" + timeS.ToString("D2") + ":" + timeMS.ToString("D2"); } else if (timeM == 0 && timeS != 0) { posBar.diffText.text = "+ " + timeS.ToString() + ":" + timeMS.ToString("D2"); } else if (timeM == 0 && timeS == 0 && timeMS != 0) { posBar.diffText.text = "+ " + timeS.ToString("D1") + ":" + timeMS.ToString("D2"); } break; } else { posBar.diffText.text = ""; } } } } if (!setFinishedCanvas) { RacingCanvasController.racingCanvas.FinishedCanvas(null); } }
protected override GeneratorResult ProcessInternal(PreviewGeneratorSettings settings, GeneratorEntry entry) { entry.State = JobStates.Processing; List <string> tempFiles = new List <string>(); int totalSteps = settings.OverlayScriptPositions ? 5 : 4; int stepsDone = 0; try { _wrapper = new FfmpegWrapper(FfmpegExePath); List <string> sectionFileNames = new List <string>(); if (settings.TimeFrames.Any(tf => tf.IsFactor)) { VideoInfo info = _wrapper.GetVideoInfo(settings.VideoFile); if (!info.IsGoodEnough()) { entry.State = JobStates.Done; entry.DoneType = JobDoneTypes.Failure; entry.Update("Failed", 1); return(GeneratorResult.Failed()); } TimeSpan duration = info.Duration; settings.TimeFrames.ForEach(tf => tf.CalculateStart(duration)); } ClipExtractorArguments clipArguments = new ClipExtractorArguments { InputFile = settings.VideoFile, Width = settings.Width, Height = settings.Height, Framerate = settings.Framerate, ClipLeft = settings.ClipLeft, DeLense = settings.ClipLeft }; VideoInfo clipInfo = null; for (int i = 0; i < settings.TimeFrames.Count; i++) { string sectionFileName = Path.Combine(Path.GetTempPath(), Path.GetFileName(settings.VideoFile) + $"-clip_{i}.mkv"); sectionFileNames.Add(sectionFileName); tempFiles.Add(sectionFileName); var timeFrame = settings.TimeFrames[i]; clipArguments.Duration = timeFrame.Duration; clipArguments.StartTimeSpan = timeFrame.StartTimeSpan; clipArguments.OutputFile = sectionFileName; entry.Update($"Generating GIF ({stepsDone + 1}/{totalSteps}): Clipping Video Section {i + 1}/{settings.TimeFrames.Count}", (stepsDone / (double)totalSteps) + ((i / (double)settings.TimeFrames.Count)) / totalSteps); if (!_wrapper.Execute(clipArguments)) { return(GeneratorResult.Failed()); } if (clipInfo == null) { clipInfo = _wrapper.GetVideoInfo(sectionFileName); } if (_canceled) { return(GeneratorResult.Failed()); } } stepsDone++; if (settings.OverlayScriptPositions) { string script = ViewModel.GetScriptFile(settings.VideoFile); var actions = ViewModel.LoadScriptActions(script, null)?.OfType <FunScriptAction>().ToList(); Size barSize = new Size(clipInfo.Resolution.Horizontal, 20); if (actions != null && actions.Count > 0) { PositionBar bar = new PositionBar { Width = barSize.Width, Height = barSize.Height, TotalDisplayedDuration = TimeSpan.FromSeconds(5), Background = Brushes.Black, Positions = new PositionCollection(actions.Select(a => new TimedPosition() { Position = a.Position, TimeStamp = a.TimeStamp })), DrawCircles = false, DrawLines = false, }; for (int i = 0; i < settings.TimeFrames.Count; i++) { TimeSpan start = settings.TimeFrames[i].StartTimeSpan; TimeSpan max = start + settings.TimeFrames[i].Duration; TimeSpan progress = start; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); Directory.CreateDirectory(tempDir); string overlayFileName = Path.Combine(Path.GetTempPath(), Path.GetFileName(settings.VideoFile) + $"-overlay_{i}.mkv"); tempFiles.Add(overlayFileName); int frame = 0; entry.Update($"Generating GIF ({stepsDone + 1}/{totalSteps}): Overlaying Positions {i + 1}/{settings.TimeFrames.Count}", (stepsDone / (double)totalSteps) + ((i / (double)settings.TimeFrames.Count)) / totalSteps); double expectedFrames = 1 + (settings.TimeFrames[i].Duration.TotalSeconds * clipInfo.FrameRate); while (progress <= max) { progress = start + TimeSpan.FromSeconds(frame / clipInfo.FrameRate); bar.Progress = progress; var bitmap = RenderToBitmap(bar, barSize); PngBitmapEncoder encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); using (FileStream f = new FileStream(Path.Combine(tempDir, $"frame{frame:0000}.png"), FileMode.CreateNew)) encoder.Save(f); frame++; } FrameMergeArguments frameMergeArguments = new FrameMergeArguments { Framerate = clipInfo.FrameRate, InputFile = Path.Combine(tempDir, "frame%04d.png"), OutputFile = overlayFileName }; if (!_wrapper.Execute(frameMergeArguments)) { return(GeneratorResult.Failed()); } var clipInfo2 = _wrapper.GetVideoInfo(overlayFileName); Directory.Delete(tempDir, true); string mergeFileName = Path.Combine(Path.GetTempPath(), Path.GetFileName(settings.VideoFile) + $"-merge_{i}.mkv"); tempFiles.Add(mergeFileName); VideoOverlayArguments overlayArguments = new VideoOverlayArguments { InputFile = sectionFileNames[i], Overlay = overlayFileName, OutputFile = mergeFileName, PosX = 0, PosY = clipInfo.Resolution.Vertical - 20 }; if (!_wrapper.Execute(overlayArguments)) { return(GeneratorResult.Failed()); } sectionFileNames[i] = mergeFileName; } } stepsDone++; } entry.Update($"Generating GIF ({stepsDone + 1}/{totalSteps}): Merging Clips", stepsDone / (double)totalSteps); string clipFileName; if (sectionFileNames.Count == 1) { clipFileName = sectionFileNames[0]; } else { ClipMergeArguments mergeArguments = new ClipMergeArguments { InputFile = settings.VideoFile, ClipFiles = sectionFileNames.ToArray(), OutputFile = Path.Combine(Path.GetTempPath(), Path.GetFileName(settings.VideoFile) + $"-clip.mkv") }; clipFileName = mergeArguments.OutputFile; tempFiles.Add(clipFileName); if (!_wrapper.Execute(mergeArguments)) { return(GeneratorResult.Failed()); } if (_canceled) { return(GeneratorResult.Failed()); } } stepsDone++; entry.Update($"Generating GIF ({stepsDone + 1}/{totalSteps}): Extracting Palette", stepsDone / (double)totalSteps); string paletteFile = clipFileName + "-palette.png"; PaletteExtractorArguments paletteArguments = new PaletteExtractorArguments { InputFile = clipFileName, OutputFile = paletteFile }; tempFiles.Add(paletteFile); if (!_wrapper.Execute(paletteArguments)) { return(GeneratorResult.Failed()); } if (_canceled) { return(GeneratorResult.Failed()); } stepsDone++; entry.Update($"Generating GIF ({stepsDone + 1}/{totalSteps}): Creating GIF", stepsDone / (double)totalSteps); string gifFileName = settings.OutputFile; GifCreatorArguments gifArguments = new GifCreatorArguments(); gifArguments.InputFile = clipFileName; gifArguments.PaletteFile = paletteFile; gifArguments.OutputFile = gifFileName; gifArguments.Framerate = settings.Framerate; if (!_wrapper.Execute(gifArguments)) { return(GeneratorResult.Failed()); } if (_canceled) { return(GeneratorResult.Failed()); } stepsDone++; entry.Update("Done", 1.0); bool success = File.Exists(gifFileName); if (success) { entry.DoneType = JobDoneTypes.Success; } else { entry.DoneType = JobDoneTypes.Failure; } OnDone(new Tuple <bool, string>(success, gifFileName)); if (success) { return(GeneratorResult.Succeeded(gifFileName)); } return(GeneratorResult.Failed()); } catch (Exception e) { entry.DoneType = JobDoneTypes.Failure; return(GeneratorResult.Failed()); } finally { entry.State = JobStates.Done; if (_canceled) { entry.DoneType = JobDoneTypes.Cancelled; } foreach (string tempFile in tempFiles) { if (File.Exists(tempFile)) { File.Delete(tempFile); } } } }