海康sdk实时音视频流剥离-视频流(二)

1、需求

上一篇写了海康sdk获取音频流,后来我发现了更好的实现方式。先说下使用sdk的限制,单设备可正常获取数据,多设备情况下,核心的HCNetSDK.FRealDataCallBack_V30 demo里有,经过咨询海康工程师,这个类多设备情况需要做成单例,这样注册的设备都可以通过回调方法invoke获取实时音视频流,那么怎么区分呢, 可以通过Pointer pBuffer 这个对象。总而言之,复杂不说,这种多设备实现下,还会产生给的pBuffer对象内设备标识乱码情况。懒得问海康工程师了,麻烦!

后续我通过JavaCV拉取RTSP流实现。不需要手动解码,而且代码层面易于维护,非常方便。

2、代码

我使用JavaCV1.5.9版本,摄像头音视频格式,设置H264,AAC,实测PCM音频格式JavaCV无法解析。

ini 复制代码
public void run(ApplicationArguments args) {
        if (!enabled) return;
        String[] rstpArr = {
            "rtsp://admin:[email protected]:554/ISAPI/streaming/audio/1"
        };
        for (int i = 0; i < rstpArr.length; i++) {
            String rtspUrl = rstpArr[i];
            processCameraStream(rtspUrl, "camera-" + i);
        }
    }
    
    
// 自定义线程池
    private ExecutorService saveWhisperAlarmExecutorService = new ThreadPoolExecutor(
        50, // 核心线程数
        100, // 最大线程数
        60, // 空闲线程存活时间
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(1000), // 有界队列,避免OOM
        new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时,由调用线程直接执行任务
    );
    
 // 核心代码
 public void processCameraStream(String rtspUrl, String cameraId) {
        executorService.submit(() -> {
            FFmpegFrameGrabber grabber = null;
            FFmpegFrameRecorder recorder = null;

            try {
                grabber = new FFmpegFrameGrabber(rtspUrl);
                grabber.setOption("rtsp_transport", "tcp"); // 使用TCP传输,避免UDP丢包
                grabber.setAudioChannels(1); // 设置音频通道,1通道
                grabber.setSampleRate(16000); // 设置音频采样率,16000
                grabber.setAudioCodecName("aac"); //设置编码格式,AAC

                grabber.start();

               

                 Frame frame;
                 while ((frame = grabber.grab()) != null) {
		                 recorder.record(frame);
                                                                                         if(frame == null){
            break;
        }
        // 保存wav格式音频文件,用于测试
        String filePath = "./cache/test.wav"
                                                                                         recorder = new FFmpegFrameRecorder(filePath, 1);
        recorder.setSampleRate(16000);
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_PCM_S16LE);
        recorder.setFormat("wav");
        recorder.start();

        // 音频流
        if (frame.samples != null) {
            if (frame.samples != null && frame.samples[0] instanceof ShortBuffer) {
                ShortBuffer shortBuffer = (ShortBuffer) frame.samples[0];// 使用ShortBuffer接收,而非ByteBuffer
                byte[] aacData = new byte[shortBuffer.remaining() * 2]; // 每个 short 占 2 个字节
                ByteBuffer byteBuffer = ByteBuffer.wrap(aacData); //转换为ByteBuffer
            }
        }

        // 视频流,这里获取I帧转成图片,仅用于测试
                                                                                         BufferedImage bufferedImage = null;
        if (frame.keyFrame) {
            bufferedImage = converter.getBufferedImage(frame);
        }else {
            // 使用 grabImage() 获取完整图像
            Frame fullFrame = grabber.grabImage();
            bufferedImage = converter.getBufferedImage(fullFrame);
        }
        // 存储,用于测试
        File outputFile = new File("./cache/test.jpg");					ImageIO.write(bufferedImage, "jpg", outputFile);
        }
         } catch (Exception e) {
         log.error("rtsp取流异常:", e);
                    } finally {
                        try {
                            if (recorder != null) recorder.release();
                            if (recorder != null) recorder.stop();
                            if (grabber != null) grabber.stop();
                        } catch (Exception e) {
                            log.error("rtsp资源关闭异常:", e);
                        }
                    }
                });
            }

3、总结

以上简单几十行代码即可分别获取摄像头的视频流,音频流,稍作处理就可以供后续业务流程,想想之前用sdk拿音视频流,对比一下, 真是泪目。代码中我使用了线程池,是为了提高并发,同时我加了个参数"camera-" + i,后续可以将线程池的业务代码封装出去,接收一个摄像头标识数据,例如ip或自有的数据库id等,这样就可做到区分摄像头数据。

4、JavaCV包体积过大

pom引入,会将所有平台的包都放进去,需要做的就是引入需要的包就行。相关资料,无论ai还是网上都比较蛋疼,javacv不同版本,内部集成版本差异也大。我的引入如下,需要什么平台引入什么平台,瘦身前jar包1个G,瘦身后jar包200M左右。

xml 复制代码
<properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <javacv.version>1.5.9</javacv.version>
        <javacv.opencv.version>4.7.0-1.5.9</javacv.opencv.version>
        <javacv.ffmpeg.version>6.0-1.5.9</javacv.ffmpeg.version>
        <javacv.openblas.version>0.3.23-1.5.9</javacv.openblas.version>
        <javacpp.platform.android-arm>android-arm</javacpp.platform.android-arm>
        <javacpp.platform.android-arm64>android-arm64</javacpp.platform.android-arm64>
        <javacpp.platform.android-x86>android-x86</javacpp.platform.android-x86>
        <javacpp.platform.android-x86_64>android-x86_64</javacpp.platform.android-x86_64>
        <javacpp.platform.ios-arm>ios-arm</javacpp.platform.ios-arm>
        <javacpp.platform.ios-arm64>ios-arm64</javacpp.platform.ios-arm64>
        <javacpp.platform.ios-x86>ios-x86</javacpp.platform.ios-x86>
        <javacpp.platform.ios-x86_64>ios-x86_64</javacpp.platform.ios-x86_64>
        <javacpp.platform.macosx-x86_64>macosx-x86_64</javacpp.platform.macosx-x86_64>
        <javacpp.platform.linux-armhf>linux-armhf</javacpp.platform.linux-armhf>
        <javacpp.platform.linux-arm64>linux-arm64</javacpp.platform.linux-arm64>
        <javacpp.platform.linux-ppc64le>linux-ppc64le</javacpp.platform.linux-ppc64le>
        <javacpp.platform.linux-x86>linux-x86</javacpp.platform.linux-x86>
        <javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>
        <javacpp.platform.windows-x86>windows-x86</javacpp.platform.windows-x86>
        <javacpp.platform.windows-x86_64>windows-x86_64</javacpp.platform.windows-x86_64>
    </properties>
    
    
  <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>${javacv.version}</version>
            <classifier>${javacpp.platform.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>${javacv.openblas.version}</version>
            <classifier>${javacpp.platform.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>${javacv.opencv.version}</version>
            <classifier>${javacpp.platform.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>${javacv.ffmpeg.version}</version>
            <classifier>${javacpp.platform.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>${javacv.version}</version>
        </dependency>
相关推荐
刘大猫261 小时前
Arthas monitor(方法执行监控)
人工智能·后端·监控
追逐时光者1 小时前
MongoDB从入门到实战之MongoDB简介
后端·mongodb
Huazie2 小时前
在WSL2 Ubuntu中部署FastDFS服务的完整指南
服务器·后端·ubuntu
行者无疆xcc3 小时前
【Django】设置让局域网内的人访问
后端·python·django
嘵奇3 小时前
基于Spring Boot实现文件秒传的完整方案
java·spring boot·后端
Value_Think_Power3 小时前
azure 一个 pod 内有多个 container ,这些container 可以 共享一块磁盘吗
后端
李菠菜3 小时前
优化Centos关闭SELinux/Swap及资源限制调整
linux·后端·centos
wangyongquan3 小时前
koa语法 | koa/router | 中间件 | 洋葱模型
后端·node.js
小兵张健3 小时前
小米 JD 调研
java·后端·面试
BigTopOne3 小时前
【君正-T41】外设采集h264流程
后端