Adds a Letterbox cropper, and fixes cover images for the anime converter
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
Adds a new letterbox cropper, which will use 10 samples at 30 points in a video in order to find the correct crop to remove letter-boxing. Makes sure to always copy codec of cover images, as ffmpeg treats them as video streams.
This commit is contained in:
@@ -33,40 +33,6 @@ public final class FFMpegHelper {
|
||||
private FFMpegHelper() {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Developer notes:
|
||||
Setting the default track:
|
||||
-disposition:s:0 +default
|
||||
Where s,a,v is the type specifier, and the number is the relative index
|
||||
|
||||
To unset default:
|
||||
-disposition:s:0 -default
|
||||
|
||||
-map command for reordering:
|
||||
First number is the index of the input file, which is 0, unless more files are given as input
|
||||
Optionally, a,v,s can be used to select the type of stream to map
|
||||
Lastly, the number is either the global index of the stream, or the relative, if a type has been specified
|
||||
If only the first number is given, all streams from that file are selected
|
||||
The output file will contain all mapped streams in the order they are mapped
|
||||
|
||||
Plan: Sort all streams by set criteria. Map all selected streams by looping using their relative index for
|
||||
selection.
|
||||
|
||||
Streams should probably have an input index, so it's easier to treat extra subtitles more seamlessly. So, by
|
||||
including any external subtitle files as input, there would be no need to fiddle more with storing input files.
|
||||
|
||||
Instead of storing the ffmpeg command as a list of strings, it should be stored as an object with different list
|
||||
for input arguments and output arguments. That way, it would be much easier to add input-related arguments later
|
||||
in the process.
|
||||
|
||||
ffmpeg -codecs:
|
||||
Used for checking which codecs are available.
|
||||
|
||||
ffmpeg -h encoder=h264_nvenc:
|
||||
Used to see available encoder presets/profiles/info
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets streams from a file
|
||||
@@ -280,6 +246,29 @@ public final class FFMpegHelper {
|
||||
return StringUtil.stringBetween(result.output(), "[STREAM]", "[/STREAM]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration, in seconds, of the given file
|
||||
*
|
||||
* @param ffprobePath <p>The path to the ffprobe executable</p>
|
||||
* @param file <p>The file to get the duration of</p>
|
||||
* @return <p>The duration</p>
|
||||
* @throws IOException <p>If unable to probe the file</p>
|
||||
* @throws NumberFormatException <p>If ffmpeg returns a non-number</p>
|
||||
*/
|
||||
public static double getDuration(@NotNull String ffprobePath, @NotNull File file) throws IOException, NumberFormatException {
|
||||
FFMpegCommand probeCommand = new FFMpegCommand(ffprobePath);
|
||||
probeCommand.addGlobalOption("-v", "error", "-show_entries", "format=duration", "-of",
|
||||
"default=noprint_wrappers=1:nokey=1");
|
||||
probeCommand.addInputFile(file.toString());
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(probeCommand.getResult());
|
||||
ProcessResult result = runProcess(processBuilder, file.getParentFile(), "", false);
|
||||
if (result.exitCode() != 0) {
|
||||
throw new IllegalArgumentException("File probe failed with code " + result.exitCode());
|
||||
}
|
||||
return Double.parseDouble(result.output().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of all streams and parses each stream into one of three objects
|
||||
*
|
||||
@@ -294,7 +283,8 @@ public final class FFMpegHelper {
|
||||
private static StreamProbeResult parseStreams(@NotNull String ffprobePath, @NotNull List<String> streams,
|
||||
@NotNull File file, @NotNull List<String> subtitleFormats,
|
||||
@NotNull List<String> audioFormats) throws IOException {
|
||||
StreamProbeResult probeResult = new StreamProbeResult(new ArrayList<>(List.of(file)), parseStreamObjects(streams));
|
||||
StreamProbeResult probeResult = new StreamProbeResult(new ArrayList<>(List.of(file)),
|
||||
parseStreamObjects(streams));
|
||||
getExternalStreams(probeResult, ffprobePath, file.getParentFile(), file.getName(), subtitleFormats);
|
||||
getExternalStreams(probeResult, ffprobePath, file.getParentFile(), file.getName(), audioFormats);
|
||||
return probeResult;
|
||||
@@ -319,18 +309,11 @@ public final class FFMpegHelper {
|
||||
StreamType streamType = getStreamType(streamInfo);
|
||||
|
||||
switch (streamType) {
|
||||
case VIDEO:
|
||||
parsedStreams.add(new VideoStream(streamInfo, 0, relativeVideoIndex++));
|
||||
break;
|
||||
case AUDIO:
|
||||
parsedStreams.add(new AudioStream(streamInfo, 0, relativeAudioIndex++));
|
||||
break;
|
||||
case SUBTITLE:
|
||||
parsedStreams.add(new SubtitleStream(streamInfo, 0, relativeSubtitleIndex++));
|
||||
break;
|
||||
case OTHER:
|
||||
parsedStreams.add(new OtherStream(streamInfo, 0));
|
||||
break;
|
||||
case VIDEO -> parsedStreams.add(new VideoStream(streamInfo, 0, relativeVideoIndex++));
|
||||
case AUDIO -> parsedStreams.add(new AudioStream(streamInfo, 0, relativeAudioIndex++));
|
||||
case SUBTITLE -> parsedStreams.add(new SubtitleStream(streamInfo, 0, relativeSubtitleIndex++));
|
||||
case OTHER -> parsedStreams.add(new OtherStream(streamInfo, 0, false));
|
||||
case COVER_IMAGE -> parsedStreams.add(new OtherStream(streamInfo, 0, true));
|
||||
}
|
||||
}
|
||||
return parsedStreams;
|
||||
@@ -347,11 +330,13 @@ public final class FFMpegHelper {
|
||||
String codecType = ValueParsingHelper.parseString(streamInfo.get(StreamTag.CODEC_TYPE), "");
|
||||
switch (codecType) {
|
||||
case "video":
|
||||
String mime = ValueParsingHelper.parseString(streamInfo.get(StreamTag.TAG_MIME_TYPE), "");
|
||||
// Some attached covers are marked as video streams
|
||||
if (ValueParsingHelper.parseInt(streamInfo.get(StreamTag.DISPOSITION_ATTACHED_PIC), 0) != 1) {
|
||||
if (ValueParsingHelper.parseInt(streamInfo.get(StreamTag.DISPOSITION_ATTACHED_PIC), 0) != 1 &&
|
||||
!mime.startsWith("image/") && !mime.endsWith("-font")) {
|
||||
return StreamType.VIDEO;
|
||||
} else {
|
||||
return StreamType.OTHER;
|
||||
return StreamType.COVER_IMAGE;
|
||||
}
|
||||
case "audio":
|
||||
return StreamType.AUDIO;
|
||||
|
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
/**
|
||||
* A class which helps with operations on strings
|
||||
*/
|
||||
final class StringUtil {
|
||||
public final class StringUtil {
|
||||
|
||||
private StringUtil() {
|
||||
|
||||
|
Reference in New Issue
Block a user