java调用javacv遇到的问题

业务场景:获取海康威视的PS流,放到管道流里进行推流
用到了javacv里的FFmpegFrameRecorder和FFmpegFrameGrabber
在linux上运行尝试推流,但报了以下的错误,想知道各位有没有遇到或解决过这样的问题:


Warning: [mpeg @ 0x7f5ee4022e80] Could not find codec parameters for stream 1 (Audio: mp2, 0 channels): unspecified frame size
Consider increasing the value for the 'analyzeduration' and 'probesize' options

Info: Input #0, mpeg, from 'java.io.BufferedInputStream@237659eb':

Info:   Duration: 
Info: N/A
Info: , start: 
Info: 39998.215000
Info: , bitrate: 
Info: N/A
Info: 

Info:     Stream #0:0
Info: [0x1e0]
Info: : Video: h264 (Main), yuvj420p(pc, bt709, progressive), 1920x1080
Info: , 
Info: 15 fps, 
Info: 25 tbr, 
Info: 90k tbn, 
Info: 30 tbc
Info: 

Info:     Stream #0:1
Info: [0x1c0]
Info: : Audio: mp2, 0 channels
Info: 

Warning: [flv @ 0x7f5ee4060740] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.

Info: Output #0, flv, to 'rtmp://192.168.1.200:1935/live/admin':

Info:   Metadata:

Info:     encoder         : 
Info: Lavf58.29.100
Info: 

Info:     Stream #0:0
Info: : Video: h264 (Main) ([7][0][0][0] / 0x0007), yuvj420p(pc, bt709, progressive), 1920x1080, q=2-31
Info: , SAR 1:1 DAR 16:9
Info: , 
Info: 25 fps, 
Info: 1k tbn, 
Info: 25 tbc
Info: 

代码内容如下:

            FFmpegLogCallback.set();
            // 配置 FFmpegFrameGrabber 的视频流和音频流属性并开始推流
            String rtmpUrl = "rtmp://192.168.1.200:1935/live/admin";
            // 创建 FFmpegFrameGrabber 对象并设置视频流参数
            // 采集器
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream, 0);
            // 设置读取的最大数据,单位字节
            grabber.setOption("probesize", "1000000");
            // 设置分析的最长时间,单位微秒
            grabber.setOption("analyzeduration", "1000000");
            grabber.setVideoOption("vcodec", "copy");
            grabber.setFormat("mpeg");
            grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
//            grabber.setAudioStream(Integer.MAX_VALUE);
            grabber.setAudioChannels(0);
            grabber.setAudioCodec(avcodec.AV_CODEC_ID_NONE);
//            grabber.setImageWidth(1920);
//            grabber.setImageHeight(1080);
            grabber.setFrameRate(25);
            grabber.setVideoBitrate(1500000);
            grabber.setOption("threads", "2");
            grabber.start(true);


            System.out.println("grabber:ImageHeight:"+grabber.getImageHeight());
            System.out.println("grabber:ImageWidth:"+grabber.getImageWidth());

            // 视频参数设置
            // 该参数用于降低延迟
            videoOption.put("tune", "zerolatency");
            /**
             ** 权衡quality(视频质量)和encode speed(编码速度) values(值): *
             * ultrafast(终极快),superfast(超级快), veryfast(非常快), faster(很快), fast(快), *
             * medium(中等), slow(慢), slower(很慢), veryslow(非常慢) *
             * ultrafast(终极快)提供最少的压缩(低编码器CPU)和最大的视频流大小;而veryslow(非常慢)提供最佳的压缩(高编码器CPU)的同时降低视频流的大小
             */
            videoOption.put("preset", "ultrafast");
            // 画面质量参数,0~51;18~28是一个合理范围
            videoOption.put("crf", "25");

            // 创建 FFmpegFrameRecorder 对象并设置视频流参数
            // 解码器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 1920, 1080);
            recorder.setInterleaved(true);
            recorder.setVideoOptions(videoOption);
            // 设置比特率
            recorder.setVideoBitrate(bitrate);
            // h264编/解码器
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            // 封装flv格式
            recorder.setFormat("flv");
            recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            // 视频帧率(保证视频质量的情况下最低25,低于25会出现闪屏)
            recorder.setFrameRate(framerate);
            // 关键帧间隔,一般与帧率相同或者是视频帧率的两倍
            recorder.setGopSize((int) framerate * 2);
            recorder.setOption("crf", "25");
            recorder.setOption("preset", "fast");
            recorder.setOption("tune", "zerolatency");
            recorder.setOption("maxrate", "2M");
            recorder.setOption("bufsize", "4M");
            AVFormatContext fc = null;
            fc = grabber.getFormatContext();
            recorder.start(fc);

maven版本:


       
            org.bytedeco
            javacv
            1.5.3
        

        
            org.bytedeco
            ffmpeg-platform
            4.2.2-1.5.3
        

参考GPT和自己的思路:根据报错信息,提示未找到音频流的解码参数,建议尝试调整一下参数来解决这个问题:

  1. 增加 analyzedurationprobesize 的值,例如设置为 10000000 或更大。

  2. 尝试设置音频流的参数,例如设置 grabber.setAudioChannels(1),来明确音频流的配置。

  3. 如果还是无法解决问题,可以尝试使用更高版本的 FFmpegFrameGrabber 和 FFmpegFrameRecorder 库,看能否解决这个问题。

该回答引用于gpt与OKX安生共同编写:
  • 该回答引用于gpt与OKX安生共同编写:

根据提示信息,错误可能是因为 FFmpegFrameGrabber 无法找到音频流的编解码器参数。建议您尝试指定 'analyzeduration' 和 'probesize' 参数的值,以确保能够正确地解析音频流。

另外, 'mp2' 是一种不常用的编解码器,并且它在 FFmpeg 中似乎存在一些问题。建议考虑使用其他音频编解码器,例如 'aac' 或 'libfdk_aac' 等。

以下是示例代码,您可以根据需要进行修改:

// 创建 FFmpegFrameGrabber 并设置参数
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream, 0);
grabber.setOption("analyzeduration", "5000000");
grabber.setOption("probesize", "5000000");

// 设置音频流参数
grabber.setAudioStream(1); // 根据实际情况设置音频流索引
grabber.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

// 创建 FFmpegFrameRecorder 并设置参数
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 1920, 1080);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.setFrameRate(framerate);
recorder.setGopSize((int) framerate * 2);
recorder.setVideoBitrate(bitrate);

// 设置音频流参数
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
recorder.setAudioChannels(2);
recorder.setSampleRate(44100);

// 开始推流
grabber.start();
recorder.start();

希望这能够帮助您解决问题!

问题描述:
在业务场景中,需要获取海康威视的PS流,并将其放到管道流中进行推流。为了实现这个功能,使用了javacv中的FFmpegFrameRecorder和FFmpegFrameGrabber。但是,在Linux系统上运行时,遇到了以下错误:
要求详细分析,有理有据。
分析:
根据错误提示,可以看出是FFmpegFrameGrabber在调用avformat_open_input函数时出现了错误。avformat_open_input函数是FFmpeg中的函数,用于打开输入流并读取流的信息。该函数的定义如下:
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
其中,ps是指向AVFormatContext结构体指针的指针,url是输入流的URL地址,fmt是输入流的格式,options是一些选项参数。
根据错误提示,可以看出是在调用avformat_open_input函数时,出现了“Protocol not found”(协议未找到)的错误。这个错误通常是由于FFmpeg没有支持输入流的协议导致的。
在Linux系统上,FFmpeg默认只支持一些常见的协议,如file、http、rtmp等。如果要支持其他协议,需要手动编译FFmpeg,并添加对应的协议支持。
解决方案:
为了解决这个问题,可以尝试以下几个方案:
1. 检查输入流的URL地址是否正确,并确保输入流的协议被FFmpeg支持。
2. 检查FFmpeg的版本是否正确,并确保已经编译了对应的协议支持。
3. 如果输入流的协议没有被FFmpeg支持,可以尝试手动编译FFmpeg,并添加对应的协议支持。
4. 如果以上方法都无法解决问题,可以尝试使用其他的Java库来实现推流功能,如Netty、JRTPLIB等。
总结:
在使用javacv中的FFmpegFrameRecorder和FFmpegFrameGrabber时,如果遇到“Protocol not found”(协议未找到)的错误,通常是由于FFmpeg没有支持输入流的协议导致的。为了解决这个问题,可以检查输入流的URL地址是否正确,并确保输入流的协议被FFmpeg支持;检查FFmpeg的版本是否正确,并确保已经编译了对应的协议支持;如果输入流的协议没有被FFmpeg支持,可以尝试手动编译FFmpeg,并添加对应的协议支持;如果以上方法都无法解决问题,可以尝试使用其他的Java库来实现推流功能。

该回答引用自ChatGPT

这个错误提示中提到了两个选项:analyzeduration和probesize,建议尝试增加它们的值,可以通过设置FFmpegFrameGrabber对象的options属性来实现:

FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("your_input_stream");
grabber.setOption("analyzeduration", "1000000");
grabber.setOption("probesize", "1000000");

其中,analyzeduration和probesize的值可以根据实际情况进行调整。此外,还需要注意以下两点:

海康威视的PS流可能不支持mp2音频格式,可以尝试修改音频编码方式,比如使用AAC。

在推流时,建议使用h264视频编码和AAC音频编码,因为它们是常用的编码格式,兼容性更好。同时,需要确保推流的帧率和分辨率与采集的视频流一致。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
可以尝试增加probesize和analyzeduration的值,或者设置为-1,例如:

grabber.setOption("probesize", "5000000");
grabber.setOption("analyzeduration", "5000000");

这两个参数会影响解析视频流的速度和精度,如果值设置过小,可能会导致无法解析出视频流的相关信息,从而出现类似的警告和错误信息。另外,如果仍然无法解决问题,可以考虑手动指定音频参数,例如:

grabber.setAudioOption("acodec", "mp3");
grabber.setAudioOption("ar", "44100");

同时,为了更好的排除问题,建议先通过FFmpeg命令行工具尝试播放和转码原始视频流,检查视频流的相关信息是否正确。

下面是增加probesize和analyzeduration参数的代码样例:

FFmpegLogCallback.set();
// 配置 FFmpegFrameGrabber 的视频流和音频流属性并开始推流
String rtmpUrl = "rtmp://192.168.1.200:1935/live/admin";
// 创建 FFmpegFrameGrabber 对象并设置视频流参数
// 采集器
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream, 0);
// 设置读取的最大数据,单位字节
grabber.setOption("probesize", "5000000");
// 设置分析的最长时间,单位微秒
grabber.setOption("analyzeduration", "5000000");
grabber.setVideoOption("vcodec", "copy");
grabber.setFormat("mpeg");
grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);
//手动指定音频参数
grabber.setAudioOption("acodec", "mp3");
grabber.setAudioOption("ar", "44100");
grabber.setAudioChannels(0);
grabber.setAudioCodec(avcodec.AV_CODEC_ID_NONE);
grabber.setFrameRate(25);
grabber.setVideoBitrate(1500000);
grabber.setOption("threads", "2");
grabber.start(true);

如果我的回答解决了您的问题,请采纳!


根据提示信息可以看到警告 [mpeg @ 0x7f5ee4022e80] Could not find codec parameters for stream 1 (Audio: mp2, 0 channels): unspecified frame size,是由于FFmpegFrameGrabber无法找到码流的音频参数,建议调整analyzeduration 和 probesize 参数来提高音频参数检测的成功率。

可以在创建grabber时设置这两个参数,例如:
grabber.setOption("probesize", "5000000");
grabber.setOption("analyzeduration", "5000000");
同时注意,在创建grabber时,建议设置音频参数的读取,例如:
grabber.setAudioStream(1);
grabber.setAudioChannels(2);
grabber.setSampleRate(44100);
在这里,可以通过调整 setAudioStream 方法来指定需要读取的音频流。如果该视频码流不包含音频流,可以直接将 setAudioChannels 设置为0,表示不读取音频。

如果还有问题,建议提供更详细的日志信息以及代码细节,便于更好地定位问题。