예제 #1
0
        internal static async Task ConvertAsync(GifModel.Config config, IProgress progress)
        {
            Debug.Assert(IsAvailable());

            var p = new Process
            {
                StartInfo =
                {
                    UseShellExecute       = false,
                    WindowStyle           = ProcessWindowStyle.Hidden,
                    CreateNoWindow        = true,
                    RedirectStandardError = true,
                    FileName  = Path,
                    Arguments = $"-framerate {config.FramesPerSecond} -i \"{config.TmpFilename}%4d.png\" -c:v libx264 -preset veryslow -crf 1 -pix_fmt yuv420p -frames:v {config.FramesPerSecond * config.NumSeconds} -r {config.FramesPerSecond} \"{config.Filename}\""
                }
            };
            var numFrames = config.NumSeconds * config.FramesPerSecond;

            progress.What = "converting";

            // progress reports
            p.ErrorDataReceived += (sender, args) =>
            {
                if (args.Data == null)
                {
                    return;
                }

                if (args.Data.StartsWith("frame="))
                {
                    if (int.TryParse(args.Data.Substring("frame=".Length), out var frame))
                    {
                        progress.Progress = frame / (float)numFrames;
                    }
                }
            };

            if (!p.Start())
            {
                throw new Exception("could not start ffmpeg.exe");
            }

            p.BeginErrorReadLine();

            while (!p.HasExited)
            {
                await Task.Run(() => p.WaitForExit(100));

                if (progress.Token.IsCancellationRequested && !p.HasExited)
                {
                    p.Kill();
                    progress.Token.ThrowIfCancellationRequested();
                }
            }
        }
예제 #2
0
        public override async void Execute()
        {
            if (models.NumEnabled != 2)
            {
                models.Window.ShowErrorDialog("Exactly two image equations should be visible for exporting");
                return;
            }

            if (models.Images.ImageType != typeof(TextureArray2D))
            {
                models.Window.ShowErrorDialog("Only 2D textures are supported");
                return;
            }

            if (!FFMpeg.IsAvailable())
            {
                if (models.Window.ShowYesNoDialog("ffmpeg is required for this feature. " +
                                                  "Please download the ffmpeg binaries and place them in the ImageViewer root directory. " +
                                                  "Open ffmpeg download page and ImageViewer root?", "download ffmpeg?"))
                {
                    var root = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                    if (root != null)
                    {
                        System.Diagnostics.Process.Start(root);
                    }
                    System.Diagnostics.Process.Start("https://www.ffmpeg.org/download.html");
                }
                return;
            }

            var ids = models.GetEnabledPipelines();
            // images valid?
            var img1 = models.Pipelines[ids[0]].Image;
            var img2 = models.Pipelines[ids[1]].Image;

            if (img1 == null)
            {
                return;
            }
            if (img2 == null)
            {
                return;
            }

            // ReSharper disable once CompareOfFloatsByEqualityOperator
            if (models.Display.Multiplier != 1.0f)
            {
                models.Window.ShowInfoDialog("Export will ignore image multiplier");
            }

            path.InitFromEquations(models);

            var sfd = new SaveFileDialog
            {
                Filter           = "MPEG-4 (*.mp4)|*.mp4",
                InitialDirectory = path.Directory,
                FileName         = path.Filename
            };

            if (sfd.ShowDialog(models.Window.TopmostWindow) != true)
            {
                return;
            }

            path.UpdateFromFilename(sfd.FileName, updateExtension: false);

            viewModel.InitTitles(models);
            var dia = new GifExportDialog(viewModel);

            if (models.Window.ShowDialog(dia) != true)
            {
                return;
            }

            // get tmp directory
            var tmpDir = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()));

            System.IO.Directory.CreateDirectory(tmpDir);
            var tmpName = tmpDir + "\\frame";

            // delete old file if it existed (otherwise ffmpeg will hang)
            System.IO.File.Delete(sfd.FileName);

            GifModel.Config config = new GifModel.Config
            {
                Filename        = sfd.FileName,
                TmpFilename     = tmpName,
                FramesPerSecond = viewModel.FramesPerSecond,
                SliderWidth     = viewModel.SliderSize,
                NumSeconds      = viewModel.TotalSeconds,
                Label1          = viewModel.Title1,
                Label2          = viewModel.Title2,
                Left            = (TextureArray2D)img1,
                Right           = (TextureArray2D)img2,
                Overlay         = (TextureArray2D)models.Overlay.Overlay
            };

            models.Gif.CreateGif(config, models.SharedModel);

            await models.Progress.WaitForTaskAsync();

            // delete tmp directory
            try
            {
                System.IO.Directory.Delete(tmpDir, true);
            }
            catch (Exception)
            {
                // ignored
            }

            if (models.Progress.LastTaskCancelledByUser)
            {
                return;
            }

            if (!String.IsNullOrEmpty(models.Progress.LastError))
            {
                models.Window.ShowErrorDialog(models.Progress.LastError);
            }
            else if (askForVideo)
            {
                askForVideo = models.Window.ShowYesNoDialog("Open video?", "Finished exporting");
                if (askForVideo)
                {
                    System.Diagnostics.Process.Start(config.Filename);
                }
            }
        }
예제 #3
0
        internal static async Task ConvertAsync(GifModel.Config config, IProgress progress)
        {
            Debug.Assert(IsAvailable());

            string startArgs =
                $"-framerate {config.FramesPerSecond} -i \"{config.TmpFilename}%4d.png\" -c:v libx264 -preset veryslow -crf 12 -pix_fmt yuv420p -frames:v {config.FramesPerSecond * config.NumSeconds} -r {config.FramesPerSecond} \"{config.Filename}\"";

            var p = new Process
            {
                StartInfo =
                {
                    UseShellExecute       = false,
                    WindowStyle           = ProcessWindowStyle.Hidden,
                    CreateNoWindow        = true,
                    RedirectStandardError = true,
                    FileName  = Path,
                    Arguments = startArgs
                }
            };
            var numFrames = config.NumSeconds * config.FramesPerSecond;

            progress.What = "converting";

            // progress reports
            string errors = "";

            p.ErrorDataReceived += (sender, args) =>
            {
                if (args.Data == null)
                {
                    return;
                }
                Console.Error.WriteLine("FFMPEG: " + args.Data);

                if (args.Data.StartsWith("frame="))
                {
                    var substr = args.Data.Substring("frame=".Length);
                    substr = substr.TrimStart().Split(' ')[0];
                    if (int.TryParse(substr, out var frame))
                    {
                        progress.Progress = frame / (float)numFrames;
                    }
                }
                else if (args.Data.StartsWith("error", StringComparison.OrdinalIgnoreCase))
                {
                    errors += args.Data;
                    errors += "\n";
                }
            };

            if (!p.Start())
            {
                throw new Exception("could not start ffmpeg.exe");
            }

            p.BeginErrorReadLine();

            while (!p.HasExited)
            {
                await Task.Run(() => p.WaitForExit(100));

                if (progress.Token.IsCancellationRequested && !p.HasExited)
                {
                    p.Kill();
                    progress.Token.ThrowIfCancellationRequested();
                }
            }

            if (!String.IsNullOrEmpty(errors))
            {
                throw new Exception(errors);
            }
        }