【音频处理】java流式调用ffmpeg命令

今天发现一个ffmpeg的用法,用子进程直接从标准输入写入输入,就可以从标准流式输出获取转码结果。

这样的好处是不用去写ffmpeg的代码,只需要写对ffmpeg的命令、在输入输出的地方加缓存就能进行流式转码了,方便快捷。

但是也有坏处,在开始的时候会引入几百ms的延时,到某个时间集体输出,后面的时间就正常了。

java 复制代码
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 的 stdin
            InputStream ffmpegStdout = process.getInputStream(); // FFmpeg 的 stdout
            InputStream 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();
        }
    }
}
相关推荐
我喜欢就喜欢3 小时前
# MVStudy 交互式视频培训系统:让每一段视频都能“与人对话“
音视频
Joy T5 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
EasyDSS7 小时前
连锁扩张管控难?一套视频会议/直播/点播/集群对讲EasyDSS音视频云平台打通门店全流程数字化
网络·音视频·easydss
m0_587383008 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·8 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
阿明副业观察8 小时前
2026年免费AI视频生成工具功能深度解析:有无水印、中文语音及商业用途考量
大数据·人工智能·ai作画·aigc·音视频·ai写作
代码不停9 小时前
自动化测试(java)
java·开发语言·selenium·自动化
国产化创客10 小时前
ESP32音频/语音技术(4)AudioTools 库及转发播放程序分析
物联网·音视频·esp32
鬼手点金11 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
martindelophy11 小时前
浏览器视频导出太慢怎么优化?一次 WebCodecs 实践:8 分钟视频耗时降低 31%
性能优化·音视频·优化