Adds Dolby Vision compatible HEVC converter
All checks were successful
KnarCraft/FFmpegConvert/pipeline/head This commit looks good

This commit is contained in:
Kristian Knarvik 2025-06-12 00:33:54 +02:00
parent 60830654f7
commit 4159210f76
4 changed files with 115 additions and 2 deletions

View File

@ -125,7 +125,7 @@
<dependency> <dependency>
<groupId>commons-beanutils</groupId> <groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId> <artifactId>commons-beanutils</artifactId>
<version>1.9.4</version> <version>1.11.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -11,6 +11,7 @@ import net.knarcraft.ffmpegconverter.converter.EmbeddedCaptionStripper;
import net.knarcraft.ffmpegconverter.converter.LetterboxCropper; import net.knarcraft.ffmpegconverter.converter.LetterboxCropper;
import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder; import net.knarcraft.ffmpegconverter.converter.MKVToMP4Transcoder;
import net.knarcraft.ffmpegconverter.converter.MkvH264Converter; import net.knarcraft.ffmpegconverter.converter.MkvH264Converter;
import net.knarcraft.ffmpegconverter.converter.MkvH265Converter;
import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter; import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter;
import net.knarcraft.ffmpegconverter.converter.StreamOrderConverter; import net.knarcraft.ffmpegconverter.converter.StreamOrderConverter;
import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed; import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed;
@ -104,7 +105,8 @@ public class FFMpegConvert {
12. Letterbox cropper 12. Letterbox cropper
13. Video's Audio to vorbis converter 13. Video's Audio to vorbis converter
14. Audio from video extractor 14. Audio from video extractor
15. Embedded caption stripper""", 1, 15, Integer.MIN_VALUE); 15. Embedded caption stripper
16. MKV to HEVC HQ converter""", 1, 16, Integer.MIN_VALUE);
return switch (choice) { return switch (choice) {
case 1 -> generateWebAnimeConverter(); case 1 -> generateWebAnimeConverter();
@ -123,6 +125,7 @@ public class FFMpegConvert {
case 14 -> new AudioExtractor(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output audio extension>", case 14 -> new AudioExtractor(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output audio extension>",
"mp3"), getChoice("<stream to extract>", 0, 1000, 0)); "mp3"), getChoice("<stream to extract>", 0, 1000, 0));
case 15 -> new EmbeddedCaptionStripper(FFPROBE_PATH, FFMPEG_PATH); case 15 -> new EmbeddedCaptionStripper(FFPROBE_PATH, FFMPEG_PATH);
case 16 -> new MkvH265Converter(FFPROBE_PATH, FFMPEG_PATH);
default -> null; default -> null;
}; };
} }

View File

@ -0,0 +1,94 @@
package net.knarcraft.ffmpegconverter.converter;
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
import net.knarcraft.ffmpegconverter.container.StreamProbeResult;
import net.knarcraft.ffmpegconverter.converter.module.ConverterModule;
import net.knarcraft.ffmpegconverter.converter.module.DebugModule;
import net.knarcraft.ffmpegconverter.converter.module.DolbyVisionModule;
import net.knarcraft.ffmpegconverter.converter.module.ModuleExecutor;
import net.knarcraft.ffmpegconverter.converter.module.mapping.MapAllModule;
import net.knarcraft.ffmpegconverter.converter.module.output.CopyAudioModule;
import net.knarcraft.ffmpegconverter.converter.module.output.CopySubtitlesModule;
import net.knarcraft.ffmpegconverter.converter.module.output.FastStartModule;
import net.knarcraft.ffmpegconverter.converter.module.output.SetOutputFileModule;
import net.knarcraft.ffmpegconverter.converter.module.output.SetQualityModule;
import net.knarcraft.ffmpegconverter.converter.module.output.SetVideoCodecModule;
import net.knarcraft.ffmpegconverter.streams.AudioStream;
import net.knarcraft.ffmpegconverter.streams.SubtitleStream;
import net.knarcraft.ffmpegconverter.streams.VideoStream;
import net.knarcraft.ffmpegconverter.utility.FFMpegHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* A converter solely for the purpose of converting video streams of MKV files into h264
*/
public class MkvH265Converter extends AbstractConverter {
/**
* Initializes variables used by the abstract converter
*
* @param ffprobePath <p>Path/command to ffprobe.</p>
* @param ffmpegPath <p>Path/command to ffmpeg.</p>
*/
public MkvH265Converter(String ffprobePath, String ffmpegPath) {
super("mkv");
this.ffprobePath = ffprobePath;
this.ffmpegPath = ffmpegPath;
}
@Override
@NotNull
public List<String> getValidFormats() {
return List.of("mkv", "mp4", "wmv");
}
@Override
@Nullable
public FFMpegCommand generateConversionCommand(@NotNull String executable, @NotNull StreamProbeResult probeResult,
@NotNull String outFile) {
FFMpegCommand command = FFMpegHelper.getFFMpegGeneralFileCommand(executable, probeResult.parsedFiles());
List<ConverterModule> modules = new ArrayList<>();
if (this.debug) {
modules.add(new DebugModule());
}
// Map video if present
List<VideoStream> videoStreams = probeResult.getVideoStreams();
if (!videoStreams.isEmpty()) {
modules.add(new FastStartModule());
modules.add(new MapAllModule<>(videoStreams));
modules.add(new SetVideoCodecModule("hevc"));
modules.add(new SetQualityModule(18, "medium"));
modules.add(new DolbyVisionModule());
}
// Map audio if present
List<AudioStream> audioStreams = probeResult.getAudioStreams();
if (!audioStreams.isEmpty()) {
modules.add(new MapAllModule<>(audioStreams));
setOutputIndexes(audioStreams);
modules.add(new CopyAudioModule());
}
// Map subtitles if present
List<SubtitleStream> subtitleStreams = probeResult.getSubtitleStreams();
if (!subtitleStreams.isEmpty()) {
modules.add(new MapAllModule<>(subtitleStreams));
modules.add(new CopySubtitlesModule());
}
// Map any fonts, cover images or similar
modules.add(new MapAllModule<>(probeResult.getOtherStreams()));
// Set output file and execute
modules.add(new SetOutputFileModule(outFile));
new ModuleExecutor(command, modules).execute();
return command;
}
}

View File

@ -0,0 +1,16 @@
package net.knarcraft.ffmpegconverter.converter.module;
import net.knarcraft.ffmpegconverter.container.FFMpegCommand;
import org.jetbrains.annotations.NotNull;
/**
* A module for enabling dolby vision encoding
*/
public class DolbyVisionModule implements ConverterModule {
@Override
public void addArguments(@NotNull FFMpegCommand command) {
command.addOutputFileOption("-dolbyvision", "1", "-x265-params", "\"vbv-bufsize=160000:vbv-maxrate=160000\"");
}
}