2021SC@SDUSC
目录
项目名称:multimedia-utils
文档
视频工具FFmpegUtils
项目名称: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 {
private static final String FFPROBE = "ffprobe";
private static final String FFMPEG = "ffmpeg";
ExecutorService executorService = Executors.newSingleThreadExecutor();
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 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;
}
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;
}
public String createCover(String tempDirectory, String inputFileName) {
return this.createCover(tempDirectory, inputFileName, null);
}
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;
}
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);
}
});
}
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());
}
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;
}
private Boolean isSetUp(Integer originalParameter, Integer targetParameter) {
if (originalParameter == null || targetParameter == null) {
return false;
}
if (originalParameter <= targetParameter) {
return false;
}
return true;
}
}



