导入依赖
<properties>
<java.version>1.8</java.version>
<ffmpeg.version>0.6.2</ffmpeg.version>
<javacv.version>1.5.8</javacv.version>
<javacpp.version>1.5.8</javacpp.version>
<bytedeco-ffmpeg.version>5.1.2-1.5.8</bytedeco-ffmpeg.version>
<opencv-platform-gpu.version>4.6.0-1.5.8</opencv-platform-gpu.version>
<javacpp.platform.dependencies>windows-x86_64</javacpp.platform.dependencies>
<javacpp.platform.linux-x86_64>linux-x86_64</javacpp.platform.linux-x86_64>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</properties>
<!-- maven依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- javacv -->
<!-- <dependency>-->
<!-- <groupId>org.bytedeco</groupId>-->
<!-- <artifactId>javacv-platform</artifactId>-->
<!-- <version>1.5.8</version>-->
<!-- </dependency>-->
<!-- Optional GPL builds with (almost) everything enabled -->
<!-- <dependency>-->
<!-- <groupId>org.bytedeco</groupId>-->
<!-- <artifactId>ffmpeg-platform-gpl</artifactId>-->
<!-- <version>5.1.2-1.5.8</version>-->
<!-- </dependency>-->
<!-- ========== 优化依赖 ffmpeg-platform-gpl javacv-platform 只导入windows liunx平台 ============== -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>${javacv.version}</version>
<exclusions>
<exclusion>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 获取视频中的图片 -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacpp.version}</version>
<classifier>${javacpp.platform.dependencies}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>${javacpp.version}</version>
<classifier>${javacpp.platform.linux-x86_64}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>${bytedeco-ffmpeg.version}</version>
<classifier>linux-x86_64-gpl</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>${bytedeco-ffmpeg.version}</version>
<classifier>windows-x86_64-gpl</classifier>
</dependency>
<dependency>
<groupId>net.bramp.ffmpeg</groupId>
<artifactId>ffmpeg</artifactId>
<version>${ffmpeg.version}</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>${opencv-platform-gpu.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
核心方法
实例代码
/**
* 视频转分辨率转视频编码
*
* @param inputFile 源文件
* @param outputPath 输出目录
* @param filterContent 过滤器配置参数
* @param width 需要转成的视频的宽度
* @param height 需要转成的视频的高度
* @param videoFormat 需要转换成的视频格式
* @return 返回新文件名称,可以根据实际使用修改
*/
public static String videoConvert(String inputFile, String outputPath,String filterContent, Integer width, Integer height, String videoFormat) {
List<String> paths = Splitter.on(".").splitToList(inputFile);
String ext = paths.get(paths.size() - 1);
if (StringUtils.isEmpty(videoFormat)) {
videoFormat = ext;
}
String newFileName = UUID.randomUUID().toString().replaceAll("-","") + "." + videoFormat;
String resultPath = Joiner.on(File.separator).join(Arrays.asList(outputPath, newFileName));
Frame frame;
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
FFmpegFrameRecorder recorder = null;
double aspectRatio = 1.0;
FFmpegFrameFilter filter =new FFmpegFrameFilter(filterContent,grabber.getImageWidth(),grabber.getImageHeight());
try {
// 初始化帧抓取器,例如数据结构(时间戳、编码器上下文、帧对象等),
// 如果入参等于true,还会调用avformat_find_stream_info方法获取流的信息,放入AVFormatContext类型的成员变量oc中
grabber.start(true);
// grabber.start方法中,初始化的解码器信息存在放在grabber的成员变量oc中
//过滤器启动
filter.start();
AVFormatContext avformatcontext = grabber.getFormatContext();
// 文件内有几个媒体流(一般是视频流+音频流)
int streamNum = avformatcontext.nb_streams();
// 没有媒体流就不用继续了
if (streamNum < 1) {
log.info("视频文件格式不对");
return "error";
}
// 取得视频的帧率
int framerate = (int) grabber.getVideoFrameRate();
log.info("视频帧率:{},视频时长:{}秒,媒体流数量:{}", framerate, avformatcontext.duration() / 1000000,
streamNum);
// 遍历每一个流,检查其类型
for (int i = 0; i < streamNum; i++) {
AVStream avstream = avformatcontext.streams(i);
AVCodecParameters avcodecparameters = avstream.codecpar();
log.info("流的索引:{},编码器类型:{},编码器ID:{}", i, avcodecparameters.codec_type(),
avcodecparameters.codec_id());
}
// 源视频宽度
int frameWidth = grabber.getImageWidth();
// 源视频高度
int frameHeight = grabber.getImageHeight();
// 源音频通道数量
int audioChannels = grabber.getAudioChannels();
log.info("源视频宽度:{},源视频高度:{},音频通道数:{}", frameWidth, frameHeight, audioChannels);
AVFormatContext ifmt_ctx = grabber.getFormatContext();
AVStream inpVideoStream = null,
inpAudioStream = null;
if (ifmt_ctx != null) {
//获取Video的AVStream
inpVideoStream = ifmt_ctx.streams(grabber.getVideoStream());
//不能使用 inpVideoStream.sample_aspect_ratio()
AVRational SAR = inpVideoStream.codecpar().sample_aspect_ratio();
double a = (double) SAR.num() / (double) SAR.den();
//默认情况为1:1
aspectRatio = a == 0.0 ? 1.0 : a;
int SARden = SAR.den();
int SARnum = SAR.num();
log.info("SARden[{}], SARnum[{}]", SARden, SARnum);
}
recorder = new FFmpegFrameRecorder(resultPath, width, height, audioChannels);
// 设置H264编码
// recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
// 设置视频格式
recorder.setFormat(videoFormat);
// 使用原始视频的码率,若需要则自行修改码率
recorder.setVideoBitrate(grabber.getVideoBitrate());
// 一秒内的帧数,帧率
recorder.setFrameRate(framerate);
// 两个关键帧之间的帧数
recorder.setGopSize(framerate);
// 设置音频通道数,与视频源的通道数相等
recorder.setAudioChannels(grabber.getAudioChannels());
//给录制器设置SAR
recorder.setAspectRatio(aspectRatio);
recorder.start();
int videoFrameNum = 0;
int audioFrameNum = 0;
int dataFrameNum = 0;
// 持续从视频源取帧
while (null != (frame = grabber.grab())) {
// 有图像,就把视频帧加一
if (null != frame.image) {
videoFrameNum++;
//把已经解码后的视频图像像素塞进过滤器
filter.push(frame);
//取出过滤器处理后的图像像素数据
Frame filterFrame =filter.pullImage();
// 取出的每一帧,都保存到视频
recorder.record(filterFrame);
}
// 有声音,就把音频帧加一
if (null != frame.samples) {
audioFrameNum++;
// 取出的每一帧
recorder.record(frame);
}
// 有数据,就把数据帧加一
if (null != frame.data) {
dataFrameNum++;
}
}
log.info("转码完成,视频帧:{},音频帧:{},数据帧:{}", videoFrameNum, audioFrameNum, dataFrameNum);
} catch (Exception e) {
log.error(e.getMessage());
return "error";
} finally {
if (recorder != null) {
try {
recorder.close();
} catch (Exception e) {
log.info("recorder.close异常" + e);
}
}
try {
filter.close();
} catch (FrameFilter.Exception e) {
log.info("filter.close异常" + e);
}
try {
grabber.close();
} catch (FrameGrabber.Exception e) {
log.info("frameGrabber.close异常" + e);
}
}
return newFileName;
}
核心类介绍
FFmpegFrameGrabber
类
JavaCV库中的一个类,它用于从视频文件、摄像头流或网络流中获取帧数据。FFmpegFrameGrabber
实际上是基于FFmpeg
库构建的,FFmpeg
是一个非常强大的跨平台音频/视频处理工具集
主要功能
读取媒体文件
:可以从各种格式的视频文件中读取帧。实时流捕获
:能够从网络流(如RTSP、HTTP流等)获取视频帧。摄像头捕获
:可以连接到本地的摄像头设备,实时读取视频帧。音频处理
:除了视频帧之外,也可以处理音频数据
使用方法
- 初始化:创建FFmpegFrameGrabber实例并设置源
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("path/to/video.mp4");
- 启动:调用start()方法来打开视频源
grabber.start();
- 获取帧:通过调用grabFrame()来获取下一帧
Frame frame = grabber.grabFrame();
- 读取元数据:可以通过getFormatContext()、getStream()等方法来访问视频的元数据信息。
int frameRate = grabber.getFrameRate();
int imageWidth = grabber.getImageWidth();
int imageHeight = grabber.getImageHeight();
- 停止:完成操作后,记得关闭资源。
grabber.stop();
属性和方法
FFmpegFrameGrabber提供了许多属性和方法,以支持不同的应用场景,例如:
- setFormat(String format):设置媒体格式。
- setDuration(long duration):设置持续时间。
- getFrameRate():获取帧率。
- getImageWidth():获取图像宽度。
- getImageHeight():获取图像高度。
FFmpegFrameRecorder
类
JavaCV库中的一个类,主要用于将帧序列编码成视频文件或推送到网络流。它是基于FFmpeg
库构建的,可以用来创建视频文件或将视频流发送到其他地方(如RTSP服务器)
主要功能
视频编码
:将一系列帧编码成视频文件。音频编码
:如果需要的话,也可以处理音频数据。流式传输
:可以将视频流发送到网络上的其他位置。
使用方法
- 初始化:创建FFmpegFrameRecorder实例并设置目标
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height);
- 设置参数:配置视频编码器和其他选项。
recorder.setFormat("mp4"); // 设置输出格式,如MP4
recorder.setVideoCodec(FFmpegFrameRecorder.H264); // 设置视频编解码器,如H264
recorder.setFrameRate(frameRate); // 设置帧率
recorder.setVideoBitrate(bitrate); // 设置比特率
- 启动:调用start()方法来初始化视频编码器。
recorder.start();
- 记录帧:通过调用record(Frame frame)来记录每一帧。
recorder.record(frame);
- 结束录制:完成操作后,记得关闭资源。
recorder.stop(); // 结束录制
recorder.release(); // 释放资源
常用属性和方法
setFormat(String format)
:设置输出格式。setVideoCodec(int codec)
:设置视频编解码器。setFrameRate(double rate)
:设置帧率。setVideoBitrate(int bitrate)
:设置视频比特率。record(Frame frame)
:记录一帧。stop()
:停止录制。release()
:释放资源。
测试方法调用
//crop=width:height::x:y
videoConvert("D:\\testvedio\\1673244765519.mp4","D:\\testvedio\\",MessageFormat.format(
"crop={0}:{1}:{2}:{3}",
String.valueOf(360),
String.valueOf(360),
String.valueOf(0),
String.valueOf(0)),360,360,"mp4");