LoginSignup
1
0

More than 3 years have passed since last update.

【Java】Processのstdinに入力する

Last updated at Posted at 2019-08-15

やりたかったこと

ffmpeg(ffprobe)では、以下のようにしてstdin(pipe:0)に入力したファイル情報をprobeすることができます。

cat a.mp4 | ffprobe -hide_banner -v error -print_format json -show_streams -i pipe:0

これを利用して、SpringBootでフロントからPOSTされたMultipartFileをファイルとして保存することなくffprobeの処理を行いたいと考えました。

環境

OS Java ffprobe
macOS Mojave 10.14.6 1.8.0_181 4.1.4

やり方

ざっくり以下のやり方で一応できます(問題点は後述、Processのstdinに入力することはできていると思います)。

ProcessOutputStreamに書き込みを行うことでファイル内容を渡せます。
org.apache.tika.io.IOUtilsを使っていますが、InputStreamに出力される処理結果を文字列に変換しているだけなので、自力実装するかライブラリを導入するかすればできると思います。

import org.apache.tika.io.IOUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

public static String probeMultipartFile(MultipartFile multipartFile) {
    List<String> commands = Arrays.asList(
            "ffprobe",
            // configure オプションやライブラリのバージョンを出力しない
            "-hide_banner",
            // ffprobe の内容だけ表示
            "-v", "error",
            // JSONで出力
            "-print_format", "json",
            // ファイルに含まれるビデオ・オーディオの情報を配列で出力
            "-show_streams",
            // パイプに入力される情報を処理する
            "-i", "pipe:0"
    );

    Process process = null;

    try {
        process = new ProcessBuilder(commands).start();

        // stdinへの書き込み
        OutputStream os = process.getOutputStream();
        os.write(multipartFile.getBytes());
        os.flush();
        os.close(); // waitFor前にcloseしないと終わらなくなるパターンが有った

        // 5秒待って終わらなければ処理を終了してnullを返す
        boolean result = process.waitFor(5, TimeUnit.SECONDS);
        if (!result) {
            process = null;
            return null;
        }
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    } finally {
        // IOException Broken pipeとなっていても取れている場合があるのでfinallyでreturnする
        if (process != null) {
            try {
                // processがnullでなければ標準出力を文字列としてreturn
                return IOUtils.toString(process.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

問題点

このやり方でファイルを処理してみたところ、手元にあったmp4やwmvファイルでjava.io.IOException: Broken pipeとなってしまう現象が起き、正常系で処理を完結することができませんでした(一応Broken pipeとなるパターンでもファイル指定で実行したprobe結果と同様のものは得られます)。
この問題はどうもファイル容量等には関係なく拡張子に依存して起きているように見えました。

調査は打ち切ってしまったので原因は把握できていませんが、どうもファイルによって以下のように反応が分かれるようです。

  • Processが適切なタイミングでOutputStreamを閉じる
  • OutputStreamが閉じず、書き込み待ちになってしまうため、Java側から閉じないと終了しなくなる
  • OutputStreamが異常なタイミングで閉じられてしまい、java.io.IOException: Broken pipeとなる

上2つは正常系内で処理が閉じますが、最後は正常系外で処理を行わなければならないようです。

これ以外にも、stdinから書き込んだ内容に対する処理結果がおかしくなる問題は度々発生しているようなので、大人しくファイルとして保存してから処理を行う方が当たり障りがなくていいのかなと思っています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0