ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

javaCV

2022-08-30 12:34:41  阅读:158  来源: 互联网

标签:new grabber static import javaCV recorder public


目录

示例一:调用本地摄像头

参考地址: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;
    }
}

标签:new,grabber,static,import,javaCV,recorder,public
来源: https://www.cnblogs.com/hhddd-1024/p/16638850.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有