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

结合BufferedImage与WAV音频生成视频时FFmpegFrameRecorder报错无音频输出流

问题:JavaCV结合BufferedImage与音频生成视频报错"No audio output stream"

尝试用JavaCV将BufferedImage数组与WAV音频结合生成MP4视频,编写代码如下:

public static void compileVideo(BufferedImage[] bufferedImages, String audioPath , String savePath) throws FFmpegFrameRecorder.Exception, FrameGrabber.Exception {
        FrameGrabber grabber = new FFmpegFrameGrabber(audioPath); // need this for audio
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(savePath, 316, 100, grabber.getAudioChannels());
        FFmpegLogCallback.set();

        
        //grabber.setAudioChannels(grabber.getAudioChannels());
        System.out.println(grabber.getAudioChannels());
        // set up the recorder video info
        recorder.setFrameRate(60);
        recorder.setFormat("mp4");
        recorder.setVideoQuality(0); // highest quality

        // set recorder audio info
        recorder.setSampleFormat(grabber.getSampleFormat());
        recorder.setSampleRate(grabber.getSampleRate());
        recorder.setAudioQuality(0); // highest quality
        
        grabber.start();
        recorder.start();

        for (BufferedImage image: bufferedImages) {
            recorder.record(Java2DFrameUtils.toFrame(image)); // convert buffered image to frame and record it
            recorder.record(grabber.grabFrame()); //TODO: Grab multiple audio frames because there are more audio frames than video frames (BufferedImages)
        }
        recorder.stop();
        grabber.stop();
    }

运行时抛出错误:

Exception in thread "main" org.bytedeco.javacv.FFmpegFrameRecorder$Exception: No audio output stream (Is audioChannels > 0 and has start() been called?) (For more details, make sure FFmpegLogCallback.set() has been called.)
    at org.bytedeco.javacv.FFmpegFrameRecorder.recordSamples(FFmpegFrameRecorder.java:1154)
    at org.bytedeco.javacv.FFmpegFrameRecorder.record(FFmpegFrameRecorder.java:1021)
    at org.bytedeco.javacv.FFmpegFrameRecorder.record(FFmpegFrameRecorder.java:1010)
    at com.Lazzza.imageToVideo.compileVideo(imageToVideo.java:31)
    at com.Lazzza.Main.main(Main.java:22)

已知grabber.getAudioChannels()返回值为2,音频文件为WAV格式。


解决方案

1. 调整初始化顺序:先启动音频抓取器再创建录制器

当前代码在grabber.start()之前就调用grabber.getAudioChannels()等参数,虽然返回了2,但部分音频内部参数可能未正确加载。必须先启动抓取器,再用其参数初始化录制器,确保所有音频元数据加载完成。

2. 显式设置音频编码器

MP4容器默认需要AAC编码的音频,而WAV是无压缩格式,需显式指定音频编码器,避免音频流初始化失败:

recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

3. 修复音视频帧同步逻辑

原代码每帧视频对应一帧音频,会导致音视频时长不匹配。需根据视频总时长计算需要读取的音频帧数量,保证两者时长一致。


修改后的完整代码

import org.bytedeco.ffmpeg.avcodec;
import org.bytedeco.javacv.*;

import java.awt.image.BufferedImage;

public class ImageToVideo {
    public static void compileVideo(BufferedImage[] bufferedImages, String audioPath, String savePath) throws FFmpegFrameRecorder.Exception, FrameGrabber.Exception {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(audioPath);
        FFmpegLogCallback.set();

        // 先启动音频抓取器,确保所有音频参数加载完成
        grabber.start();

        // 用已加载的音频参数初始化录制器
        int videoWidth = 316;
        int videoHeight = 100;
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(savePath, videoWidth, videoHeight, grabber.getAudioChannels());

        // 视频配置
        recorder.setFrameRate(60);
        recorder.setFormat("mp4");
        recorder.setVideoQuality(0);

        // 音频配置:显式设置编码器,MP4需AAC编码
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
        recorder.setSampleFormat(grabber.getSampleFormat());
        recorder.setSampleRate(grabber.getSampleRate());
        recorder.setAudioQuality(0);

        recorder.start();

        // 计算视频总时长(秒)
        double videoTotalDuration = (double) bufferedImages.length / recorder.getFrameRate();
        // 计算需要读取的音频样本总数
        long totalAudioSamples = (long) (videoTotalDuration * grabber.getSampleRate() * grabber.getAudioChannels());
        long recordedSamples = 0;

        // 先写入所有视频帧
        for (BufferedImage image : bufferedImages) {
            recorder.record(Java2DFrameUtils.toFrame(image));
        }

        // 读取并写入对应时长的音频帧
        Frame audioFrame;
        while (recordedSamples < totalAudioSamples && (audioFrame = grabber.grabFrame(false, true, false, false)) != null) {
            recorder.record(audioFrame);
            // 累加已录制的样本数
            recordedSamples += audioFrame.samples.length * grabber.getAudioChannels();
        }

        recorder.stop();
        grabber.stop();
        recorder.release();
        grabber.release();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:14:59