|
|
@@ -4,7 +4,7 @@ package com.tzld.supply.job;
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
|
-import com.tzld.supply.api.AigcFFmpegApiService;
|
|
|
+import com.tzld.supply.api.FFmpegApiService;
|
|
|
import com.tzld.supply.api.fish.FishApiService;
|
|
|
import com.tzld.supply.common.enums.ProduceVideoStatusEnum;
|
|
|
import com.tzld.supply.common.enums.SpiderContentStatusEnum;
|
|
|
@@ -26,10 +26,12 @@ import lombok.experimental.Accessors;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
@Slf4j
|
|
|
@Component
|
|
|
@@ -52,7 +54,10 @@ public class VideoGenerateJob {
|
|
|
@Autowired
|
|
|
private ProduceVideoMaterialMapper produceVideoMaterialMapper;
|
|
|
@Autowired
|
|
|
- private AigcFFmpegApiService ffmpegApiService;
|
|
|
+ private FFmpegApiService ffmpegApiService;
|
|
|
+
|
|
|
+ @Value("${produce.video.max.size:10}")
|
|
|
+ private Integer maxProduceSize;
|
|
|
|
|
|
/**
|
|
|
* 生成音频
|
|
|
@@ -68,9 +73,16 @@ public class VideoGenerateJob {
|
|
|
}
|
|
|
Long endTime = startTime + 86400 * 1000;
|
|
|
List<SpiderContent> contentList = new ArrayList<>();
|
|
|
+ Integer produceCount = maxProduceSize;
|
|
|
if (StringUtils.isNotBlank(param)) {
|
|
|
contentList.add(spiderContentMapper.selectByPrimaryKey(Long.parseLong(param)));
|
|
|
} else {
|
|
|
+ // 检查是否有正在处理的视频
|
|
|
+ Long processingCount = countProcessionVideo();
|
|
|
+ if (processingCount >= maxProduceSize) {
|
|
|
+ return ReturnT.FAIL;
|
|
|
+ }
|
|
|
+ produceCount = Math.toIntExact(produceCount - processingCount);
|
|
|
contentList = spiderMapperExt.getVideoGenerateContent(SpiderContentStatusEnum.PASSED.getCode(),
|
|
|
startTime, endTime);
|
|
|
}
|
|
|
@@ -78,6 +90,10 @@ public class VideoGenerateJob {
|
|
|
return ReturnT.SUCCESS;
|
|
|
}
|
|
|
for (SpiderContent content : contentList) {
|
|
|
+ if (produceCount == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ produceCount--;
|
|
|
// 检查是否存在模型
|
|
|
List<AiModelTts> ttsList = ttsMapper.selectByExample(new AiModelTtsExample());
|
|
|
// 随机选择一个模型
|
|
|
@@ -116,6 +132,12 @@ public class VideoGenerateJob {
|
|
|
return ReturnT.SUCCESS;
|
|
|
}
|
|
|
|
|
|
+ private Long countProcessionVideo() {
|
|
|
+ ProduceVideoExample example = new ProduceVideoExample();
|
|
|
+ example.createCriteria().andStatusEqualTo(ProduceVideoStatusEnum.PROCESSING.getCode());
|
|
|
+ return produceVideoMapper.countByExample(example);
|
|
|
+ }
|
|
|
+
|
|
|
@Getter
|
|
|
@Setter
|
|
|
@Accessors(chain = true)
|
|
|
@@ -170,8 +192,8 @@ public class VideoGenerateJob {
|
|
|
|
|
|
// 线程池
|
|
|
private final ExecutorService pool = new ThreadPoolExecutor(
|
|
|
- 5,
|
|
|
- 5,
|
|
|
+ 1,
|
|
|
+ 1,
|
|
|
0L, TimeUnit.SECONDS,
|
|
|
new LinkedBlockingQueue<>(10000),
|
|
|
new ThreadFactoryBuilder().setNameFormat("imageToVideo-%d").build(),
|
|
|
@@ -190,6 +212,7 @@ public class VideoGenerateJob {
|
|
|
String command = randomImageToVideoAnimation(image.getOssKey(), outputVideoName);
|
|
|
CommandParam commandParam = CommandParam.builder()
|
|
|
.ffmpegCommand(command)
|
|
|
+ .inputFile(image.getOssKey())
|
|
|
.outputFile(outputVideoName)
|
|
|
.outputType("video")
|
|
|
.build();
|
|
|
@@ -219,24 +242,18 @@ public class VideoGenerateJob {
|
|
|
switch (new Random().nextInt(2)) {
|
|
|
// 放大
|
|
|
case 0:
|
|
|
- command = String.format("ffmpeg -loop 1 -i %s -filter_complex " +
|
|
|
- "\"[0:v]scale=720:1280:force_original_aspect_ratio=increase,boxblur=40:40,crop=720:1280[bg];" +
|
|
|
- "[0:v]scale=720:1280:force_original_aspect_ratio=decrease,setsar=1[fg];" +
|
|
|
- "[bg][fg]overlay=(W-w)/2:(H-h)/2,format=yuv420p[base];" +
|
|
|
- "[base]zoompan=z='min(zoom+0.003,1.3)':d=75:x='(iw/2)-(iw/zoom/2)':y='(ih/2)-(ih/zoom/2)':s=720x1280:fps=25[zoom]\" " +
|
|
|
- "-map \"[zoom]\" -t 3 -y \"%s\"",
|
|
|
+ command = String.format("ffmpeg -y -loop 1 -i %s -t 3 -vf " +
|
|
|
+ "\"scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black\" " +
|
|
|
+ "-c:v libx264 -pix_fmt yuv420p %s",
|
|
|
imageUrl, outputVideoName);
|
|
|
break;
|
|
|
// 平移
|
|
|
case 1:
|
|
|
// 背景模糊
|
|
|
default:
|
|
|
- command = String.format("ffmpeg -y -loop 1 -i %s -t 3 -r 25 -filter_complex " +
|
|
|
- "\"[0:v]scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280,boxblur=10:1[bg];" +
|
|
|
- "[0:v]scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:color=black@0[fg];" +
|
|
|
- "[fg]zoompan=z='min(1.00+0.0003*on,1.03)':d=75:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=720x1280[fg_anim];" +
|
|
|
- "[bg][fg_anim]overlay=0:0:format=auto,format=yuv420p\" " +
|
|
|
- "-c:v libx264 -pix_fmt yuv420p -movflags +faststart %s",
|
|
|
+ command = String.format("ffmpeg -y -loop 1 -i %s -t 3 -vf " +
|
|
|
+ "\"scale=720:1280:force_original_aspect_ratio=decrease,pad=720:1280:(ow-iw)/2:(oh-ih)/2:black\" " +
|
|
|
+ "-c:v libx264 -pix_fmt yuv420p %s",
|
|
|
imageUrl, outputVideoName);
|
|
|
}
|
|
|
return command;
|
|
|
@@ -282,7 +299,7 @@ public class VideoGenerateJob {
|
|
|
String endTime = formatSecondsToTime(endSeconds);
|
|
|
// 截取视频片段
|
|
|
VideoTimeCutParam timeCutParam = VideoTimeCutParam.builder()
|
|
|
- .videoUrl(video.getUrl())
|
|
|
+ .videoUrl(video.getOssKey())
|
|
|
.startTime(lastEndTime)
|
|
|
.endTime(endTime)
|
|
|
.build();
|
|
|
@@ -362,15 +379,20 @@ public class VideoGenerateJob {
|
|
|
return ReturnT.SUCCESS;
|
|
|
}
|
|
|
for (ProduceVideo produceVideo : produceList) {
|
|
|
- // 检查是否存在素材
|
|
|
+ // 检查是否存在素材 todo 判断素材是否生成完成
|
|
|
+ List<SpiderContentMedia> mediaList = getSpiderContentMediaByContentId(produceVideo.getContentId());
|
|
|
+ Integer totalMediaSize = mediaList.size();
|
|
|
List<ProduceVideoMaterial> materialList = getVideoMaterialList(produceVideo);
|
|
|
- if (materialList.size() < 10) {
|
|
|
+ List<Long> existMediaIds = materialList.stream().map(ProduceVideoMaterial::getSourceMediaId).collect(Collectors.toList());
|
|
|
+ mediaList.removeIf(o -> existMediaIds.contains(o.getId()));
|
|
|
+ if (materialList.size() < 10 || (mediaList.size() * 1.0 / totalMediaSize) > 0.2) {
|
|
|
continue;
|
|
|
}
|
|
|
ProduceVideoAudio audio = getProduceAudio(produceVideo);
|
|
|
if (audio == null) {
|
|
|
continue;
|
|
|
}
|
|
|
+ audio.setDuration(audio.getDuration() + 500);
|
|
|
// short video to long video
|
|
|
// 视频素材拼接长视频 随机排序
|
|
|
Collections.shuffle(materialList);
|
|
|
@@ -381,11 +403,14 @@ public class VideoGenerateJob {
|
|
|
.pretreatmentSwitch(1)
|
|
|
.build();
|
|
|
String concatVideoUrl = ffmpegApiService.videoConcat(concatParam);
|
|
|
+ if (StringUtils.isBlank(concatVideoUrl)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
// 按音频时长剪切
|
|
|
VideoTimeCutParam timeCutParam = VideoTimeCutParam.builder()
|
|
|
.videoUrl(concatVideoUrl)
|
|
|
.startTime("00:00:00")
|
|
|
- .endTime(formatSecondsToTime(audio.getDuration() / 1000))
|
|
|
+ .endTime(formatSecondsToTime(audio.getDuration() / 1000 ))
|
|
|
.build();
|
|
|
String cutVideoUrl = ffmpegApiService.timeCutVideo(timeCutParam);
|
|
|
// add audio
|
|
|
@@ -394,6 +419,7 @@ public class VideoGenerateJob {
|
|
|
cutVideoUrl, audio.getUrl(), videoName);
|
|
|
CommandParam commandParam = CommandParam.builder()
|
|
|
.ffmpegCommand(command)
|
|
|
+ .inputFile(cutVideoUrl)
|
|
|
.outputFile(videoName)
|
|
|
.outputType("video")
|
|
|
.build();
|
|
|
@@ -401,13 +427,21 @@ public class VideoGenerateJob {
|
|
|
String fileName = String.format("supply/produce/video/%s", videoName);
|
|
|
String finalVideoUrl = AliOssFileTool.downloadAndSaveInOSS(fileName, commandResponseData, "video/mp4");
|
|
|
// 更新视频地址
|
|
|
- produceVideo.setUrl(finalVideoUrl);
|
|
|
- produceVideo.setStatus(ProduceVideoStatusEnum.PASSED.getCode());
|
|
|
- produceVideoMapper.updateByPrimaryKeySelective(produceVideo);
|
|
|
+ if (StringUtils.isNotBlank(finalVideoUrl)) {
|
|
|
+ produceVideo.setUrl(finalVideoUrl);
|
|
|
+ produceVideo.setStatus(ProduceVideoStatusEnum.PASSED.getCode());
|
|
|
+ produceVideoMapper.updateByPrimaryKeySelective(produceVideo);
|
|
|
+ }
|
|
|
}
|
|
|
return ReturnT.SUCCESS;
|
|
|
}
|
|
|
|
|
|
+ private List<SpiderContentMedia> getSpiderContentMediaByContentId(Long contentId) {
|
|
|
+ SpiderContentMediaExample example = new SpiderContentMediaExample();
|
|
|
+ example.createCriteria().andContentIdEqualTo(contentId).andStatusEqualTo(1);
|
|
|
+ return spiderContentMediaMapper.selectByExample(example);
|
|
|
+ }
|
|
|
+
|
|
|
private List<String> selectMaterial(List<ProduceVideoMaterial> materialList, ProduceVideoAudio audio) {
|
|
|
Integer duration = audio.getDuration();
|
|
|
List<String> videoUrls = new ArrayList<>();
|