From cd71314df4978df05e370a3a07b4ed6459dcbb19 Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Fri, 8 May 2020 18:51:24 +0200 Subject: [PATCH] Makes a new abstract converter --- .../converter/AbstractConverter.java | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java diff --git a/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java new file mode 100644 index 0000000..92b0889 --- /dev/null +++ b/src/main/java/net/knarcraft/ffmpegconverter/converter/AbstractConverter.java @@ -0,0 +1,212 @@ +package net.knarcraft.ffmpegconverter.converter; + +import net.knarcraft.ffmpegconverter.streams.AudioStream; +import net.knarcraft.ffmpegconverter.streams.StreamObject; +import net.knarcraft.ffmpegconverter.streams.SubtitleStream; +import net.knarcraft.ffmpegconverter.streams.VideoStream; +import net.knarcraft.ffmpegconverter.utility.FileUtil; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Implements all methods which can be useful for any implementation of a converter. + */ +public abstract class AbstractConverter implements Converter { + String ffprobePath; + String ffmpegPath; + final boolean DEBUG = false; + String[] audioFormats; + String[] videoFormats; + + /** + * Initializes variables used by the abstract converter + */ + AbstractConverter() { + try { + audioFormats = FileUtil.readFileLines("audio_formats.txt"); + videoFormats = FileUtil.readFileLines("video_formats.txt"); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Gets all valid input formats for the converter + * @return

A list of valid input formats

+ */ + public abstract String[] getValidFormats(); + + /** + * Filters parsed streams into one of the stream types + * @param streams

A list of stream objects.

+ * @param codecType

The codec type of the streams to select.

+ * @param

The correct object type for the streams with the selected codec type.

+ * @return

A potentially shorter list of streams.

+ */ + @SuppressWarnings("unchecked") + static List filterStreamsByType(List streams, String codecType) { + Iterator i = streams.iterator(); + List newStreams = new ArrayList<>(); + while (i.hasNext()) { + StreamObject next = i.next(); + if (next.getCodecType().equals(codecType)) { + newStreams.add((G) next); + } + } + return newStreams; + } + + /** + * Filters and sorts audio streams according to chosen languages + * @param audioStreams

A list of audio streams.

+ * @param audioLanguages

A list of languages.

+ * @return

A list containing just audio tracks of chosen languages, sorted in order of languages.

+ */ + static List filterAudioStreams(List audioStreams, String[] audioLanguages) { + List filtered = new ArrayList<>(); + for (String language : audioLanguages) { + for (AudioStream stream : audioStreams) { + if ((stream.getLanguage() != null && stream.getLanguage().equals(language)) || language.equals("*")) { + filtered.add(stream); + } + } + //Tries to reduce execution time from n^2 + audioStreams.removeAll(filtered); + } + return filtered; + } + + /** + * Filters and sorts subtitle streams according to chosen languages + * @param subtitleStreams

A list of subtitle streams.

+ * @param subtitleLanguages

A list of languages.

+ * @param preventSignsAndSongs

Whether partial subtitles should be avoided.

+ * @return

A list containing just subtitles of chosen languages, sorted in order of languages.

+ */ + static List filterSubtitleStreams(List subtitleStreams, String[] subtitleLanguages, + boolean preventSignsAndSongs) { + List filtered = new ArrayList<>(); + //Go through languages. Select all subtitles of the language + for (String language : subtitleLanguages) { + for (SubtitleStream stream : subtitleStreams) { + String streamLanguage = stream.getLanguage(); + if (((streamLanguage != null && streamLanguage.equals(language)) || language.equals("*")) && + (!preventSignsAndSongs || stream.getIsFullSubtitle())) { + filtered.add(stream); + } + } + //Tries to reduce execution time from n^2 + subtitleStreams.removeAll(filtered); + } + return filtered; + } + + /** + * Escapes special characters which can cause trouble for ffmpeg + * @param fileName

The filename to escape.

+ * @return

A filename with known special characters escaped.

+ */ + private static String escapeSpecialCharactersInFileName(String fileName) { + return fileName.replaceAll("\\\\", "\\\\\\\\\\\\\\\\") + .replaceAll("'", "'\\\\\\\\\\\\\''") + .replaceAll("%", "\\\\\\\\\\\\%") + .replaceAll(":", "\\\\\\\\\\\\:") + .replace("]", "\\]") + .replace("[", "\\["); + } + + + + /** + * Adds audio to a command + * @param command

The command to add audio to.

+ * @param audioStream

The audio stream to be added.

+ * @param toStereo

Whether to convert the audio stream to stereo.

+ */ + void addAudioStreams(List 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"); + } + } + } + + /** + * Adds subtitles and video mapping to a command + * @param command

The list containing the rest of the command.

+ * @param subtitleStream

The subtitle stream to be used.

+ * @param videoStream

The video stream to be used.

+ * @param file

The file to convert.

+ */ + void addSubtitles(List 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; + } + + 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

The list containing the FFmpeg commands.

+ * @param subtitleStream

The subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private void addSubtitle(List 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); + } + + /** + * Adds image subtitle commands to a command list + * @param command

The list containing the FFmpeg commands.

+ * @param subtitleStream

The subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private void addInternalImageSubtitle(List 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

The list containing the FFmpeg commands.

+ * @param externalImageSubtitle

The external image subtitle stream to add.

+ * @param videoStream

The video stream to burn the subtitle into.

+ */ + private void addExternalImageSubtitle(List 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"); + } +}