1、pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo1</artifactId><version>0.0.1-SNAPSHOT</version><name>demo1</name><description>demo1</description><properties><java.version>11</java.version><javacpp.platform.macosx-x86_64>macosx-x86_64</javacpp.platform.macosx-x86_64><javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64><javacpp.platform.windows-x86_64>windows-x86_64</javacpp.platform.windows-x86_64><javacv.version>1.5.11</javacv.version><javacv.ffmpeg.version>7.1-1.5.11</javacv.ffmpeg.version><javacv.opencv.version>4.10.0-1.5.11</javacv.opencv.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- mqtt --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId></dependency><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-stream</artifactId></dependency><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-mqtt</artifactId></dependency><!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --><!-- <dependency>--><!-- <groupId>com.baomidou</groupId>--><!-- <artifactId>mybatis-plus-boot-starter</artifactId>--><!-- <version>3.5.1</version>--><!-- </dependency>--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.4.6</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-dysmsapi</artifactId><version>2.2.1</version></dependency><!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core --><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.6.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>dyvmsapi20170525</artifactId><version>2.1.4</version></dependency><!-- https://mvnrepository.com/artifact/io.netty/netty-all --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.75.Final</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><!-- https://mvnrepository.com/artifact/org.redisson/redisson --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.0</version></dependency><!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java --><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.20.0</version></dependency><!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util --><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java-util</artifactId><version>3.20.0</version><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/com.google.guava/guava --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency><dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.8</version></dependency><!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>4.0.3</version></dependency><!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna --><dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.14.0</version></dependency><dependency><groupId>ai.djl</groupId><artifactId>api</artifactId><version>0.29.0</version></dependency><!-- https://mvnrepository.com/artifact/ai.djl.mxnet/mxnet-engine --><dependency><groupId>ai.djl.mxnet</groupId><artifactId>mxnet-engine</artifactId><version>0.29.0</version><scope>runtime</scope></dependency><!-- https://mvnrepository.com/artifact/ai.djl.mxnet/mxnet-model-zoo --><dependency><groupId>ai.djl.mxnet</groupId><artifactId>mxnet-model-zoo</artifactId><version>0.29.0</version></dependency><dependency><groupId>ai.djl</groupId><artifactId>basicdataset</artifactId><version>0.29.0</version></dependency><dependency><groupId>ai.djl</groupId><artifactId>model-zoo</artifactId><version>0.29.0</version></dependency><dependency><groupId>ai.djl.timeseries</groupId><artifactId>timeseries</artifactId><version>0.29.0</version></dependency><dependency><groupId>ai.djl.huggingface</groupId><artifactId>tokenizers</artifactId><version>0.29.0</version></dependency><dependency><groupId>ai.djl.audio</groupId><artifactId>audio</artifactId><version>0.29.0</version></dependency><dependency><groupId>commons-cli</groupId><artifactId>commons-cli</artifactId><version>1.5.0</version></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/javacv --><dependency><groupId>org.bytedeco</groupId><artifactId>javacv</artifactId><version>${javacv.version}</version></dependency><!--javacv 精简依赖 只依赖windows64位ffmpeg--><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>ffmpeg</artifactId><version>${javacv.ffmpeg.version}</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/opencv-platform --><dependency><groupId>org.bytedeco</groupId><artifactId>opencv</artifactId><version>4.10.0-1.5.11</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/tesseract --><dependency><groupId>org.bytedeco</groupId><artifactId>tesseract</artifactId><version>5.5.0-1.5.11</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/openblas --><dependency><groupId>org.bytedeco</groupId><artifactId>openblas</artifactId><version>0.3.28-1.5.11</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/leptonica --><dependency><groupId>org.bytedeco</groupId><artifactId>leptonica</artifactId><version>1.85.0-1.5.11</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/flycapture --><dependency><groupId>org.bytedeco</groupId><artifactId>flycapture</artifactId><version>2.13.3.31-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/libdc1394 --><dependency><groupId>org.bytedeco</groupId><artifactId>libdc1394</artifactId><version>2.2.6-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/libfreenect --><dependency><groupId>org.bytedeco</groupId><artifactId>libfreenect</artifactId><version>0.5.7-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/libfreenect2 --><dependency><groupId>org.bytedeco</groupId><artifactId>libfreenect2</artifactId><version>0.2.0-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/librealsense --><dependency><groupId>org.bytedeco</groupId><artifactId>librealsense</artifactId><version>1.12.4-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/librealsense2 --><dependency><groupId>org.bytedeco</groupId><artifactId>librealsense2</artifactId><version>2.53.1-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/videoinput --><dependency><groupId>org.bytedeco</groupId><artifactId>videoinput</artifactId><version>0.200-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!-- https://mvnrepository.com/artifact/org.bytedeco/artoolkitplus --><dependency><groupId>org.bytedeco</groupId><artifactId>artoolkitplus</artifactId><version>2.3.1-1.5.9</version><classifier>${javacpp.platform.windows-x86_64}</classifier></dependency><!--javacv 精简依赖 只依赖windows64位ffmpeg--></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2、java代码
package com.example.demo.ffpemg;import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.FFmpegLogCallback;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder.Exception;
import org.bytedeco.javacv.Java2DFrameConverter;import javax.sound.sampled.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Scanner;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class VideoRecordRtmp {public final static int WIDTH = Toolkit.getDefaultToolkit().getScreenSize().width;public final static int HEIGHT = Toolkit.getDefaultToolkit().getScreenSize().height;//线程池 screenTimer,录制视频private ScheduledThreadPoolExecutor screenTimer;//获取屏幕尺寸private final Rectangle rectangle = new Rectangle(WIDTH, HEIGHT); // 截屏的大小//视频类 FFmpegFrameRecorderprivate FFmpegFrameRecorder recorder;private Robot robot;//线程池 exec,录制音频private ScheduledThreadPoolExecutor exec;private TargetDataLine line;private AudioFormat audioFormat;private DataLine.Info dataLineInfo;///是否开启录音设备private boolean isHaveDevice = true;private long startTime = 0;private long videoTS = 0;private long pauseTimeStart = 0;//开始暂停的时间private long pauseTime = 0;//暂停的时长private double frameRate = 5;private String state = "start";//录制状态:start正在录制,pause暂停录制,stop停止录制public String getState() {return state;}public VideoRecordRtmp(String fileName, boolean isHaveDevice) {// TODO Auto-generated constructor stubFFmpegLogCallback.set();String rtmpUrl = "rtmp://127.0.0.1:1935/live/streamxxxxx";recorder = new FFmpegFrameRecorder(rtmpUrl, WIDTH, HEIGHT);recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 13recorder.setFormat("flv");// recorder.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");recorder.setSampleRate(44100);recorder.setFrameRate(frameRate);recorder.setVideoQuality(0);recorder.setVideoOption("crf", "23");// 2000 kb/s, 720P视频的合理比特率范围recorder.setVideoBitrate(1000000);/*** 权衡quality(视频质量)和encode speed(编码速度) values(值): ultrafast(终极快),superfast(超级快),* veryfast(非常快), faster(很快), fast(快), medium(中等), slow(慢), slower(很慢),* veryslow(非常慢)* ultrafast(终极快)提供最少的压缩(低编码器CPU)和最大的视频流大小;而veryslow(非常慢)提供最佳的压缩(高编码器CPU)的同时降低视频流的大小* 参考:https://trac.ffmpeg.org/wiki/Encode/H.264 官方原文参考:-preset ultrafast as the* name implies provides for the fastest possible encoding. If some tradeoff* between quality and encode speed, go for the speed. This might be needed if* you are going to be transcoding multiple streams on one machine.*/recorder.setVideoOption("preset", "slow");recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // yuv420precorder.setAudioChannels(2);recorder.setAudioOption("crf", "0");// Highest qualityrecorder.setAudioQuality(0);recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);try {robot = new Robot();} catch (AWTException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {recorder.start();} catch (Exception e) {// TODO 自动生成的 catch 块e.printStackTrace();}if (isHaveDevice) {/*** float sampleRate:采样率;每秒采样数 ;* int sampleSizeInBits:采样位数;每个样本中的位数 ;* int channels:音频通道数,1为mono,2为立体声;* boolean signed;* boolean bigEndian:是否为大端存储;指示单个样本的数据是否以大字节顺序存储*/audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);try {line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);} catch (LineUnavailableException e1) {// TODO Auto-generated catch blockSystem.out.println("未获得音频线路," + e1);}}this.isHaveDevice = isHaveDevice;}/*** 开始录制*/public void start() {state = "start";if (startTime == 0) {startTime = System.currentTimeMillis();}if (pauseTimeStart != 0) {//计算暂停的时长pauseTime = System.currentTimeMillis() - pauseTimeStart;pauseTimeStart = 0;//归零} else {//没有暂停过,暂停时长为0pauseTime = 0;}// 如果有录音设备则启动录音线程if (isHaveDevice) {new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubSoundCaputre();}}).start();}//录屏screenCaptrue();}//开启录屏的线程private void screenCaptrue() {// 录屏screenTimer = new ScheduledThreadPoolExecutor(1);/**** 参数:* command - 要执行的任务initialDelay - 延迟第一次执行的时间 ,延迟一帧的时间,我们设置的mp4的帧速为frameRate=每秒5帧,所以一帧的时间为 1秒/5period - 连续执行之间的时期 ,执行周期,为1帧的时间unit - initialDelay和period参数的时间单位,TimeUnit.MILLISECONDS为千分之一秒,就是1毫秒*/screenTimer.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {BufferedImage screenCapture = robot.createScreenCapture(rectangle); // 截屏BufferedImage videoImg = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_3BYTE_BGR); // 声明一个BufferedImage用重绘截图Graphics2D videoGraphics = videoImg.createGraphics();// 创建videoImg的Graphics2DvideoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,RenderingHints.VALUE_COLOR_RENDER_SPEED);videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);videoGraphics.drawImage(screenCapture, 0, 0, null); // 重绘截图Java2DFrameConverter java2dConverter = new Java2DFrameConverter();Frame frame = java2dConverter.convert(videoImg);try {//计算总时长videoTS = 1000L * (System.currentTimeMillis() - startTime - pauseTime);// 检查偏移量if (videoTS > recorder.getTimestamp()) {recorder.setTimestamp(videoTS);}recorder.record(frame); // 录制视频} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}// 释放资源videoGraphics.dispose();videoGraphics = null;videoImg.flush();videoImg = null;java2dConverter = null;screenCapture.flush();screenCapture = null;}}, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);}/*** 开启抓取声音的线程*/public void SoundCaputre() {try {if (!line.isRunning()) {line.open(audioFormat);line.start();}} catch (LineUnavailableException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}final int sampleRate = (int) audioFormat.getSampleRate();final int numChannels = audioFormat.getChannels();int audioBufferSize = sampleRate * numChannels;final byte[] audioBytes = new byte[audioBufferSize];exec = new ScheduledThreadPoolExecutor(1);exec.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {int nBytesRead = line.read(audioBytes, 0, line.available());int nSamplesRead = nBytesRead / 2;short[] samples = new short[nSamplesRead];// Let's wrap our short[] into a ShortBuffer and// pass it to recordSamplesByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);// recorder is instance of// org.bytedeco.javacv.FFmpegFrameRecorderrecorder.recordSamples(sampleRate, numChannels, sBuff);// System.gc();} catch (Exception e) {e.printStackTrace();}}}, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);}/*** 暂停录制*/public void pause() {state = "pause";screenTimer.shutdownNow();screenTimer = null;if (isHaveDevice) {exec.shutdownNow();exec = null;}pauseTimeStart = System.currentTimeMillis();}/*** 停止录制*/public void stop() {state = "stop";if (null != screenTimer) {screenTimer.shutdownNow();}try {if (isHaveDevice) {if (null != exec) {exec.shutdownNow();}if (null != line) {line.stop();line.close();}dataLineInfo = null;audioFormat = null;}recorder.stop();recorder.release();recorder.close();screenTimer = null;// screenCapture = null;} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}public static void main(String[] args) {VideoRecordRtmp videoRecord = new VideoRecordRtmp("C:\\Users\\Administrator\\Desktop\\视频2", false);videoRecord.start();System.out.println("****start继续录制,pause暂停录制,stop停止录制****");while (true) {Scanner sc = new Scanner(System.in);if (sc.hasNext()) {String cmd = sc.next();if (cmd.equalsIgnoreCase("stop")) {videoRecord.stop();System.out.println("****已经停止录制****");break;}if (cmd.equalsIgnoreCase("pause")) {if (videoRecord.getState().equals("pause")) {System.out.println("*error:已经暂停,请勿重复操作pause*");continue;}videoRecord.pause();System.out.println("****已暂停,start继续录制,stop结束录制****");}if (cmd.equalsIgnoreCase("start")) {if (videoRecord.getState().equals("start")) {System.out.println("*error:请勿重复操作start*");continue;}videoRecord.start();System.out.println("****正在录制****");}}}}}
3、采用ZLMediaKit作为流媒体服务器