记录一次使用 javacv + FFmpeg 处理mp4的moov 前置功能的过程

文章目录


mp4的moov前置可以支持视频渐进式播放

技术细节

依靠ffmpeg的功能实现,基础脚本

ffmpeg -i input.mp4 -movflags faststart -c copy output.mp4

如何使用java实现呢
需要在服务器事先安装ffmpeg组件,然后java执行脚本(保存到临时文件速度更快)


   try {
            // 定义 FFmpeg 命令参数(注意路径需根据实际情况调整)
            String[] command = {
                "ffmpeg",
                "-i", "input.mp4",
                "-movflags", "faststart",
                "-c", "copy",
                "output.mp4"
            };

            // 创建进程并执行命令
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            Process process = processBuilder.start();

            // 读取 FFmpeg 的输出流和错误流(避免进程阻塞)
            InputStream inputStream = process.getInputStream();
            InputStream errorStream = process.getErrorStream();
            printStream(inputStream, "Output");
            printStream(errorStream, "Error");

            // 等待命令执行完成
            int exitCode = process.waitFor();
            System.out.println("Exit Code: " + exitCode);

        } catch (Exception e) {
            e.printStackTrace();
        }

如果不想安装组件使用javacv怎么实现呢

FFmpegFrameGrabber grabber = null;
        FFmpegFrameRecorder recorder = null;

        try {
            // 1. 初始化 Grabber(读取输入文件)
            grabber = new FFmpegFrameGrabber("input.mp4");
            grabber.start();

            // 2. 初始化 Recorder(写入输出文件)
            recorder = new FFmpegFrameRecorder(
                "output.mp4",
                grabber.getImageWidth(),
                grabber.getImageHeight(),
                grabber.getAudioChannels()
            );

            // 3. 设置关键参数
            recorder.setFormat("mp4");
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 或直接复制流
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder.setSampleRate(grabber.getSampleRate());
            recorder.setFrameRate(grabber.getFrameRate());

            // 4. 启用 faststart(核心参数)
            recorder.setOption("movflags", "faststart");

            // 5. 开始录制
            recorder.start();

            // 6. 逐帧复制(直接复制流)
            while (true) {
                // 抓取帧(视频或音频)
                Object frame = grabber.grab();

                if (frame == null) {
                    break; // 结束循环
                }

                // 录制帧
                if (frame instanceof org.bytedeco.javacv.Frame) {
                    recorder.record((org.bytedeco.javacv.Frame) frame);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (recorder != null) {
                    recorder.close();
                }
                if (grabber != null) {
                    grabber.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

但经测试,速度不太理想,
如果既不想安装组件又希望速度更快呢?
直接从javacv的运行环境里拿出核心组件去执行脚本

try {
            // 1. 获取 FFmpeg 可执行文件的路径(通过 JavaCPP 加载)
            String ffmpeg = Loader.load(FFmpeg.class);

            // 2. 定义 FFmpeg 命令行参数(与终端命令一致)
            String[] cmd = {
                ffmpeg,
                "-i", "input.mp4",
                "-movflags", "faststart",
                "-c", "copy",
                "output.mp4"
            };

            // 3. 将参数转换为 PointerPointer 类型
            PointerPointer<String> command = new PointerPointer<>(cmd.length);
            for (int i = 0; i < cmd.length; i++) {
                command.put(i, cmd[i]);
            }

            // 4. 执行命令
            int exitCode = FFmpeg.exec(command);
            System.out.println("Exit Code: " + exitCode);

        } catch (Exception e) {
            e.printStackTrace();
        }

好像不错,本地经测试完美,
但是线上崩溃各种找不到相关依赖,
因为是容器化部署不想折腾怎么办?
再看下纯java的javacv方式为什么速度很慢,毕竟原理是相同的?
经查果然"6. 逐帧复制" 这一步拖后腿


		String inputFile = "input.mp4";
        String outputFile = "output.mp4";

        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
        grabber.start();

        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 0);
        recorder.setFormat("mp4");
        // 设置movflags为faststart,确保moov前置
        recorder.setOption("movflags", "faststart");
        // 复制视频和音频编解码参数
        recorder.setVideoCodec(grabber.getVideoCodec());
        recorder.setAudioCodec(grabber.getAudioCodec());
        // 设置媒体参数,如分辨率、采样率等
        recorder.setImageWidth(grabber.getImageWidth());
        recorder.setImageHeight(grabber.getImageHeight());
        recorder.setSampleRate(grabber.getSampleRate());
        recorder.setAudioChannels(grabber.getAudioChannels());
        recorder.setAudioBitrate(grabber.getAudioBitrate());

        // 启动录制器
        recorder.start(grabber.getFormatContext());

        AVPacket packet;
        while ((packet = grabber.grabPacket()) != null) {
            // 直接写入数据包,避免编解码
            recorder.recordPacket(packet);
            avcodec.av_packet_unref(packet); // 释放数据包资源
        }

        // 关闭资源
        recorder.close();
        grabber.close();

效果完美,又省事又快速

总结

通过直接写入数据包 ‌绕过解码/编码‌,直接操作已压缩的媒体流数据,使得处理速度接近单纯的“文件复制”,而 faststart 操作仅需调整元数据位置,因此速度极快。这是 FFmpeg/JavaCV 中优化媒体处理性能的经典方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值