3提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
本次对于在背景介绍中所描述的FFmpegUtils的代码进行一个分析
1.
/**
* ffmpeg命令ffprobe,需设置ffmpeg环境变量
*/
private static final String FFPROBE = "ffprobe";
/**
* ffmpeg命令ffmpeg,需设置ffmpeg环境变量
*/
private static final String FFMPEG = "ffmpeg";
/**
* 创建大小为 1 的固定线程池,同时执行任务的只有一个,
* 其它的任务都放在LinkedBlockingQueue中排队等待执行
*/
ExecutorService executorService = Executors.newSingleThreadExecutor();
2. 获取视频信息 @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;
}
3. 获取封面图 @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 输入的文件名
* @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;
}
}