【音频处理】java流式调用ffmpeg命令
今天发现一个ffmpeg的用法,用子进程直接从标准输入写入输入,就可以从标准流式输出获取转码结果。
这样的好处是不用去写ffmpeg的代码,只需要写对ffmpeg的命令、在输入输出的地方加缓存就能进行流式转码了,方便快捷。
package ffmpegPro;
import java.io.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class Main {public static void main(String[] argv) {ExecutorService executor = Executors.newFixedThreadPool(8);Future<?> f1 = executor.submit(()->{progress("D:\\data\\audio\\a_out.wav","D:\\data\\audio\\a_output.pcm", executor);});Future<?> f2 = executor.submit(()->{progress("D:\\data\\audio\\b_out.wav","D:\\data\\audio\\b_output.pcm", executor);});try {f1.get();f2.get();} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executor.shutdown();}public static void progress(String inputPath, String outputPath, ExecutorService executor) {try {// 1. 定义 FFmpeg 命令(示例:H264 → VP9,实时转码)String[] ffmpegCmd = {"ffmpeg","-loglevel", "error","-hide_banner", "-nostats", //关闭日志"-f", "wav", // 输入格式"-i", "pipe:0", // 从标准输入读取"-f", "s16le", // 输出格式"-acodec", "pcm_s16le","-ar", "8000", // 16kHz"-ac", "1", // 单声道"pipe:1" // 输出到标准输出};// 2. 启动 FFmpeg 进程ProcessBuilder pb = new ProcessBuilder(ffmpegCmd);Process process = pb.start();// 3. 获取输入/输出流OutputStream ffmpegStdin = process.getOutputStream(); // FFmpeg 的 stdinInputStream ffmpegStdout = process.getInputStream(); // FFmpeg 的 stdoutInputStream ffmpegStderr = process.getErrorStream(); // FFmpeg 的 stderr(日志)// 4. 异步读取转码后的数据(防止阻塞)// 线程1:读取 FFmpeg 的输出(转码后的数据)executor.submit(() -> {byte[] buffer = new byte[8192];int bytesRead;try {FileOutputStream pcmFile = new FileOutputStream(outputPath);while ((bytesRead = ffmpegStdout.read(buffer)) != -1) {// 处理转码后的数据(示例:写入文件或推送到网络)System.out.println("收到转码数据,长度: " + bytesRead);pcmFile.write(buffer, 0, bytesRead);}} catch (IOException e) {e.printStackTrace();}});// 线程2:打印 FFmpeg 的错误日志(调试用)executor.submit(() -> {try (BufferedReader reader = new BufferedReader(new InputStreamReader(ffmpegStderr))) {String line;while ((line = reader.readLine()) != null) {System.err.println("[FFmpeg] " + line);}} catch (IOException e) {e.printStackTrace();}});// 5. 模拟向 FFmpeg 发送原始数据(示例:从文件读取)try (InputStream rawVideoStream = new FileInputStream(inputPath)) {byte[] buffer = new byte[8192];int bytesRead;while ((bytesRead = rawVideoStream.read(buffer)) != -1) {ffmpegStdin.write(buffer, 0, bytesRead);System.out.println("已发送原始数据,长度: " + bytesRead);}ffmpegStdin.close(); // 关闭输入流,通知 FFmpeg 结束} catch (IOException e) {e.printStackTrace();}// 6. 等待 FFmpeg 结束try {int exitCode = process.waitFor();System.out.println("FFmpeg 进程结束,退出码: " + exitCode);} catch (InterruptedException e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}}
}