随笔 - 8  文章 - 0  评论 - 2  阅读 - 22819

Java调本地摄像头推流到服务器

JAVA CV 开发

我这个项目最终还是要要用在安卓的机器上的,但是安卓那边说推流推不了,我只好自己先试试电脑的本地摄像头,后面再准备把这个打包给安卓,这个是我本人在学习Java CV 所做的一个总结
 
 
项目地址 https://github.com/lingfenghu/java-cv
 
参考:
https://www.w3cschool.cn/opencv/
https://blog.csdn.net/eguid_1/article/details/51659578
 
 
OpenCV示例
https://www.cnblogs.com/crow9527/p/13722691.html
 
常见错误:
org.bytedeco.javacv.FrameRecorder$Exception: avio_open2 error() error -138: Could not open 'null'
解决:https://blog.csdn.net/weixin_40777510/article/details/105843741
需要使用 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>

 

多线程控制
https://cloud.tencent.com/developer/article/1155102
 
类对象在内存唯一
https://cloud.tencent.com/developer/article/1158707
 
获取配置文 .properties 件内容
https://www.cnblogs.com/sebastian-tyd/p/7895182.html
 
 

推流器

代码内容 推流和加水印

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();//关闭抓取器

 

本地摄像头加水印
https://blog.csdn.net/weixin_30563917/article/details/96963308
https://www.cnblogs.com/eguid/p/6821579.html
https://blog.csdn.net/eguid_1/article/details/58027720
添加水印出错
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));
 
去水印参考:
https://www.jb51.net/article/165113.htm
 

收流器

rtmp流测试
湖南TV rtmp://58.200.131.2:1935/livetv/hunantv
 
https://blog.csdn.net/eguid_1/article/details/52680802
https://www.cnblogs.720com/pijunqi/p/14251629.html
 
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、 发现保存下的视频播放非常快,接近两倍速,分析发现视频帧将近一半丢失
解决:
https://www.cnblogs.com/svenwu/p/9663038.html
其他因素: 收流线程不能像发送流那样设置线程睡眠
收流器的 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。

posted on   蒟蒻鸡  阅读(2253)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

点击右上角即可分享
微信分享提示