Adds converter modules and sorters
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good

Converters are now the result of combining multiple modules, and running them in the selected sequence. I hope to be able to completely remove converters by replacing them with templates that simply run some modules in a sequence, before running FFmpeg. Then I want to be able to parse a command into a template, so users have full control of what a template does.

Sorters each sort a list of streams after a single criteria. They can be chained in order to create complex chained sorters.
This commit is contained in:
2024-04-12 15:31:34 +02:00
parent 376d5655f2
commit 461c7552b3
51 changed files with 1819 additions and 579 deletions

View File

@@ -162,61 +162,6 @@ public final class FFMpegHelper {
return output.toString();
}
/**
* Maps an audio track to a ffmpeg command's output
*
* @param command <p>The command to add the audio track to</p>
* @param audioStream <p>The audio stream to be mapped</p>
* @param toStereo <p>Whether to convert the audio stream to stereo</p>
*/
public static void addAudioStream(@NotNull FFMpegCommand command, @NotNull AudioStream audioStream, boolean toStereo) {
mapStream(command, audioStream);
if (toStereo && audioStream.getChannels() > 2) {
command.addOutputFileOption("-af", "pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR");
}
}
/**
* Adds subtitles and video mapping to a command
*
* @param command <p>The list containing the rest of the command.</p>
* @param subtitleStream <p>The subtitle stream to be used.</p>
* @param videoStream <p>The video stream to be used.</p>
*/
public static void addSubtitleAndVideoStream(@NotNull FFMpegCommand command, @Nullable SubtitleStream subtitleStream,
@NotNull VideoStream videoStream) {
//No appropriate subtitle was found. Just add the video stream.
if (subtitleStream == null) {
mapStream(command, videoStream);
return;
}
//Add the correct command arguments depending on the subtitle type
if (!subtitleStream.getIsImageSubtitle()) {
addBurnedInSubtitle(command, subtitleStream, videoStream);
} else {
addBurnedInImageSubtitle(command, subtitleStream, videoStream);
}
}
/**
* Adds subtitle commands to a command list
*
* @param command <p>The list containing the FFmpeg commands.</p>
* @param subtitleStream <p>The subtitle stream to add.</p>
* @param videoStream <p>The video stream to burn the subtitle into.</p>
*/
private static void addBurnedInSubtitle(@NotNull FFMpegCommand command, @NotNull SubtitleStream subtitleStream,
@NotNull VideoStream videoStream) {
mapStream(command, videoStream);
String safeFileName = escapeSpecialCharactersInFileName(
command.getInputFiles().get(subtitleStream.getInputIndex()));
String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName,
subtitleStream.getRelativeIndex());
command.addOutputFileOption("-vf", subtitleCommand);
}
/**
* Adds arguments for converting a file to h264 using hardware acceleration
*
@@ -259,8 +204,9 @@ public final class FFMpegHelper {
*
* @param command <p>The command to add the mappings to</p>
* @param streams <p>The streams to map</p>
* @param <K> <p>The type of stream object to map</p>
*/
public static void mapAllStreams(@NotNull FFMpegCommand command, @NotNull List<StreamObject> streams) {
public static <K extends StreamObject> void mapAllStreams(@NotNull FFMpegCommand command, @NotNull List<K> streams) {
for (StreamObject stream : streams) {
mapStream(command, stream);
}
@@ -283,7 +229,7 @@ public final class FFMpegHelper {
* @param fileName <p>The filename to escape.</p>
* @return <p>A filename with known special characters escaped.</p>
*/
private static String escapeSpecialCharactersInFileName(String fileName) {
public static String escapeSpecialCharactersInFileName(String fileName) {
return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\")
.replaceAll("'", "'\\\\\\\\\\\\\''")
.replaceAll("%", "\\\\\\\\\\\\%")
@@ -293,21 +239,23 @@ public final class FFMpegHelper {
}
/**
* Adds external image subtitle commands to a command list
* Gets the nth stream from a list of streams
*
* @param command <p>The FFMPEG command to modify</p>
* @param subtitleStream <p>The external image subtitle stream to burn in</p>
* @param videoStream <p>The video stream to burn the subtitle into</p>
* @param streams <p>A list of streams</p>
* @param n <p>The index of the audio stream to get</p>
* @return <p>The first audio stream found, or null if no audio streams were found</p>
*/
private static void addBurnedInImageSubtitle(@NotNull FFMpegCommand command,
@NotNull SubtitleStream subtitleStream,
@NotNull VideoStream videoStream) {
command.addOutputFileOption("-filter_complex",
String.format("[%d:%d]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:%d][sub]overlay",
subtitleStream.getInputIndex(), subtitleStream.getAbsoluteIndex(), videoStream.getWidth(),
videoStream.getHeight(), videoStream.getWidth(), videoStream.getHeight(),
videoStream.getInputIndex(), videoStream.getAbsoluteIndex()));
command.addOutputFileOption("-profile:v", "baseline");
public static <G extends StreamObject> G getNthSteam(@NotNull List<G> streams, int n) {
if (n < 0) {
throw new IllegalArgumentException("N cannot be negative!");
}
G stream = null;
if (streams.size() > n) {
stream = streams.get(n);
} else if (!streams.isEmpty()) {
stream = streams.get(0);
}
return stream;
}
/**

View File

@@ -1,8 +1,5 @@
package net.knarcraft.ffmpegconverter.utility;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;