使用java技术栈来对视频进行压缩和剪辑。
方案一:jave2实现–底层封装的是ffmpeg,但是代码不在维护了。
github地址:https://github.com/a-schild/jave2
import ws.schild.jave.Encoder;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.VideoInfo;
import java.io.File;
import java.math.BigDecimal;
/**
* 视频压缩工具类--参考网上的资料测试的 JAVE2
* github: https://github.com/a-schild/jave2
*/
public class VideoCompressorUtil {
public static final Logger logger = LoggerFactory.getLogger(VideoUtil.class);
/**
* 传视频File对象(这是一个具体的文件),返回压缩后File对象信息
*
* @param source
*/
public static File compressionVideo(File source, String picName) {
if (source == null) {
return null;
}
String newPath = source.getAbsolutePath().substring(0, source.getAbsolutePath().lastIndexOf(File.separator)).concat(File.separator).concat(picName);
File target = new File(newPath);
try {
MultimediaObject object = new MultimediaObject(source);
// 根据视频大小来判断是否需要进行压缩,
int maxSize = 200;
double mb = Math.ceil(source.length() / 1048576);
int second = (int) object.getInfo().getDuration() / 1000;
BigDecimal bd = new BigDecimal(String.format("%.4f", mb / second));
System.out.println("开始压缩视频了--> 视频每秒平均 " + bd + " MB ");
// 视频 > 200MB, 或者每秒 > 0.5 MB 才做压缩
boolean temp = mb > maxSize || bd.compareTo(new BigDecimal(0.5)) > 0;
if (temp) {
long time = System.currentTimeMillis();
// 视频压缩参数
int maxAudioBitRate = 128000;
int maxSamplingRate = 44100;
int maxVideoBitRate = 800000;
int maxFrameRate = 30;
// 音频属性设置
AudioInfo audioInfo = object.getInfo().getAudio();
AudioAttributes audio = new AudioAttributes();
// 设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 128000 = 182kb)
if (audioInfo.getBitRate() > maxAudioBitRate) {
audio.setBitRate(new Integer(maxAudioBitRate));
}
// 设置重新编码的音频流中使用的声道数(1 =单声道,2 = 双声道(立体声))。
// 如果未设置任何声道值,则编码器将选择默认值 0。
audio.setChannels(audioInfo.getChannels());
// 采样率越高声音的还原度越好,文件越大
// 设置音频采样率,单位:赫兹 hz
// 设置编码时候的音量值,未设置为0;如果256,则音量值不会改变
audio.setVolume(256);
if (audioInfo.getSamplingRate() > maxSamplingRate) {
audio.setSamplingRate(maxSamplingRate);
}
// 视频编码属性配置
VideoInfo videoInfo = object.getInfo().getVideo();
VideoAttributes video = new VideoAttributes();
//设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 800000 = 800kb)
if (videoInfo.getBitRate() > maxVideoBitRate) {
video.setBitRate(maxVideoBitRate);
}
// 视频帧率:15 f / s 帧率越低,效果越差
// 设置视频帧率(帧率越低,视频会出现断层,越高让人感觉越连续),视频帧率(Frame rate)是用于测量显示帧数的量度。
// 所谓的测量单位为每秒显示帧数(Frames per Second,简:FPS)或“赫兹”(Hz)。
if (videoInfo.getFrameRate() > maxFrameRate) {
video.setFrameRate(maxFrameRate);
}
EncodingAttributes attr = new EncodingAttributes();
attr.setOutputFormat("mp4");
attr.setAudioAttributes(audio);
attr.setVideoAttributes(video);
// 设置线程数
attr.setEncodingThreads(Runtime.getRuntime().availableProcessors() / 2);
Encoder encoder = new Encoder();
encoder.encode(new MultimediaObject(source), target, attr);
System.out.println("压缩总耗时:" + (System.currentTimeMillis() - time) / 1000);
return target;
}
} catch (Exception e) {
logger.error("压缩视频时的错误信息:{}", e.getMessage(), e);
}
return source;
}
public static void main(String[] args) {
compressionVideo(new File("C:\\xf\\2024-06.mp4"), "test2.mp4");
}
}
方案2:采用javacv,同样是封装ffmpeg,资料多
gitHub:https://github.com/bytedeco/javacv
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.FrameRecorder;
/**
* 视频压缩工具类--利用javaCV 实现
*/
public class VideoCompressor {
public static final Logger logger = LoggerFactory.getLogger(VideoCompressor.class);
public static void compressVideo(String inputPath, String outputPath, double fps, double bitrate) throws FrameGrabber.Exception, FrameRecorder.Exception {
// 参数校验
if (inputPath == null || outputPath == null || inputPath.isEmpty() || outputPath.isEmpty()) {
throw new IllegalArgumentException("输入路径和输出路径不能为空");
}
if (fps <= 0 || bitrate <= 0) {
throw new IllegalArgumentException("帧率和比特率必须大于0");
}
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorder recorder = null;
try {
// 初始化帧抓取器
grabber = new FFmpegFrameGrabber(inputPath);
grabber.start();
// 初始化帧记录器
recorder = new FFmpegFrameRecorder(outputPath, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
recorder.setFormat("mp4"); // 输出格式
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 视频编码器
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 音频编码器
recorder.setFrameRate(fps); // 帧率
recorder.setVideoBitrate((int) (bitrate * 1024)); // 视频比特率
recorder.setAudioBitrate(128 * 1024); // 音频比特率
recorder.setSampleRate(grabber.getSampleRate()); // 音频采样率
recorder.setAudioChannels(grabber.getAudioChannels()); // 音频通道数
recorder.start();
Frame frame;
while ((frame = grabber.grab()) != null) {
if (frame.image != null) {
recorder.record(frame);
}
if (frame.samples != null) {
recorder.recordSamples(frame.samples);
}
}
// 清理资源
} catch (Exception e) {
// 记录详细的异常信息和上下文
logger.error("视频压缩过程中发生异常:{} ",e.getMessage(),e);
} finally {
// 确保资源的释放
if (recorder != null) {
try {
recorder.stop();
} catch (Exception e) {
// 异常处理,资源释放失败的情况
logger.error("释放录制器资源时发生异常:{} ",e.getMessage(),e);
}
}
if (grabber != null) {
try {
grabber.stop();
} catch (Exception e) {
// 异常处理,资源释放失败的情况
logger.error("释放录制器资源时发生异常:{} ",e.getMessage(),e);
}
}
}
}
public static void main(String[] args) {
try {
String inputFilePath = "C:\\xf\\2022.8-2023.1.mp4";
String outputFilePath = "C:\\xf\\compressed_video.mp4";
double targetFps = 30; // 目标帧率
double targetBitrate = 0.5; // 目标比特率,这里是原视频的50%
System.out.println("Starting video compression...");
long time = System.currentTimeMillis();
VideoCompressor.compressVideo(inputFilePath, outputFilePath, targetFps, targetBitrate);
System.out.println("Video compression completed.");
System.out.println((System.currentTimeMillis() - time) / 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
视频剪辑
使用的也是javaCV
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VideoClippingUtil {
public static final Logger logger = LoggerFactory.getLogger(VideoClippingUtil.class);
public static void clipVideo(String inputPath, String outputPath, double startTime, double duration) {
// 参数校验
if (inputPath == null || outputPath == null || startTime < 0 || duration < 0) {
logger.error("Invalid input parameters.");
return;
}
FFmpegFrameGrabber grabber = null;
FFmpegFrameRecorder recorder = null;
try {
grabber = new FFmpegFrameGrabber(inputPath);
grabber.start();
// 初始化recorder
recorder = initializeRecorder(outputPath, grabber);
// 处理帧
processFrames(grabber, recorder, (int) (startTime * grabber.getFrameRate()),
(int) Math.min((startTime + duration) * grabber.getFrameRate(), grabber.getLengthInFrames()));
} catch (Exception e) {
logger.error("Failed to clip video.", e);
} finally {
// 确保资源被正确关闭
if (recorder != null) {
try {
recorder.stop();
} catch (Exception e) {
logger.error("Failed to stop recorder.", e);
}
}
if (grabber != null) {
try {
grabber.stop();
} catch (Exception e) {
logger.error("Failed to stop grabber.", e);
}
}
}
}
private static FFmpegFrameRecorder initializeRecorder(String outputPath, FFmpegFrameGrabber grabber) throws Exception {
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
recorder.setFormat("mp4");
recorder.setFrameRate(grabber.getFrameRate());
recorder.setVideoQuality(0);
recorder.start();
return recorder;
}
private static void processFrames(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder, int startFrame, int endFrame) throws Exception {
for (int i = startFrame; i < endFrame; i++) {
if ((i % grabber.getFrameRate()) == 0) {
logger.info("Processing frame " + i);
}
Frame frame = grabber.grabImage();
if (frame != null) {
recorder.record(frame);
}
}
}
public static void main(String[] args) {
String inputFilePath = "C:\\xf\\test.mp4";
String outputFilePath = "C:\\xf\\clip.mp4";
double startTimeSeconds = 10; // 开始时间,单位秒
double durationSeconds = 30; // 持续时间,单位秒
clipVideo(inputFilePath, outputFilePath, startTimeSeconds, durationSeconds);
}
}