Simplifies and improves code. Removes some duplication
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good
This commit is contained in:
@ -88,24 +88,149 @@ public final class FFMpegHelper {
|
||||
/**
|
||||
* Starts and prints output of a process
|
||||
*
|
||||
* @param process <p>The process to run.</p>
|
||||
* @param folder <p>The folder the process should run in.</p>
|
||||
* @param processBuilder <p>The process to run.</p>
|
||||
* @param folder <p>The folder the process should run in.</p>
|
||||
* @param spacer <p>The character(s) to use between each new line read.</p>
|
||||
* @param write <p>Whether to write the output directly instead of storing it.</p>
|
||||
* @throws IOException <p>If the process can't be readProcess.</p>
|
||||
*/
|
||||
public static void convertProcess(ProcessBuilder process, File folder) throws IOException {
|
||||
public static String runProcess(ProcessBuilder processBuilder, File folder, String spacer, boolean write)
|
||||
throws IOException {
|
||||
//Give the user information about what's about to happen
|
||||
OutputUtil.print("Command to be run: ");
|
||||
OutputUtil.println(process.command().toString());
|
||||
process.directory(folder);
|
||||
process.redirectErrorStream(true);
|
||||
Process processConvert = process.start();
|
||||
BufferedReader readerConvert = new BufferedReader(new InputStreamReader(processConvert.getInputStream()));
|
||||
while (processConvert.isAlive()) {
|
||||
String read = readProcess(readerConvert, "\n");
|
||||
OutputUtil.println(processBuilder.command().toString());
|
||||
|
||||
//Set directory and error stream
|
||||
processBuilder.directory(folder);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
Process process = processBuilder.start();
|
||||
BufferedReader processReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
StringBuilder output = new StringBuilder();
|
||||
while (process.isAlive()) {
|
||||
String read = readProcess(processReader, spacer);
|
||||
if (!read.equals("")) {
|
||||
OutputUtil.println(read);
|
||||
if (write) {
|
||||
OutputUtil.print(read);
|
||||
} else {
|
||||
output.append(read);
|
||||
}
|
||||
}
|
||||
}
|
||||
OutputUtil.println("Process finished.");
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds audio to a command
|
||||
*
|
||||
* @param command <p>The command to add audio to.</p>
|
||||
* @param audioStream <p>The audio stream to be added.</p>
|
||||
* @param toStereo <p>Whether to convert the audio stream to stereo.</p>
|
||||
*/
|
||||
public static void addAudioStreams(List<String> command, AudioStream audioStream, boolean toStereo) {
|
||||
if (audioStream != null) {
|
||||
command.add("-map");
|
||||
command.add("0:" + audioStream.getAbsoluteIndex());
|
||||
if (toStereo && audioStream.getChannels() > 2) {
|
||||
command.add("-af");
|
||||
command.add("pan=stereo|FL=FC+0.30*FL+0.30*BL|FR=FC+0.30*FR+0.30*BR");
|
||||
//command.add("pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*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>
|
||||
* @param file <p>The file to convert.</p>
|
||||
*/
|
||||
public static void addSubtitlesAndVideo(List<String> command, SubtitleStream subtitleStream,
|
||||
VideoStream videoStream, File file) {
|
||||
//No appropriate subtitle was found. Just add the video stream.
|
||||
if (subtitleStream == null) {
|
||||
command.add("-map");
|
||||
command.add(String.format("0:%d", videoStream.getAbsoluteIndex()));
|
||||
return;
|
||||
}
|
||||
|
||||
//Add the correct command arguments depending on the subtitle type
|
||||
if (!subtitleStream.getIsImageSubtitle()) {
|
||||
addSubtitle(command, subtitleStream, videoStream);
|
||||
} else if (file.getName().equals(subtitleStream.getFile())) {
|
||||
addInternalImageSubtitle(command, subtitleStream, videoStream);
|
||||
} else {
|
||||
addExternalImageSubtitle(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 addSubtitle(List<String> command, SubtitleStream subtitleStream, VideoStream videoStream) {
|
||||
command.add("-map");
|
||||
command.add(String.format("0:%d", videoStream.getAbsoluteIndex()));
|
||||
command.add("-vf");
|
||||
String safeFileName = escapeSpecialCharactersInFileName(subtitleStream.getFile());
|
||||
String subtitleCommand = String.format("subtitles='%s':si=%d", safeFileName,
|
||||
subtitleStream.getRelativeIndex());
|
||||
command.add(subtitleCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes special characters which can cause trouble for ffmpeg
|
||||
*
|
||||
* @param fileName <p>The filename to escape.</p>
|
||||
* @return <p>A filename with known special characters escaped.</p>
|
||||
*/
|
||||
private static String escapeSpecialCharactersInFileName(String fileName) {
|
||||
return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\")
|
||||
.replaceAll("'", "'\\\\\\\\\\\\\''")
|
||||
.replaceAll("%", "\\\\\\\\\\\\%")
|
||||
.replaceAll(":", "\\\\\\\\\\\\:")
|
||||
.replace("]", "\\]")
|
||||
.replace("[", "\\[");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds image 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 addInternalImageSubtitle(List<String> command, SubtitleStream subtitleStream,
|
||||
VideoStream videoStream) {
|
||||
command.add("-filter_complex");
|
||||
String filter = String.format("[0:v:%d][0:%d]overlay", videoStream.getAbsoluteIndex(),
|
||||
subtitleStream.getAbsoluteIndex());
|
||||
command.add(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds external image subtitle commands to a command list
|
||||
*
|
||||
* @param command <p>The list containing the FFmpeg commands.</p>
|
||||
* @param externalImageSubtitle <p>The external image subtitle stream to add.</p>
|
||||
* @param videoStream <p>The video stream to burn the subtitle into.</p>
|
||||
*/
|
||||
private static void addExternalImageSubtitle(List<String> command, SubtitleStream externalImageSubtitle,
|
||||
VideoStream videoStream) {
|
||||
command.add("-i");
|
||||
command.add(externalImageSubtitle.getFile());
|
||||
command.add("-filter_complex");
|
||||
command.add(String.format("[1:s]scale=width=%d:height=%d,crop=w=%d:h=%d:x=0:y=out_h[sub];[%d:v]" +
|
||||
"[sub]overlay", videoStream.getWidth(), videoStream.getHeight(), videoStream.getWidth(),
|
||||
videoStream.getHeight(), videoStream.getAbsoluteIndex()));
|
||||
command.add("-profile:v");
|
||||
command.add("baseline");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +242,7 @@ public final class FFMpegHelper {
|
||||
* @throws IOException <p>If something goes wrong while probing.</p>
|
||||
*/
|
||||
private static String[] probeForStreams(String ffprobePath, File file) throws IOException {
|
||||
ProcessBuilder builderProbe = new ProcessBuilder(
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(
|
||||
ffprobePath,
|
||||
"-v",
|
||||
"error",
|
||||
@ -125,21 +250,8 @@ public final class FFMpegHelper {
|
||||
"stream_tags=language,title:stream=index,codec_name,codec_type,channels,codec_type,width,height",
|
||||
file.toString()
|
||||
);
|
||||
OutputUtil.println();
|
||||
OutputUtil.print("Probe command: ");
|
||||
OutputUtil.println(builderProbe.command().toString());
|
||||
builderProbe.redirectErrorStream(true);
|
||||
Process processProbe = builderProbe.start();
|
||||
BufferedReader readerProbe = new BufferedReader(new InputStreamReader(processProbe.getInputStream()));
|
||||
StringBuilder output = new StringBuilder();
|
||||
while (processProbe.isAlive()) {
|
||||
String read = readProcess(readerProbe, PROBE_SPLIT_CHARACTER);
|
||||
if (!read.equals("")) {
|
||||
OutputUtil.printDebug(read);
|
||||
output.append(read);
|
||||
}
|
||||
}
|
||||
return StringUtil.stringBetween(output.toString(), "[STREAM]", "[/STREAM]");
|
||||
String result = runProcess(processBuilder, file.getParentFile(), PROBE_SPLIT_CHARACTER, false);
|
||||
return StringUtil.stringBetween(result, "[STREAM]", "[/STREAM]");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,19 +292,22 @@ public final class FFMpegHelper {
|
||||
private static List<StreamObject> getExternalSubtitles(String ffprobePath, File directory, String convertingFile)
|
||||
throws IOException {
|
||||
List<StreamObject> parsedStreams = new ArrayList<>();
|
||||
//Find all files in the same directory with external subtitle formats
|
||||
String[] subtitleFormats = FileUtil.readFileLines("subtitle_formats.txt");
|
||||
File[] subtitleFiles = FileUtil.listFilesRecursive(directory, subtitleFormats, 1);
|
||||
|
||||
//Return early if no files were found
|
||||
if (subtitleFiles == null) {
|
||||
return parsedStreams;
|
||||
}
|
||||
|
||||
String fileTitle = FileUtil.stripExtension(convertingFile);
|
||||
List<File> subtitleFilesList = new ArrayList<>(Arrays.asList(subtitleFiles));
|
||||
//Finds the files which are subtitles belonging to the file
|
||||
//Finds the files which are subtitles probably belonging to the file
|
||||
subtitleFilesList = ListUtil.getMatching(subtitleFilesList,
|
||||
(subtitleFile) -> subtitleFile.getName().contains(fileTitle));
|
||||
for (File subtitleFile : subtitleFilesList) {
|
||||
//Probe the files and add them to the result list
|
||||
String[] streams = probeForStreams(ffprobePath, subtitleFile);
|
||||
for (String stream : streams) {
|
||||
String[] streamParts = stream.split(PROBE_SPLIT_CHARACTER);
|
||||
@ -291,7 +406,7 @@ public final class FFMpegHelper {
|
||||
String language = null;
|
||||
String title = "";
|
||||
for (String streamPart : streamParts) {
|
||||
if (streamPart.contains("codec_name=")) {
|
||||
if (streamPart.startsWith("codec_name=")) {
|
||||
codecName = streamPart.replace("codec_name=", "");
|
||||
} else if (streamPart.startsWith("index=")) {
|
||||
absoluteIndex = Integer.parseInt(streamPart.replace("index=", ""));
|
||||
|
Reference in New Issue
Block a user