forked from j62/ctbrec
Implement contact sheet creation
This commit is contained in:
parent
ce908bded2
commit
dcd3df5d0c
|
@ -0,0 +1,60 @@
|
|||
package ctbrec.ui.settings;
|
||||
|
||||
import ctbrec.recorder.postprocessing.CreateContactSheet;
|
||||
import ctbrec.recorder.postprocessing.PostProcessor;
|
||||
import ctbrec.ui.settings.api.Category;
|
||||
import ctbrec.ui.settings.api.Preferences;
|
||||
import ctbrec.ui.settings.api.Setting;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.scene.control.ColorPicker;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
public class CreateContactSheetPaneFactory extends AbstractPostProcessingPaneFactory {
|
||||
|
||||
private SimpleStringProperty background;
|
||||
|
||||
@Override
|
||||
public Preferences doCreatePostProcessorPane(PostProcessor pp) {
|
||||
SimpleStringProperty totalSize = new SimpleStringProperty(null, CreateContactSheet.TOTAL_SIZE, pp.getConfig().getOrDefault(CreateContactSheet.TOTAL_SIZE, "1920"));
|
||||
SimpleStringProperty padding = new SimpleStringProperty(null, CreateContactSheet.PADDING, pp.getConfig().getOrDefault(CreateContactSheet.PADDING, "4"));
|
||||
SimpleStringProperty cols = new SimpleStringProperty(null, CreateContactSheet.COLS, pp.getConfig().getOrDefault(CreateContactSheet.COLS, "8"));
|
||||
SimpleStringProperty rows = new SimpleStringProperty(null, CreateContactSheet.ROWS, pp.getConfig().getOrDefault(CreateContactSheet.ROWS, "7"));
|
||||
SimpleStringProperty filename = new SimpleStringProperty(null, CreateContactSheet.FILENAME, pp.getConfig().getOrDefault(CreateContactSheet.FILENAME, "contactsheet.jpg"));
|
||||
background = new SimpleStringProperty(null, CreateContactSheet.BACKGROUND, pp.getConfig().getOrDefault(CreateContactSheet.BACKGROUND, "0x333333"));
|
||||
properties.add(totalSize);
|
||||
properties.add(padding);
|
||||
properties.add(cols);
|
||||
properties.add(rows);
|
||||
properties.add(filename);
|
||||
properties.add(background);
|
||||
|
||||
Setting backgroundSetting = Setting.of("", background, "Hexadecimal value of the background color for the space between the thumbnails");
|
||||
Preferences prefs = Preferences.of(new MapPreferencesStorage(),
|
||||
Category.of(pp.getName(),
|
||||
Setting.of("Total Width", totalSize, "Total width of the generated contact sheet"),
|
||||
Setting.of("Padding", padding, "Padding between the thumbnails"),
|
||||
Setting.of("Columns", cols ),
|
||||
Setting.of("Rows", rows),
|
||||
Setting.of("File Name", filename),
|
||||
Setting.of("Background", createColorPicker(background.get())),
|
||||
backgroundSetting
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
// hide the background color input field, because we use a color picker instead
|
||||
backgroundSetting.getGui().setVisible(false);
|
||||
} catch (Exception e) {
|
||||
// hiding the background color input field didn't work, that's ok
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
private ColorPicker createColorPicker(String hexColor) {
|
||||
Color preselection = Color.web(hexColor);
|
||||
ColorPicker colorPicker = new ColorPicker(preselection);
|
||||
colorPicker.setOnAction(e -> background.set(colorPicker.getValue().toString()));
|
||||
return colorPicker;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.recorder.postprocessing.CreateContactSheet;
|
||||
import ctbrec.recorder.postprocessing.DeleteTooShort;
|
||||
import ctbrec.recorder.postprocessing.Move;
|
||||
import ctbrec.recorder.postprocessing.PostProcessor;
|
||||
|
@ -28,6 +29,7 @@ public class PostProcessingDialogFactory {
|
|||
ppToDialogMap.put(Rename.class, RenamerPaneFactory.class);
|
||||
ppToDialogMap.put(Move.class, MoverPaneFactory.class);
|
||||
ppToDialogMap.put(DeleteTooShort.class, DeleteTooShortPaneFactory.class);
|
||||
ppToDialogMap.put(CreateContactSheet.class, CreateContactSheetPaneFactory.class);
|
||||
}
|
||||
|
||||
private PostProcessingDialogFactory() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.Optional;
|
|||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.recorder.postprocessing.Copy;
|
||||
import ctbrec.recorder.postprocessing.CreateContactSheet;
|
||||
import ctbrec.recorder.postprocessing.DeleteOriginal;
|
||||
import ctbrec.recorder.postprocessing.DeleteTooShort;
|
||||
import ctbrec.recorder.postprocessing.Move;
|
||||
|
@ -42,7 +43,8 @@ public class PostProcessingStepPanel extends GridPane {
|
|||
Script.class,
|
||||
DeleteOriginal.class,
|
||||
DeleteTooShort.class,
|
||||
RemoveKeepFile.class
|
||||
RemoveKeepFile.class,
|
||||
CreateContactSheet.class
|
||||
}; // @formatter: on
|
||||
|
||||
ListView<PostProcessor> stepListView;
|
||||
|
|
|
@ -1,14 +1,33 @@
|
|||
package ctbrec.recorder.postprocessing;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.NotImplementedExcetion;
|
||||
import ctbrec.OS;
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.io.StreamRedirectThread;
|
||||
import ctbrec.recorder.RecordingManager;
|
||||
|
||||
public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CreateContactSheet.class);
|
||||
|
||||
public static final String TOTAL_SIZE = "total_size";
|
||||
public static final String PADDING = "padding";
|
||||
public static final String COLS = "columns";
|
||||
public static final String ROWS = "rows";
|
||||
public static final String BACKGROUND = "background";
|
||||
public static final String FILENAME = "filename";
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "create contact sheet";
|
||||
|
@ -16,6 +35,72 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
|
|||
|
||||
@Override
|
||||
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
|
||||
throw new NotImplementedExcetion();
|
||||
int totalWidth = Integer.parseInt(getConfig().getOrDefault(TOTAL_SIZE, "1920"));
|
||||
int padding = Integer.parseInt(getConfig().getOrDefault(PADDING, "4"));
|
||||
int cols = Integer.parseInt(getConfig().getOrDefault(COLS, "8"));
|
||||
int rows = Integer.parseInt(getConfig().getOrDefault(ROWS, "7"));
|
||||
String color = getConfig().getOrDefault(BACKGROUND, "0x333333");
|
||||
String filename = getConfig().getOrDefault(FILENAME, "contactsheet.jpg");
|
||||
|
||||
int thumbWidth = (int) ((totalWidth - (cols + 1) * padding) / (double)cols);
|
||||
int numberOfThumbs = rows * cols;
|
||||
long lengthInSeconds = rec.getLength().getSeconds();
|
||||
double thumbnailInterval = lengthInSeconds / (double)numberOfThumbs;
|
||||
|
||||
String filterArg = MessageFormat.format("fps=1/{0},scale={4}:-1,tile={1}x{2}:color={6}:margin={3}:padding={3},scale={5}:-1",
|
||||
thumbnailInterval,
|
||||
cols, rows,
|
||||
padding,
|
||||
Integer.toString(thumbWidth),
|
||||
Integer.toString(totalWidth),
|
||||
color);
|
||||
File executionDir = rec.getPostProcessedFile().isDirectory() ? rec.getPostProcessedFile() : rec.getPostProcessedFile().getParentFile();
|
||||
File output = new File(executionDir, filename);
|
||||
|
||||
File input = getInputFile(rec);
|
||||
String[] args = {
|
||||
"-skip_frame",
|
||||
"nokey",
|
||||
"-i",
|
||||
input.getCanonicalPath(),
|
||||
"-vf",
|
||||
filterArg,
|
||||
"-an",
|
||||
"-vsync",
|
||||
"0",
|
||||
"-qscale:v",
|
||||
"3",
|
||||
output.getCanonicalPath()
|
||||
};
|
||||
String[] cmdline = OS.getFFmpegCommand(args);
|
||||
LOG.info("Executing {} in working directory {}", Arrays.toString(cmdline), executionDir);
|
||||
Process ffmpeg = Runtime.getRuntime().exec(cmdline, new String[0], executionDir);
|
||||
int exitCode = 1;
|
||||
File ffmpegLog = File.createTempFile("create_contact_sheet_" + input.getName(), ".log");
|
||||
try (FileOutputStream mergeLogStream = new FileOutputStream(ffmpegLog)) {
|
||||
Thread stdout = new Thread(new StreamRedirectThread(ffmpeg.getInputStream(), mergeLogStream));
|
||||
Thread stderr = new Thread(new StreamRedirectThread(ffmpeg.getErrorStream(), mergeLogStream));
|
||||
stdout.start();
|
||||
stderr.start();
|
||||
exitCode = ffmpeg.waitFor();
|
||||
LOG.debug("FFmpeg exited with code {}", exitCode);
|
||||
stdout.join();
|
||||
stderr.join();
|
||||
mergeLogStream.flush();
|
||||
}
|
||||
if (exitCode != 1) {
|
||||
if (ffmpegLog.exists()) {
|
||||
Files.delete(ffmpegLog.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getInputFile(Recording rec) {
|
||||
if (rec.isSingleFile()) {
|
||||
return rec.getPostProcessedFile();
|
||||
} else {
|
||||
// TODO this has to be changed to support other playlist types like DASH
|
||||
return new File(rec.getPostProcessedFile(), "playlist.m3u8");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue