protected override GeneratorResult ProcessInternal(ThumbnailBannerGeneratorSettings settings, GeneratorEntry entry) { try { entry.State = JobStates.Processing; _wrapper = new FfmpegWrapper(FfmpegExePath); var videoInfo = _wrapper.GetVideoInfo(settings.VideoFile); if (!videoInfo.IsGoodEnough()) { entry.State = JobStates.Done; entry.DoneType = JobDoneTypes.Failure; entry.Update("Failed", 1); return(GeneratorResult.Failed()); } TimeSpan duration = videoInfo.Duration; TimeSpan intervall = duration.Divide(settings.Columns * settings.Rows + 1); var frameArguments = new FrameConverterArguments { Width = 800, Intervall = intervall.TotalSeconds, StatusUpdateHandler = (progress) => { entry.Update(null, progress); }, InputFile = settings.VideoFile, OutputDirectory = FfmpegWrapper.CreateRandomTempDirectory() }; entry.Update("Extracting Frames", 0); var frames = _wrapper.ExtractFrames(frameArguments); if (_canceled) { return(GeneratorResult.Failed()); } entry.Update("Saving Thumbnails", 1); List <ThumbnailBannerGeneratorImage> images = new List <ThumbnailBannerGeneratorImage>(); foreach (var frame in frames) { images.Add(new ThumbnailBannerGeneratorImage { Image = frame.Item2, Position = frame.Item1 }); } ThumbnailBannerGeneratorData data = new ThumbnailBannerGeneratorData(); data.Settings = settings; data.Images = images.ToArray(); data.VideoName = settings.VideoFile; data.VideoInfo = videoInfo; data.FileSize = new FileInfo(settings.VideoFile).Length; var result = CreateBanner(data); JpegBitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create((BitmapSource)result)); using (FileStream stream = new FileStream(settings.OutputFile, FileMode.Create)) encoder.Save(stream); Directory.Delete(frameArguments.OutputDirectory); entry.DoneType = JobDoneTypes.Success; entry.Update("Done", 1); return(GeneratorResult.Succeeded(settings.OutputFile)); } catch (Exception) { entry.Update("Failed", 1); entry.DoneType = JobDoneTypes.Failure; return(GeneratorResult.Failed()); } finally { entry.State = JobStates.Done; if (_canceled) { entry.DoneType = JobDoneTypes.Cancelled; entry.Update("Cancelled", 1); } } }
protected override GeneratorResult ProcessInternal(HeatmapGeneratorSettings settings, GeneratorEntry entry) { try { entry.State = JobStates.Processing; _wrapper = new FfmpegWrapper(FfmpegExePath); var videoInfo = _wrapper.GetVideoInfo(settings.VideoFile); if (videoInfo.Duration <= TimeSpan.Zero) { entry.State = JobStates.Done; entry.DoneType = JobDoneTypes.Failure; entry.Update("Failed", 1); return(GeneratorResult.Failed()); } TimeSpan duration = videoInfo.Duration; //TODO string script = ViewModel.GetScriptFile(settings.VideoFile); var actions = ViewModel.LoadScriptActions(script, null); if (actions == null || actions.Count == 0) { entry.State = JobStates.Done; entry.DoneType = JobDoneTypes.Failure; entry.Update("Failed", 1); return(GeneratorResult.Failed()); } List <TimedPosition> timeStamps = ViewModel.FilterDuplicates(actions.ToList()).Cast <FunScriptAction>().Select(f => new TimedPosition { Position = f.Position, TimeStamp = f.TimeStamp }).ToList(); Brush heatmap = HeatMapGenerator.Generate3(timeStamps, TimeSpan.FromSeconds(10), TimeSpan.Zero, duration, 1.0, out Geometry bounds); bounds.Transform = new ScaleTransform(settings.Width, settings.Height); var rect = new Rect(0, 0, settings.Width, settings.Height); DrawingVisual visual = new DrawingVisual(); using (DrawingContext context = visual.RenderOpen()) { if (!settings.TransparentBackground) { context.DrawRectangle(Brushes.Black, null, rect); } if (settings.MovementRange) { context.PushClip(bounds); } context.DrawRectangle(heatmap, null, rect); if (settings.AddShadow) { LinearGradientBrush shadow = new LinearGradientBrush(); shadow.StartPoint = new Point(0.5, 0); shadow.EndPoint = new Point(0.5, 1); shadow.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF - 0x20, 0, 0, 0), 0)); shadow.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF - 0xcc, 0, 0, 0), 0.98)); shadow.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF - 0x50, 0, 0, 0), 0.98)); shadow.GradientStops.Add(new GradientStop(Color.FromArgb(0xFF - 0x50, 0, 0, 0), 1)); context.DrawRectangle(shadow, null, rect); } if (settings.MovementRange) { context.Pop(); } } RenderTargetBitmap bitmap = new RenderTargetBitmap(settings.Width, settings.Height, 96, 96, PixelFormats.Pbgra32); bitmap.Render(visual); PngBitmapEncoder encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(bitmap)); using (FileStream stream = new FileStream(settings.OutputFile, FileMode.Create)) encoder.Save(stream); entry.DoneType = JobDoneTypes.Success; entry.Update("Done", 1); return(GeneratorResult.Succeeded(settings.OutputFile)); } catch (Exception) { entry.Update("Failed", 1); entry.DoneType = JobDoneTypes.Failure; return(GeneratorResult.Failed()); } finally { entry.State = JobStates.Done; if (_canceled) { entry.DoneType = JobDoneTypes.Cancelled; entry.Update("Cancelled", 1); } } }
protected override GeneratorResult ProcessInternal(ThumbnailGeneratorSettings settings, GeneratorEntry entry) { try { entry.State = JobStates.Processing; _wrapper = new FfmpegWrapper(FfmpegExePath); double intervall = settings.Intervall; if (settings.Intervall < 1) { var info = _wrapper.GetVideoInfo(settings.VideoFile); if (!info.IsGoodEnough()) { entry.Update("Failed", 1); entry.DoneType = JobDoneTypes.Failure; return(GeneratorResult.Failed()); } const double targetFrameCount = 500.0; intervall = info.Duration.TotalSeconds / targetFrameCount; intervall = Math.Min(Math.Max(1, intervall), 10); } FrameConverterArguments arguments = new FrameConverterArguments { StatusUpdateHandler = (progress) => { entry.Update(null, progress); }, InputFile = settings.VideoFile, OutputDirectory = FfmpegWrapper.CreateRandomTempDirectory(), Intervall = intervall, Width = settings.Width, Height = settings.Height, ClipLeft = settings.ClipLeft, DeLense = settings.ClipLeft }; string thumbfile = Path.ChangeExtension(settings.VideoFile, "thumbs"); entry.Update("Extracting Frames", 0); var frames = _wrapper.ExtractFrames(arguments); if (_canceled) { return(GeneratorResult.Failed()); } entry.Update("Saving Thumbnails", 1); VideoThumbnailCollection thumbnails = new VideoThumbnailCollection(); foreach (var frame in frames) { thumbnails.Add(frame.Item1, frame.Item2); } using (FileStream stream = new FileStream(thumbfile, FileMode.Create, FileAccess.Write)) { thumbnails.Save(stream); } thumbnails.Dispose(); entry.DoneType = JobDoneTypes.Success; entry.Update("Done", 1); return(GeneratorResult.Succeeded(thumbfile)); } catch (Exception) { entry.DoneType = JobDoneTypes.Failure; return(GeneratorResult.Failed()); } finally { entry.State = JobStates.Done; if (_canceled) { entry.DoneType = JobDoneTypes.Cancelled; } } }
protected override GeneratorResult ProcessInternal(PreviewGeneratorSettings settings, GeneratorEntry entry) { entry.State = JobStates.Processing; List <string> tempFiles = new List <string>(); 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 }; 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 (1/4): Clipping Video Section {i + 1}/{settings.TimeFrames.Count}", ((i / (double)settings.TimeFrames.Count)) / 4.0); if (!_wrapper.Execute(clipArguments)) { return(GeneratorResult.Failed()); } if (_canceled) { return(GeneratorResult.Failed()); } } entry.Update("Generating GIF (2/4): Merging Clips", 1 / 4.0); 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()); } } entry.Update("Generating GIF (3/4): Extracting Palette", 2 / 4.0); 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()); } entry.Update("Generating GIF (3/4): Creating GIF", 3 / 4.0); 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()); } entry.Update("Done!", 4 / 4.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 { 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); } } } }
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); } } } }