Java调本地摄像头推流到服务器
JAVA CV 开发
我这个项目最终还是要要用在安卓的机器上的,但是安卓那边说推流推不了,我只好自己先试试电脑的本地摄像头,后面再准备把这个打包给安卓,这个是我本人在学习Java CV 所做的一个总结
参考:
OpenCV示例
常见错误:
org.bytedeco.javacv.FrameRecorder$Exception: avio_open2 error() error -138: Could not open 'null'
需要使用 nginx服务 代理推送流 这个具体去看 rtsp转rtmp 那篇 博客

关闭摄像头录像报错
org.bytedeco.javacv.FrameGrabber$Exception: videoInput.getPixels() Error: Could not get pixels.

解决:
javacv-platform 版本升级为 1.5.4 能解决大部分问题,只需要引这一个依赖
1 2 3 4 5 | <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version> 1.5 . 4 </version> </dependency> |
多线程控制
类对象在内存唯一
获取配置文 .properties 件内容
推流器
代码内容 推流和加水印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | OpenCVFrameGrabber grabber = new OpenCVFrameGrabber( 0 ); //如果有多个摄像头请改这个。0表示第一个 //360p 480x360 或 480p 640x480 grabber.setImageWidth( 480 ); grabber.setImageHeight( 360 ); grabber.start(); //开始获取摄像头数据 CanvasFrame frame = new CanvasFrame( "camera" ); //新建一个窗口 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setAlwaysOnTop( true ); OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); //转换器 IplImage grabbedImage = converter.convert(grabber.grab()); //抓取一帧视频并将其转换为图像,至于用这个图像用来做什么?加水印,人脸识别等等自行添加 // 水印文字位置 Point point = new Point( 50 , 30 ); // 颜色,使用黄色 Scalar scalar = new Scalar( 0 , 255 , 255 , 0 ); int width = grabbedImage.width(); int height = grabbedImage.height(); FrameRecorder recorder = FrameRecorder.createDefault(outputFile, width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // avcodec.AV_CODEC_ID_H264,编码 recorder.setFormat( "flv" ); //封装格式,如果是推送到rtmp就必须是flv封装格式 recorder.setFrameRate(frameRate); recorder.start(); //开启录制器 long startTime = 0 ; long videoTS = 0 ; Mat mat = null ; Frame rotatedFrame = converter.convert(grabbedImage); //不知道为什么这里不做转换就不能推到rtmp while ((grabbedImage = converter.convert(grabber.grab())) != null ) { String now = sdf.format(System.currentTimeMillis()); rotatedFrame = converter.convert(grabbedImage); // 取一帧视频(图像),并转换为Mat mat = converter.convertToMat(rotatedFrame); opencv_imgproc.putText(mat, now, point, opencv_imgproc.INTER_LINEAR, 1.2 , scalar); frame.showImage(converter.convert(mat)); if (startTime == 0 ) { startTime = System.currentTimeMillis(); } videoTS = 1000 * (System.currentTimeMillis() - startTime); recorder.setTimestamp(videoTS); recorder.record(rotatedFrame); //和帧率相关,不设置则是全帧率 Thread.sleep( 40 ); } mat.release(); mat.close(); frame.dispose(); //关闭窗口 recorder.close(); //关闭推流录制器,close包含release和stop操作 grabber.close(); //关闭抓取器 |
本地摄像头加水印
添加水印出错
java.lang.NullPointerException: Pointer address of argument 0 is NULL.
解决:
mat不能在循环中关闭或释放
关键代码
1 2 3 4 5 6 7 8 9 10 | // 水印文字位置 Point point = new Point( 30 , 30 ); // 颜色,使用黄色 Scalar scalar = new Scalar( 0 , 255 , 255 , 0 ); ... mat = converter.convertToMat(rotatedFrame); // 加文字水印,opencv_imgproc.putText(图片,水印文字,文字位置,字体,字体大小,字体颜色 opencv_imgproc.putText(mat, "111" ,point, 1 , 1.2 ,scalar); frame.showImage(converter.convert(mat)); |
去水印参考:
收流器
rtmp流测试
湖南TV rtmp://58.200.131.2:1935/livetv/hunantv
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile); //分辨率 grabber.setImageWidth( 480 ); grabber.setImageHeight( 360 ); // 流媒体输出地址,分辨率(长,高),是否录制音频(0:不录制/1:录制) FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 480 , 360 , audioChannel); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat( "flv" ); //建议在线程中使用该方法 try { grabber.start(); recorder.start(); Frame frame = null ; while (status&& (frame = grabber.grabFrame()) != null ) { recorder.record(frame); Thread.sleep( 40 ); System.out.println(sdf.format(System.currentTimeMillis())); } recorder.stop(); grabber.stop(); } finally { if (grabber != null ) { grabber.stop(); } } 问题: 1 、 发现保存下的视频播放非常快,两倍速,分析发现视频帧将近一半丢失 https: //www.cnblogs.com/svenwu/p/9663038.html 其他因素: 收流线程不能像发流那样设置线程睡眠 grabber.setOption( "http_transport" , "tcp" ); grabber.setFrameRate( 25 ); ... recorder.setFrameRate( 25 ); //一般考虑比推流器发送的视频比特流大,如下图 recorder.setVideoBitrate( 1000 * 1000 ); |
问题:
1、 发现保存下的视频播放非常快,接近两倍速,分析发现视频帧将近一半丢失
解决:
其他因素: 收流线程不能像发送流那样设置线程睡眠
收流器的 grabber 和 recorder 都要设置帧率
1 2 3 4 5 6 | grabber.setOption( "http_transport" , "tcp" ); grabber.setFrameRate( 25 ); ... recorder.setFrameRate( 25 ); //一般考虑比推流器发送的视频比特流大,如下图 recorder.setVideoBitrate( 1000 * 1000 ); |
推流视频流信息
2、等待推流器关闭再等待收流器自动关闭,会有一段等待收流的画面卡住暂停时间,这部分也被算入到视频时长,视频时长不正确
手动关闭收流和推流在VLC中比较

手动关闭收流器会导致视频时长信息丢失
不同分辨率视频大小参考
144p (192×144,20帧/秒),4:3,录制一分钟大约1MB;
240p (320×240,20帧/秒),4:3,录制一分钟大约3MB;
360p (480×360,20帧/秒) ,4:3,录制一分钟大约7MB;
480p (640×480,20帧/秒),4:3,录制一分钟大约12MB;
720p (1280×720,30帧/秒) , 16:9,录制一分钟大约35MB;
1080p (1920×1080,30帧/秒) ,16:9 , 录制一分钟大约80MB。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库