How to call mkvmerge correctly from within a Java program?

Viewed 590

I have a few hundred episodes of an anime for which I would like to merge the subtitle and video files I have into one. I decided to write a short Java program to loop through the files and merge them. The video files are named Bleach1.mkv, the subtitles as Bleach1.srt. To avoid any potential issues with spaces the files are saved at the root of my C drive. The code I have written is below.

According to the documentation for mkvmerge, the appropriate command line call would be "mkvmerge -o remux_Bleach1.mkv Bleach1.mkv Bleach1.srt". I have confirmed and this does in fact work. I am running the java program from a different folder at the moment, so I used absolute paths for each of the files.

The command in my code prints out as being "[C:\Program Files\MKVToolNix\mkvmerge.exe, -o C:\Bleach\remux_Bleach_1.mkv C:\Bleach\Bleach_1.mkv C:\Bleach\Bleach_1.srt]"

The error message I am getting back from mkvmerge is "Error: no destination file name was given."

I don't understand exactly how the arguments are passed when using ProcessBuilder - what am I doing wrong?

import org.apache.commons.io.FilenameUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;


public class Main {

    private static String mkvmergePath = "C:\\Program Files\\MKVToolNix\\mkvmerge.exe";

    public static void mergeVideoAndSubtitles(final File folder, String videoExtension, String subtitleExtension) {
        for (final File fileEntry : folder.listFiles()) {
            if (fileEntry.isDirectory()) {
                listFilesForFolder(fileEntry, videoExtension, subtitleExtension);
                // for each mkv file that is found
            } else if (FilenameUtils.isExtension(fileEntry.getName(), videoExtension)) {
                String parentFolderPath = fileEntry.getParentFile().getPath();
                String baseName = FilenameUtils.getBaseName(fileEntry.getName());
                String outputFileAbsolutePath = parentFolderPath + "\\remux_" + fileEntry.getName();
                String inputVideoAbsolutePath = fileEntry.getAbsolutePath();
                String inputSubtitleAbsolutePath = parentFolderPath + "\\" + baseName + "." + subtitleExtension;
                String param1 = "-o " + outputFileAbsolutePath + " " + inputVideoAbsolutePath + " " + 
                        inputSubtitleAbsolutePath;
//                String param2 = "--default-track \"und\"";
//                String param3 = "--language 0:und " + baseName + "." + subtitleExtension + "\"";
//                System.out.println(param1);
//                System.out.println(param2);
//                System.out.println(param3);
                BufferedReader br = null;
                String line;
                try {
                List<String> list = new ArrayList<String>();
                list.add(mkvmergePath);
                list.add(param1);

                ProcessBuilder build = new ProcessBuilder(list);
                    System.out.println(build.command());
                                        Process process = build.start();

                    InputStream is = process.getInputStream();
                    InputStreamReader isr = new InputStreamReader(is);
                    br = new BufferedReader(isr);

                    while ((line = br.readLine()) != null) {
                        System.out.println(line);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }


            }
        }
    }

    public static void main(String[] args) {
        final File folder = new File("C:\\Bleach");
        mergeVideoAndSubtitles(folder, "mkv", "srt");
    }
}
1 Answers

I was writing a similar program in Go and ran into the same error. Apparently the spaces in the arguments (e.g. --language 0:eng) cause this problem. I tried to split those into separate arguments and it seems to work:

public static void main(String[] args) throws IOException {
    String mkvmerge = "C:\\Program Files\\MKVToolNix\\mkvmerge.exe";

    List<String> command = new ArrayList<String>();
    command.add(mkvmerge);

    command.add("--ui-language");
    command.add("en");
    command.add("--output");
    command.add("D:\\Dump\\test.mkv");
    command.add("--language");
    command.add("0:eng");
    command.add("--default-track");
    command.add("0:yes");
    command.add("--language");
    command.add("1:eng");
    command.add("--default-track");
    command.add("1:yes");
    command.add("--language");
    command.add("2:eng");
    command.add("D:\\Dump\\Game of Thrones\\Game.of.Thrones.S01.1080p.WEB-DL.DD5.1.H.264-SA89[rartv]\\Game.of.Thrones.S01E01.Winter.Is.Coming.1080p.WEB-DL.DD5.1.H.264-SA89.mkv");
    command.add("--sub-charset");
    command.add("0:UTF-8");
    command.add("--language");
    command.add("0:eng");
    command.add("D:\\Dump\\Game of Thrones\\Game.of.Thrones.S01.1080p.WEB-DL.DD5.1.H.264-SA89[rartv]\\Subs\\Game.of.Thrones.S01E01.Winter.Is.Coming.1080p.WEB-DL.DD5.1.H.264-SA89.srt");
    command.add("--track-order");
    command.add("0:0,0:1,0:2,1:0");

    ProcessBuilder builder = new ProcessBuilder(command);
    builder.redirectErrorStream(true);
    Process process = builder.start();

    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

For some reason this doesn't apply to spaces in paths. This works in Go as well.

Related