You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio JavaCV场景下ProcessBuilder.start()启动进程失败

问题根因

pb.start() 抛出空指针的直接原因是传入ProcessBuilder的命令数组中存在null元素,对应你代码里的问题就是ffmpeg可执行文件路径值为null。
你把Loader.load(org.bytedeco.ffmpeg.ffmpeg.class)写在了类的静态成员初始化位置:静态成员会在类加载阶段就执行初始化,这个时机App还没完成Context绑定,JavaCV的Loader必须依赖有效应用Context才能把内置的ffmpeg二进制解压到App私有目录、返回可执行文件路径,没有Context的情况下Loader加载失败直接返回null,最终传入命令数组触发空指针。
对照Android系统源码,ProcessBuilder类第1012行的逻辑就是遍历命令数组做非空校验,只要存在null元素就直接抛出NullPointerException,和你抓到的崩溃堆栈完全匹配。

修复方案
  • 把ffmpeg路径加载、命令数组组装的逻辑从静态成员区挪到子线程的执行逻辑里,保证调用Loader.load时App已经完成初始化,能给Loader提供有效Context
  • 输出文件不要直接写test.flv,Android 10以上开启分区存储后,App对存储根目录没有写权限,要把输出路径指定到App私有目录下,否则就算进程启动成功也会因为写文件失败直接退出
  • 删除重复的pb.redirectErrorStream(true)调用,进程启动后要单独开线程消费进程的标准输出流,否则缓冲区写满后会直接卡死ffmpeg进程
  • 删掉onCreate上无意义的@RequiresApi(api = Build.VERSION_CODES.O)注解,ProcessBuilder从API 1就存在,不需要Android 8.0以上版本支持,加这个注解反而可能在低版本系统上触发不必要的类加载错误
修正后的核心代码
public class MainActivity extends AppCompatActivity {
    static final int cols = 192;
    static final int rows = 108;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(t1).start();
    }

    private final Runnable t1 = () -> {
        Log.e("TAG", "工作线程启动");
        // 在线程内加载ffmpeg,此时App已完成初始化,Loader可正常获取Context
        String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
        // 输出路径指定到App私有缓存目录,规避存储权限问题
        String outputPath = new File(getCacheDir(), "test.flv").getAbsolutePath();
        String[] command = {ffmpeg,
                "-y",
                "-f", "rawvideo",
                "-vcodec", "rawvideo",
                "-pix_fmt", "bgr24",
                "-s", cols + "x" + rows,
                "-r", "10",
                "-i", "pipe:",
                "-c:v", "libx264",
                "-pix_fmt", "yuv420p",
                "-preset", "ultrafast",
                "-f", "flv",
                outputPath};

        ProcessBuilder pb = new ProcessBuilder(command).redirectErrorStream(true);
        try {
            Process process = pb.start();
            // 单独开线程读取ffmpeg输出日志,避免缓冲区阻塞
            new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        Log.d("FFMPEG_OUT", line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
            
            OutputStream writer = process.getOutputStream();
            Log.e("TAG", "ffmpeg进程启动成功");
            // 后续在这里往writer写入raw视频帧数据,处理完后调用process.waitFor()获取退出码
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("TAG", "进程启动失败", e);
        }
    };
}

额外提醒:如果后续要推流到RTMP服务器,记得给App加INTERNET权限,否则网络请求会被系统拦截。

内容的提问来源于stack exchange,提问作者xnok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 21:51:32