1

I want to launch an ffmpeg process from my code and then process the resulting output. The ffmpeg process will print out progress information and I want to send that info back to another application.

I am trying to use the ProcessBuilder API to achieve this. Based on this answer ProcessBuilder: Forwarding stdout and stderr of started processes without blocking the main thread I have tried the following code

@Service
class ProcessExecutorService(@Autowired private val configProps: AppConfigurationProperties) {

    private val logger: Logger = Logger.getLogger(ProcessExecutorService::class.qualifiedName)

    fun execute(command: List<String>): Int {
        logger.info("Executing command $command")

        val processBuilder = ProcessBuilder()
        processBuilder.command(command)
        processBuilder.directory(File(configProps.encoding.workDirectory))

        val process = processBuilder.start()
        Thread(StreamGobbler(process.inputStream, logger::info)).start()

        return process.waitFor()
    }

    inner class StreamGobbler(private val inputStream: InputStream, private val consumer: (String) -> Unit): Runnable {

        override fun run() {
            BufferedReader(InputStreamReader(inputStream) as Reader)
                    .lines()
                    .forEach(consumer)
        }

    }
}

But this doesn't log any output. When I change it to do

@Service
class ProcessExecutorService(@Autowired private val configProps: AppConfigurationProperties) {

    private val logger: Logger = Logger.getLogger(ProcessExecutorService::class.qualifiedName)

    fun execute(command: List<String>): Int {
        logger.info("Executing command $command")

        val processBuilder = ProcessBuilder()
        processBuilder.command(command)
        processBuilder.directory(File(configProps.encoding.workDirectory))
        processBuilder.inheritIO()

        val process = processBuilder.start()

        return process.waitFor()
    }
}

then I see the ffmpeg output, but I can't do anything useful with this output.

Any suggestions as to why the first approach might not be working?

  • It is Kotlin, isn't it? – i.bondarenko Sep 18 '19 at 13:49
  • In the first case, is the BufferedReader getting closed properly?  (If not, it might have unflushed data which could explain why you're not seeing it.)  Instead of the Java stream `lines()`, I'd try using the Kotlin extensions [forEachLine()](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-reader/for-each-line.html) or [useLines()](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-reader/use-lines.html), as those should automatically close the stream. – gidds Sep 18 '19 at 21:18
  • I tried that suggestion but I still don't see output. – the_witch_king_of_angmar Sep 20 '19 at 12:20

1 Answers1

0

If you run just "ffmpeg" the process object will write to the error stream so you need to log that stream also. The "ffmpeg -version" for example does not return in the error state so it should be captured by your StreamGobbler class.

Matej
  • 79
  • 1
  • 6