javaCV
2022/8/31 1:22:56
本文主要是介绍javaCV,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 示例一:调用本地摄像头
- 示例二:javacv实现直播流
- 示例三:javaCV 视频工具—截取视频缩略图、获取视频属性
- 依赖引入
- 实现
示例一:调用本地摄像头
参考地址:https://www.jianshu.com/p/9920d1636787
1.环境准备
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5</version> </dependency>
2.调用本地摄像头并且显示在CanvasFrame里面
import org.bytedeco.javacv.CanvasFrame; import org.bytedeco.javacv.FrameGrabber; import org.bytedeco.javacv.OpenCVFrameGrabber; import javax.swing.*; /** * @author JHL * @version 1.0 * @date 2022/8/25 14:50 * @since : JDK 11 */ public class T { public static void main(String[] args) { OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(0);// 0就是本地摄像头 try { grabber.start(); //开始获取摄像头数据 } catch (FrameGrabber.Exception e) { e.printStackTrace(); } CanvasFrame canvas = new CanvasFrame("JavaCV");//新建一个窗口 canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); while (true) { if (!canvas.isDisplayable())// 窗口状态控制线程 { try { grabber.stop();//停止抓取摄像头 } catch (FrameGrabber.Exception e) { e.printStackTrace(); } } try { canvas.showImage(grabber.grab());//获取摄像头一帧视频图像并放到窗口上显示 } catch (FrameGrabber.Exception e) { e.printStackTrace(); } /* try { Thread.sleep(20);//可以加个间隔 } catch (InterruptedException e) { e.printStackTrace(); } */ } } }
示例二:javacv实现直播流
参考地址:https://www.jianshu.com/p/238e52bc16c4
pom依赖
<!-- 需要注意,javacv主要是一组API为主,还需要加入对应的实现 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.6</version> </dependency> <!-- 用到了 ffmpeg 需要把 ffmpeg 的平台实现依赖引入 --> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg-platform</artifactId> <version>4.4-1.5.6</version> </dependency> <!--所有平台实现,依赖非常大,几百MB吧--> <!--<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.6</version> </dependency>--> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-base</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-graphics</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-swing</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-web</artifactId> <version>17.0.2</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-media</artifactId> <version>17.0.2</version> </dependency> </dependencies>
测试类
import cn.hutool.core.io.FileUtil; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.stage.WindowEvent; import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.javacv.*; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.TargetDataLine; import java.io.File; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.Timer; import java.util.TimerTask; /** * @author JHL * @version 1.0 * @date 2022/8/25 14:50 * @since : JDK 11 */ public class Test extends Application { private static final int frameRate = 24;// 录制的帧率 private static boolean isStop = false; private static TargetDataLine line; @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("我的桌面录屏大师"); ImageView imageVideo = new ImageView();// 用于软件录制显示 imageVideo.setFitWidth(800); imageVideo.setFitHeight(600); Button button = new Button("停止录制"); button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { isStop = true; if (line != null) {// 马上停止声音录入 try { line.close(); } catch (Exception e) { } } Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("info"); alert.setHeaderText("已经停止录制"); alert.setOnCloseRequest(event1 -> alert.hide()); alert.showAndWait(); } }); VBox box = new VBox(); box.getChildren().addAll(button, imageVideo); primaryStage.setScene(new Scene(box)); primaryStage.setHeight(600); primaryStage.setWidth(800); primaryStage.show(); primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent event) {// 退出时停止 isStop = true; System.exit(0); } }); // 帧记录 // window 建议使用 FFmpegFrameGrabber("desktop") 进行屏幕捕捉 FrameGrabber grabber = new FFmpegFrameGrabber("desktop"); grabber.setFormat("gdigrab"); grabber.setFrameRate(frameRate);// 帧获取间隔 // 捕获指定区域,不设置则为全屏 grabber.setImageHeight(600); grabber.setImageWidth(800); // grabber.setOption("offset_x", "200"); // grabber.setOption("offset_y", "200");//必须设置了大小才能指定区域起点,参数可参考 FFmpeg 入参 grabber.start(); // 视频格式 flv/avi String videoFormat = "avi"; File file = FileUtil.newFile("output." + videoFormat); if (file.exists()) { file.delete(); } // 直播推流 // final FFmpegFrameRecorder recorder = new FFmpegFrameRecorder( // "rtmp://10.8.4.191/live/livestream", // grabber.getImageWidth(), grabber.getImageHeight(), 2); // 存到本地 // final FFmpegFrameRecorder recorder = new FFmpegFrameRecorder( // "test.flv", // grabber.getImageWidth(), grabber.getImageHeight(), 2); // 用于存储视频 , 调用stop后,需要释放,就会在指定位置输出文件,,这里我保存到D盘 FFmpegFrameRecorder recorder = FFmpegFrameRecorder.createDefault(file, grabber.getImageWidth(), grabber.getImageHeight()); recorder.setInterleaved(true); // https://trac.ffmpeg.org/wiki/StreamingGuide recorder.setVideoOption("tune", "zerolatency");// 加速 // https://trac.ffmpeg.org/wiki/Encode/H.264 recorder.setVideoOption("preset", "ultrafast"); recorder.setFrameRate(frameRate);// 设置帧率,重要! // Key frame interval, in our case every 2 seconds -> 30 (fps) * 2 = 60 recorder.setGopSize(frameRate * 2); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);// 编码,使用编码能让视频占用内存更小,根据实际自行选择 // https://trac.ffmpeg.org/wiki/Encode/H.264 recorder.setVideoOption("crf", "28"); // 2000 kb/s 720P recorder.setVideoBitrate(2000000); recorder.setFormat(videoFormat); // 添加音频录制 // 不可变音频 recorder.setAudioOption("crf", "0"); // 最高音质 recorder.setAudioQuality(0); // 192 Kbps recorder.setAudioBitrate(192000); recorder.setSampleRate(44100); recorder.setAudioChannels(2); recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); recorder.start(); // 44100 16声道 AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false); DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat); // 可以捕捉不同声道 line = (TargetDataLine) AudioSystem.getLine(dataLineInfo); // 录制声音 new Thread(new Runnable() { @Override public void run() { try { line.open(audioFormat); line.start(); final int sampleRate = (int) audioFormat.getSampleRate(); final int numChannels = audioFormat.getChannels(); // 缓冲区 final int audioBufferSize = sampleRate * numChannels; final byte[] audioBytes = new byte[audioBufferSize]; Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { try { if (isStop) {// 停止录音 line.stop(); line.close(); System.out.println("已经停止!"); timer.cancel(); } // 读取音频 // read会阻塞 int readLenth = 0; while (readLenth == 0){ readLenth = line.read(audioBytes, 0, line.available()); } // audioFormat 定义了音频输入为16进制,需要将字节[]转为短字节[] // FFmpegFrameRecorder.recordSamples 源码中的 AV_SAMPLE_FMT_S16 int rl = readLenth / 2; short[] samples = new short[rl]; // short[] 转换为 ShortBuffer ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples); ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, rl); // 记录 recorder.recordSamples(sampleRate, numChannels, sBuff); } catch (Exception e) { e.printStackTrace(); } } }, 1000, 1000 / frameRate); } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { // 获取屏幕捕捉的一帧 Frame frame = null; // 屏幕录制,由于已经对音频进行了记录,需要对记录时间进行调整即可 // 即上面调用了 recorder.recordSamples 需要重新分配时间,否则视频输出时长等于实际 的2倍 while ((frame = grabber.grab()) != null) { if (isStop) { try { // 停止 recorder.stop(); grabber.stop(); // 释放内存,我们都知道c/c++需要手动释放资源 recorder.release(); grabber.release(); } catch (Exception e) { e.printStackTrace(); } break; } // 将这帧放到录制 recorder.record(frame); Image convert = new JavaFXFrameConverter().convert(frame); imageVideo.setImage(convert); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } public static void main(String[] args) { launch(args); } }
什么是javaFX应用???
JavaFx类型应用启动引导类,解决启动报错
错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序。
原因详解:https://www.cnblogs.com/hhddd-1024/p/16634369.html
/** * 用来引导 JavaFX应用解决控制台异常:错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序。 * * @author JHL * @version 1.0 * @date 2022/8/25 18:03 * @since : JDK 11 */ public class JavaFXBootstrap { public static void main(String[] args) { Test.main(args); } }
然后用docker起一个srs进行推流播放。
# 先启动 docker run -p 1935:1935 -p 1985:1985 -p 8080:8080 \ ccr.ccs.tencentyun.com/ossrs/srs:4
最后消费者端即可拉流
示例三:javaCV 视频工具—截取视频缩略图、获取视频属性
https://www.jianshu.com/p/d691f0b68060
前言
通过javaCV 视频工具—截取视频缩略图、获取视频属性
依赖引入
<!--javaCV 视频工具--> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5</version> </dependency>
实现
@Slf4j public class VideoUtils { private static final String IMAGEMAT = "png"; private static final String ROTATE = "rotate"; /** * 默认截取视频的中间帧为封面 */ public static final int MOD = 2; /** * 视频缩略图后缀 */ private static final String VIDEO_THUMBNAIL_SUF = "th.png"; /** * 视频缩略图前缀 */ private static final String VIDEO_THUMBNAIL_PRE = "video/thumbnail/"; private static final String SYMBOL = "."; /** * 获取视频缩略图 * @param filePath:视频路径 * @param mod:视频长度/mod获取第几帧 * @throws Exception */ public static String randomGrabberFFmpegImage(String filePath, int mod) { String targetFilePath = ""; try{ FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(filePath); ff.start(); //图片位置是否正确 String rotate = ff.getVideoMetadata(ROTATE); //获取帧数 int ffLength = ff.getLengthInFrames(); Frame f; int i = 0; //设置截取帧数 int index = ffLength / mod; while (i < ffLength) { f = ff.grabImage(); if(i == index){ if (null != rotate && rotate.length() > 1) { OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage(); IplImage src = converter.convert(f); f = converter.convert(rotate(src, Integer.parseInt(rotate))); } targetFilePath = getImagePath(filePath, i); doExecuteFrame(f, targetFilePath); break; } i++; } ff.stop(); }catch (Exception e){ log.error("获取视频缩略图异常:" + e.getMessage()); } return targetFilePath; } /** * 随机生成生成缩略图存放路径 * @param filePath:视频路径 * @param index:第几帧 * @return:缩略图的存放路径 */ private static String getImagePath(String filePath, int index){ String fileName = FileUtils.getName(filePath); //去后缀 fileName = fileName.substring(0, fileName.indexOf(SYMBOL)); return TencentCosConfig.baseUrl + VIDEO_THUMBNAIL_PRE + DateUtils.datePath() + "/" + fileName + "_" + index + VIDEO_THUMBNAIL_SUF; } /** * 旋转图片 * @param src * @param angle * @return */ public static IplImage rotate(IplImage src, int angle) { IplImage img = IplImage.create(src.height(), src.width(), src.depth(), src.nChannels()); opencv_core.cvTranspose(src, img); opencv_core.cvFlip(img, img, angle); return img; } /** * 截取缩略图 * @param f * @param targerFilePath:封面图片 */ public static void doExecuteFrame(Frame f, String targerFilePath) { COSClient cosClient = TencentCosUtils.initCosClient(); if (null == f || null == f.image) { return; } Java2DFrameConverter converter = new Java2DFrameConverter(); BufferedImage bi = converter.getBufferedImage(f); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { ImageIO.write(bi, IMAGEMAT, out); // 获取文件流 InputStream bufferedImage = new ByteArrayInputStream(out.toByteArray()); int length = out.size(); ObjectMetadata objectMetadata = new ObjectMetadata(); // 从输入流上传必须制定content length, 否则http客户端可能会缓存所有数据,存在内存OOM的情况 objectMetadata.setContentLength(length); // 默认下载时根据cos路径key的后缀返回响应的contenttype, 上传时设置contenttype会覆盖默认值 PutObjectRequest putObjectRequest = new PutObjectRequest(TencentCosConfig.bucket, targerFilePath, bufferedImage, objectMetadata); PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); log.info("腾讯COS上传视频缩略图成功:{}", putObjectResult.getETag()); //关闭输入输出流 bufferedImage.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } finally { cosClient.shutdown(); } } /** * 根据视频长度随机生成随机数集合 * @param baseNum:基础数字,此处为视频长度 * @param length:随机数集合长度 * @return:随机数集合 */ public static List<Integer> random(int baseNum, int length) { List<Integer> list = new ArrayList<Integer>(length); while (list.size() < length) { Integer next = (int) (Math.random() * baseNum); if (list.contains(next)) { continue; } list.add(next); } Collections.sort(list); return list; } /** * 获取视频时长 单位/秒 * @param video * @return */ public static long getVideoDuration(File video) { long duration = 0L; FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video); try { ff.start(); duration = ff.getLengthInTime() / (1000 * 1000); ff.stop(); } catch (FrameGrabber.Exception e) { e.printStackTrace(); } return duration; } /** * 获取视频时长 单位/秒 * @param inputStream 输入流 * @return */ public static long getVideoDuration(InputStream inputStream) { long duration = 0L; FFmpegFrameGrabber ff = new FFmpegFrameGrabber(inputStream); try { ff.start(); duration = ff.getLengthInTime() / (1000 * 1000); ff.stop(); } catch (FrameGrabber.Exception e) { e.printStackTrace(); } return duration; } /** * 转换视频文件为mp4 * @param file * @return */ public static String convertToMp4(File file) { FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file); String fileName = null; Frame captured_frame = null; FFmpegFrameRecorder recorder = null; try { frameGrabber.start(); fileName = file.getAbsolutePath() + "__.mp4"; recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels()); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); //avcodec.AV_CODEC_ID_H264 //AV_CODEC_ID_MPEG4 recorder.setFormat("mp4"); recorder.setFrameRate(frameGrabber.getFrameRate()); //recorder.setSampleFormat(frameGrabber.getSampleFormat()); // recorder.setSampleRate(frameGrabber.getSampleRate()); recorder.setAudioChannels(frameGrabber.getAudioChannels()); recorder.setFrameRate(frameGrabber.getFrameRate()); recorder.start(); while ((captured_frame = frameGrabber.grabFrame()) != null) { try { recorder.setTimestamp(frameGrabber.getTimestamp()); recorder.record(captured_frame); } catch (FrameRecorder.Exception e) { e.printStackTrace(); } } recorder.stop(); recorder.release(); frameGrabber.stop(); } catch (Exception | FrameRecorder.Exception e) { e.printStackTrace(); } return fileName; } }
这篇关于javaCV的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-01为什么公共事业机构会偏爱 TiDB :TiDB 数据库在某省妇幼健康管理系统的应用
- 2024-04-26敏捷开发:想要快速交付就必须舍弃产品质量?
- 2024-04-26静态代码分析的这些好处,我竟然都不知道?
- 2024-04-26你在测试金字塔的哪一层?(下)
- 2024-04-26快刀斩乱麻,DevOps让代码评审也自动起来
- 2024-04-262024年最好用的10款ER图神器!
- 2024-04-2203-为啥大模型LLM还没能完全替代你?
- 2024-04-2101-大语言模型发展
- 2024-04-17基于SpringWeb MultipartFile文件上传、下载功能
- 2024-04-14个人开发者,Spring Boot 项目如何部署