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>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
<version>1.11.0</version>
<scope>compile</scope>
</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.MKVToMP4Transcoder;
import net.knarcraft.ffmpegconverter.converter.MkvH264Converter;
import net.knarcraft.ffmpegconverter.converter.MkvH265Converter;
import net.knarcraft.ffmpegconverter.converter.MkvH265ReducedConverter;
import net.knarcraft.ffmpegconverter.converter.StreamOrderConverter;
import net.knarcraft.ffmpegconverter.converter.SubtitleEmbed;
@ -104,7 +105,8 @@ public class FFMpegConvert {
12. Letterbox cropper
13. Video's Audio to vorbis converter
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) {
case 1 -> generateWebAnimeConverter();
@ -123,6 +125,7 @@ public class FFMpegConvert {
case 14 -> new AudioExtractor(FFPROBE_PATH, FFMPEG_PATH, getChoice("<output audio extension>",
"mp3"), getChoice("<stream to extract>", 0, 1000, 0));
case 15 -> new EmbeddedCaptionStripper(FFPROBE_PATH, FFMPEG_PATH);
case 16 -> new MkvH265Converter(FFPROBE_PATH, FFMPEG_PATH);
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\"");
}
}