共计 2773 个字符,预计需要花费 7 分钟才能阅读完成。
1. 首先说了一下为什么要用 ISUP 协议来取流
ISUP 主要就是用来解决摄像头没有公网 ip 的情况, 如果摄像头或者所在局域网的路由器有公网 ip 的话,其实采用 rtsp 直接取流是最方便也是性能最好的,但是项目的摄像头没有公网 IP 所以被迫使用 ISUP,ISUP 是海康自己的协议,海康官网是有对应的 DEMO,我主要根据他们的 java 版本的 demo 进行改造海康 DEMO 地址,
2. 具体实现
首先得设置摄像头编码格式 H.264
音频编码要改成 ACC
还需要设置一下 ehome 协议,ip 地址填取流服务器的地址,本地测试就填本机 ip 地址就行,这个密钥要和 ISUP 服务器的密钥一样 – 注意上述配置修改完后要点击保存才会生效
3. 核心代码_取流并再推流到 nginx-rtmp
代码(完整服务源码地址)
thread = new Thread(() -> {
try {
// 打印 FFmpeg 日志可以帮助确定输入流的音视频编码格式帧率等信息, 需要时可以取消注释
// avutil.av_log_set_level(avutil.AV_LOG_INFO);
// FFmpegLogCallback.set();
grabber = new FFmpegFrameGrabber(inputStream, 0);
grabber.setOption("rtsp_transport", "tcp"); // 设置 RTSP 传输协议为 TCP
// grabber.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置视频编解码器为 H.264
// grabber.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 设置音频编解码器为 ACC
grabber.setFormat("mpeg"); // 设置格式为 MPEG
grabber.start();
// 获取输入格式上下文
AVFormatContext ifmt_ctx = grabber.getFormatContext();
log.info("视频宽度:" + grabber.getImageWidth());
log.info("视频高度:" + grabber.getImageHeight());
log.info("音频通道:" + grabber.getAudioChannels());
recorder = new FFmpegFrameRecorder(pushAddress, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
recorder.setInterleaved(true); // 设置音视频交织方式
recorder.setVideoOption("crf", "23"); // 画质参数
recorder.setFormat("flv"); // 设置推流格式为 FLV
// recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 设置音频编码器为 AAC
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 设置视频编码器为 H.264
recorder.setSampleRate(grabber.getSampleRate()); // 设置音频采样率
recorder.setFrameRate(grabber.getFrameRate()); // 设置视频帧率
recorder.setVideoBitrate(3000000); // 设置视频比特率为 3 Mbps(根据需要调整)// recorder.setVideoQuality(0); // 设置视频质量参数(0 为最高质量)// recorder.setAudioQuality(0); // 设置音频质量参数(0 为最高质量)recorder.setGopSize((int) (grabber.getFrameRate()*2));
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
recorder.setVideoOption("tune", "zerolatency"); // 降低编码延迟
recorder.setVideoOption("preset", "superfast"); // 提升编码速度
recorder.start(ifmt_ctx); // 启动推流器
Frame frame;
count=0;
long t1 = System.currentTimeMillis();
AVPacket packet;
while (running &&(packet = grabber.grabPacket()) != null) {
count++;
recorder.recordPacket(packet);
// if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {
// break;
// }
if (count % 100 == 0) {
// 处理每帧
log.info("packet 推流帧 ====>" + count);
}
}
}
4. 结语
搭建 nginx-rtmp 可以看看搭建 nginx-rtmp, 搭建完成后,nginx-rtmp 可以提供 HLS 的 url 以供前端播放, 也可以使用 rtmp 协议的 url 播放,只不过 rtmp 协议的现在的浏览器基本不支持播放了
本地实测延迟大概在 5 秒内,一般 3 秒左右
其实刚开始用的是别人代码,虽然也能实现视频预览,但是光一个摄像头进行推流就占了 40% 的 cpu,性能消耗太多了,后来改了一下 javaCV 的配置,现在 4 核 8G 的服务器实测开启一个摄像头推流仅占 1%cpu,cpu 占用下降了不少
有些问题,
1. 如果你拉的流解析没有音频通道(为 0)的话,nginx-rtmp 是不会生成.m3u8 和 ts 文件的,无法生成播放 hls 的文件那么就只能用 rtmp 协议的 url 播放了 或者 手动添加音频。。。
2. 如果运行出现 Pipe closed 异常,那么你应该找异常栈栈顶出现的异常,栈顶异常会导致流被关闭,但主线程会一直向流里面写入数据,所以会导致出现一大串 Pipe closed
3. 源码中依赖的 lib 文件里面动态链接库最好不要修改相对位置。.dll 是 windows 系统运行需要的,.so 是 linux 系统需要的
4. 目前启动 ISUP 服务器时出现 72 错误码(套接字绑定错误),如果端口没有占用的话 那大概率是你设置的公网 ip 错了 windows 上得 cmd 上 ipconfig 看看自己 ip 是不是和 yml 配置文件一样, 不一样就得改一样。
原文地址: 海康威视摄像头 ISUP(原 EHOME 协议) 摄像头实时预览 springboot 版本 java 实现,并可以在浏览器 vue 前端播放 (附带源码)