1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
/*
Peek Copyright (c) 2017-2018 by Philipp Wolfer <ph.wolfer@gmail.com>
This file is part of Peek.
This software is licensed under the GNU General Public License
(version 3 or later). See the LICENSE file in this distribution.
*/
using Peek.Recording;
namespace Peek.PostProcessing {
/**
* Use FFmpeg to generate an optimized GIF from a video input
*/
public class FfmpegPostProcessor : CliPostProcessor {
private RecordingConfig config;
public FfmpegPostProcessor (RecordingConfig config) {
this.config = config;
}
public override async Array<File>? process_async (Array<File> files) throws RecordingError {
var input_file = files.index (0);
var palette_file = yield generate_palette_async (input_file);
if (palette_file == null) {
return null;
}
var output_file = yield generate_animation_async (input_file, palette_file);
try {
yield palette_file.delete_async ();
} catch (Error e) {
stderr.printf ("Error deleting palette file: %s\n", e.message);
}
if (output_file == null) {
return null;
}
var result = new Array<File> ();
result.append_val (output_file);
return result;
}
public static bool is_available () {
return Utils.check_for_executable ("ffmpeg");
}
private async File? generate_palette_async (File file) throws RecordingError {
try {
var palette_file = Utils.create_temp_file ("png");
string[] args = {
"ffmpeg", "-y",
"-i", file.get_path (),
"-vf", "fps=%d,palettegen".printf (config.framerate),
palette_file
};
try {
yield spawn_command_async (args);
} catch (RecordingError e) {
FileUtils.remove (palette_file);
throw e;
}
return File.new_for_path (palette_file);
} catch (FileError e) {
stderr.printf ("Error: %s\n", e.message);
throw new RecordingError.POSTPROCESSING_ABORTED (e.message);
}
}
private async File? generate_animation_async (File input_file, File palette_file) throws RecordingError {
try {
var extension = Utils.get_file_extension_for_format (config.output_format);
var output_file = Utils.create_temp_file (extension);
var argv = new Array<string> ();
argv.append_val ("ffmpeg");
argv.append_val ("-y");
argv.append_val ("-i");
argv.append_val (input_file.get_path ());
argv.append_val ("-i");
argv.append_val (palette_file.get_path ());
argv.append_val ("-filter_complex");
argv.append_val ("fps=%d,paletteuse".printf (config.framerate));
if (config.output_format == OutputFormat.APNG) {
argv.append_val ("-plays");
argv.append_val ("0");
}
argv.append_val (output_file);
try {
yield spawn_command_async (argv.data);
} catch (RecordingError e) {
FileUtils.remove (output_file);
throw e;
}
return File.new_for_path (output_file);
} catch (FileError e) {
throw new RecordingError.POSTPROCESSING_ABORTED (e.message);
}
}
}
}
|