2021SC@SDUSC
目录
项目名称:multimedia-utils
博客五
文档
上篇博客我们介绍了图片工具,并且给出了详细的介绍。下面我们介绍其他各个文件所实现的功能和用处。接下来看视频工具FFmpegUtils
视频工具FFmpegUtils
方法
1.获取视频信息
getInfo(tempDirectory,inputFileName)
输入参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
tempDirectory | String | 是 | 临时文件目录 |
inputFileName | String | 是 | 输入文件名 |
输出参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
format | FFFormat | 是 | 文件信息 |
videoInfo | VideoInfo | 是 | 视频信息 |
audioInfo | AudioInfo | 是 | 音频信息 |
其中:
FFFormat
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
filename | String | 是 | 文件名全路径名 |
duration | String | 是 | 文件时长 |
size | String | 是 | 文件大小 |
bitRate | String | 是 | 文件比特率 |
VideoInfo
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
codecName | String | 是 | 视频 编码名称 |
codecType | String | 是 | 类型 video |
width | Integer | 是 | 视频 宽度 |
height | Integer | 是 | 视频 高度 |
frameRate | Integer | 是 | 视频 帧率 |
duration | Integer | 是 | 视频 时长 |
bitRate | Integer | 是 | 视频 比特率 |
nbFrames | Integer | 是 | 视频 总帧数 |
AudioInfo
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
codecName | String | 是 | 音频 编码名称 |
codecType | String | 是 | 类型 audio |
duration | Integer | 是 | 音频 时长 |
bitRate | Integer | 是 | 音频 比特率 |
sampleRate | Integer | 是 | 音频 采样率 |
channels | Integer | 是 | 音频 声道数 1:单声道 2:双声道 |
2.截取视频封面
createCover(tempDirectory, inputFileName, outputFileSuffix)
createCover(tempDirectory, inputFileName)
输入参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
tempDirectory | String | 是 | 临时文件目录 |
inputFileName | String | 是 | 输入文件名 |
outputFileSuffix | Suffix | 否 | 输出文件格式,默认jpg |
输出参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
outputFileName | String | 是 | 输出文件名 |
3.视频压缩
putCompressionTask(tempDirectory, inputFileName, compressionAttributes)
putCompressionTask(tempDirectory, inputFileName, compressionAttributes, outputFileName)
输入参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
tempDirectory | String | 是 | 临时文件目录 |
inputFileName | String | 是 | 输入文件名 |
compressionAttributes | CompressionAttributes | 是 | 压缩参数 |
outputFileName | String | 否 | 输出文件名,默认UUID生成 |
其中:
CompressionAttributes
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
videoAttributes | VideoAttributes | 否 | 视频压缩参数 |
audioAttributes | AudioAttributes | 否 | 音频压缩参数 |
progressUrl | String | 是 | 压缩完成后的回调地址 |
VideoAttributes
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
codec | VideoCodec | 否 | 视频编码(默认libx264,libx265) |
maxFps | Integer | 否 | 最大帧率 |
videoSize | VideoSize | 否 | 视频分辨率(hd480,hd720,hd1080,800x800) |
maxDuration | Integer | 否 | 最大时长 |
maxBitRate | Integer | 否 | 视频最大比特率 |
AudioAttributes
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
maxBitRate | Integer | 否 | 音频最大比特率 |
maxSamplingRate | Integer | 否 | 音频最大采样率 |
输出参数
参数 | 类型 | 必需 | 注释 |
---|---|---|---|
outputFileName | String | 是 | 输出文件名 |
下面我们给出源码(我们已经在源码中给出了相应的注释)
import com.whty.zdxt.multimedia.enumeration.VideoSize;
import com.whty.zdxt.multimedia.pojo.AudioInfo;
import com.whty.zdxt.multimedia.pojo.FFStream;
import com.whty.zdxt.multimedia.pojo.FFmpegInfo;
import com.whty.zdxt.multimedia.pojo.VideoInfo;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FFmpegUtils {
/**
* ffmpeg命令ffprobe,需设置ffmpeg环境变量
*/
private static final String FFPROBE = "ffprobe";
/**
* ffmpeg命令ffmpeg,需设置ffmpeg环境变量
*/
private static final String FFMPEG = "ffmpeg";
/**
* 创建大小为 1 的固定线程池,同时执行任务的只有一个,
* 其它的任务都放在LinkedBlockingQueue中排队等待执行
*/
ExecutorService executorService = Executors.newSingleThreadExecutor();
/**
* 获取视频信息
*
* @param tempDirectory 临时文件目录
* @param inputFileName 视频文件名
* @return 文件信息
*/
public FFmpegInfo getInfo(String tempDirectory, String inputFileName) {
if (!FileUtils.checkFileName(inputFileName)) {
throw new RuntimeException("输入文件名格式错误");
}
StringBuilder command = new StringBuilder();
command.append(FFPROBE);
command.append(" -v quiet -print_format json -show_format -show_streams ");
command.append(FileUtils.getFilePath(tempDirectory, inputFileName));
String response = RuntimeUtils.execSuccessful(command.toString());
FFmpegInfo fFmpegInfo = JsonUtils.toBean(response, FFmpegInfo.class);
if (fFmpegInfo.getStreams() == null) {
throw new RuntimeException("文件不存在");
}
List<FFStream> streams = fFmpegInfo.getStreams();
for (FFStream ffStream : streams) {
if ("audio".equals(ffStream.getCodecType())) {
AudioInfo audioInfo = new AudioInfo();
BeanUtils.copyProperties(ffStream, audioInfo);
fFmpegInfo.setAudioInfo(audioInfo);
} else if ("video".equals(ffStream.getCodecType())) {
VideoInfo videoInfo = new VideoInfo();
BeanUtils.copyProperties(ffStream, videoInfo);
String rFrameRate = ffStream.getrFrameRate();
String[] split = rFrameRate.split("/");
videoInfo.setFrameRate(Integer.valueOf(split[1]) == 0 ? 0 : (Integer.valueOf(split[0]) / Integer.valueOf(split[1])));
fFmpegInfo.setVideoInfo(videoInfo);
}
}
return fFmpegInfo;
}
/**
* 获取封面图
*
* @param tempDirectory 临时文件目录
* @param inputFileName 视频文件名
* @param outputFileSuffix 输出文件后缀名
* @return 封面文件名
*/
public String createCover(String tempDirectory, String inputFileName, Suffix outputFileSuffix) {
if (!FileUtils.checkFileName(inputFileName)) {
throw new RuntimeException("输入文件名格式错误");
}
// 设置封面图格式
String suffix = outputFileSuffix == null ? Suffix.JPG.getCode() : outputFileSuffix.getCode();
String outputFileName = FileUtils.createFileName(suffix);
StringBuilder command = new StringBuilder();
command.append(FFMPEG);
command.append(" -i ");
command.append(FileUtils.getFilePath(tempDirectory, inputFileName));
command.append(" -ss 1 -t 1 -r 1 -f image2 ");
command.append(FileUtils.getFilePath(tempDirectory, outputFileName));
RuntimeUtils.execSF(command.toString());
return outputFileName;
}
/**
* 获取封面图
*
* @param tempDirectory 临时文件目录
* @param inputFileName 视频文件名
* @return 封面文件名
*/
public String createCover(String tempDirectory, String inputFileName) {
return this.createCover(tempDirectory, inputFileName, null);
}
/**
* 将任务放入执行队列中
*
* @param tempDirectory 临时文件目录
* @param inputFileName 输入的文件名
* @param compressionAttributes 压缩参数
* @return 输出文件名
*/
public String putCompressionTask(String tempDirectory, String inputFileName, CompressionAttributes compressionAttributes) {
// 生成输出文件名
String suffix = FileUtils.getSuffix(inputFileName);
String outputFileName = FileUtils.createFileName(suffix);
this.executeTask(tempDirectory, inputFileName, compressionAttributes, outputFileName);
return outputFileName;
}
/**
* 将任务放入执行队列中
*
* @param tempDirectory 临时文件目录
* @param inputFileName 输入的文件名
* @param compressionAttributes 压缩参数
* @param outputFileName 输出的文件名
* @return 输出文件名
*/
public String putCompressionTask(String tempDirectory, String inputFileName, CompressionAttributes compressionAttributes, String outputFileName) {
this.executeTask(tempDirectory, inputFileName, compressionAttributes, outputFileName);
return outputFileName;
}
/**
* 添加压缩任务
*/
private void executeTask(String tempDirectory, String inputFileName, CompressionAttributes compressionAttributes, String outputFileName) {
executorService.execute(new Runnable() {
public void run() {
compressionVideo(tempDirectory, inputFileName, compressionAttributes, outputFileName);
}
});
}
/**
* 压缩视频文件
*
* @param tempDirectory 临时文件目录
* @param inputFileName 原视频文件名
* @param compressionAttributes 压缩参数
* @param outputFileName 压缩视频文件名
*/
private void compressionVideo(String tempDirectory, String inputFileName, CompressionAttributes compressionAttributes, String outputFileName) {
if (!FileUtils.checkFileName(inputFileName)) {
throw new RuntimeException("输入文件名格式错误");
}
if (!FileUtils.checkFileName(outputFileName)) {
throw new RuntimeException("输出文件名格式错误");
}
if (compressionAttributes == null) {
throw new RuntimeException("缺少压缩参数");
}
String progressUrl = compressionAttributes.getProgressUrl();
if (progressUrl == null) {
throw new RuntimeException("缺少压缩完成回调URL");
}
VideoAttributes videoAttributes = compressionAttributes.getVideoAttributes();
AudioAttributes audioAttributes = compressionAttributes.getAudioAttributes();
// 获取原视频信息
FFmpegInfo info = this.getInfo(tempDirectory, inputFileName);
VideoInfo videoInfo = info.getVideoInfo();
AudioInfo audioInfo = info.getAudioInfo();
StringBuilder command = new StringBuilder();
command.append(FFMPEG);
// 设置视频源
command.append(" -i ");
command.append(FileUtils.getFilePath(tempDirectory, inputFileName));
command.append(" ");
// 设置视频属性
if (videoAttributes != null && videoInfo != null) {
// 设置视频编码
if (videoAttributes.getCodec() != null) {
command.append("-c:v ");
command.append(videoAttributes.getCodec().getCode());
command.append(" ");
}
// 设置视频帧率
if (this.isSetUp(videoInfo.getFrameRate(), videoAttributes.getMaxFps())) {
command.append("-r ");
command.append(videoAttributes.getMaxFps());
command.append(" ");
}
// 设置视频比特率
if (this.isSetUp(videoInfo.getBitRate(), videoAttributes.getMaxBitRate())) {
command.append("-b:v ");
command.append(videoAttributes.getMaxBitRate());
command.append(" ");
}
// 设置视频宽度
VideoSize videoSize = videoAttributes.getVideoSize();
if (videoSize != null) {
command.append("-s ");
command.append(videoSize.getCode());
command.append(" ");
}
// 设置视频时长
if (this.isSetUp(Double.valueOf(videoInfo.getDuration()).intValue(), videoAttributes.getMaxDuration())) {
command.append("-t ");
command.append(videoAttributes.getMaxDuration());
command.append(" ");
}
}
// 设置音频属性
if (audioAttributes != null && audioInfo != null) {
// 设置音频比特率
if (this.isSetUp(audioInfo.getBitRate(), audioAttributes.getMaxBitRate())) {
command.append("-b:a ");
command.append(audioAttributes.getMaxBitRate());
command.append(" ");
}
// 设置音频采样率
if (this.isSetUp(audioInfo.getSampleRate(), audioAttributes.getMaxSamplingRate())) {
command.append("-ar ");
command.append(audioAttributes.getMaxSamplingRate());
command.append(" ");
}
}
command.append(FileUtils.getFilePath(tempDirectory, outputFileName));
command.append(" -progress ");
command.append(progressUrl);
RuntimeUtils.execFailure(command.toString());
}
/**
* 截取文件(截取相应时长 start秒 - end秒)
*
* @param tempDirectory 临时文件目录
* @param inputFileName 输入文件名
* @param start 截取开始时间 单位s
* @param end 截取结束时间 单位s
* @return 输出文件名
*/
public String copy(String tempDirectory, String inputFileName, Integer start, Integer end) {
if (!FileUtils.checkFileName(inputFileName)) {
throw new RuntimeException("输入文件名格式错误");
}
if (start == null || end == null) {
throw new RuntimeException("缺少必要参数");
}
String suffix = FileUtils.getSuffix(inputFileName);
String outputFileName = FileUtils.createFileName(suffix);
StringBuilder command = new StringBuilder();
command.append(FFMPEG);
command.append(" -i ");
command.append(FileUtils.getFilePath(tempDirectory, inputFileName));
command.append(" -ss ");
command.append(start);
command.append(" -c copy -to ");
command.append(end);
command.append(" ");
command.append(FileUtils.getFilePath(tempDirectory, outputFileName));
RuntimeUtils.execFailure(command.toString());
return outputFileName;
}
/**
* 判断是否设置属性
*
* @param originalParameter 原视频属性
* @param targetParameter 目标属性
*/
private Boolean isSetUp(Integer originalParameter, Integer targetParameter) {
if (originalParameter == null || targetParameter == null) {
return false;
}
if (originalParameter <= targetParameter) {
return false;
}
return true;
}
}