随着短视频生态的发展,视频生产的质量和效率已经越来越重要,本篇介绍如何用一段音乐和多张图片,快速自动生成一段卡点相册视频。
概述
背景
卡点视频是短视频生产中常用的效果,将素材的开始结束时间卡在音乐的节奏点上,能够带动观众的感官和气氛。本篇介绍如何用一段音乐和多张图片,快速自动生成一段卡点相册视频。
本文基于智能媒体服务IMS,用户通过组装Timeline,即可对云端的音视频素材进行剪辑,最后附有示例代码,帮助大家更快速上手。
目标读者
有短视频批量制作的商家,或视频批量生产工具的开发者。
方案介绍
先来看成片示例
生产此视频的素材是一段音乐和一批图片。
整体流程:1、调用智能生产接口识别音乐的节奏点信息。
2、根据节奏点信息封装Timeline。
3、调用剪辑合成接口,传入Timeline,合成最终成片。
其中,Timeline格式参考Timeline配置说明,根据音乐节奏点信息,可以很方便的生成Timeline:
如果要为图片添加转场,需要为每张图片的时长加上转场时长:
本文示例中还为图片增加了背景模糊效果,具体见代码示例。
还可以为视频增加字幕、贴图、特效,可参考短视频常用功能。
方案实施
接口说明
提供文字转语音、人声识别、节奏检测等能力
输入Timeline和成片地址,提交剪辑合成作业,返回剪辑作业JobId
示例代码
package com.aliyun.ice.util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.ice20201109.Client;
import com.aliyun.ice20201109.models.GetMediaProducingJobRequest;
import com.aliyun.ice20201109.models.GetMediaProducingJobResponse;
import com.aliyun.ice20201109.models.SubmitMediaProducingJobRequest;
import com.aliyun.ice20201109.models.SubmitMediaProducingJobResponse;
import com.aliyun.mts20140618.models.QueryIProductionJobRequest;
import com.aliyun.mts20140618.models.QueryIProductionJobResponse;
import com.aliyun.mts20140618.models.SubmitIProductionJobRequest;
import com.aliyun.mts20140618.models.SubmitIProductionJobResponse;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.teaopenapi.models.Config;
import com.google.common.io.CharStreams;
import org.apache.commons.io.Charsets;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Create by oushu
* Date 2022/07/11 上午11:57
*/
public class BatchProduceBeatMusicRelease {
private String accessKeyId;
private String accessKeySecret;
private String bucket;
private String regionId;
private OSS ossClient;
private Client iceClient;
private com.aliyun.mts20140618.Client mpsClient;
public void initClient() throws Exception {
accessKeyId = "your ak";
accessKeySecret = "your sk";
bucket = "your-bucket";
regionId = "cn-shanghai";
iceClient = createIceClient();
ossClient = createOssClient();
mpsClient = createMpsClient();
}
public static void main(String[] args) throws Exception {
BatchProduceBeatMusicRelease batchProduceVideo = new BatchProduceBeatMusicRelease();
batchProduceVideo.initClient();
batchProduceVideo.batchProduceVideo();
}
public void batchProduceVideo() throws Exception {
String musicUrl = "https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-music.mp3";
List<String> mediaUrls = new ArrayList();
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-image1.jpg");
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-image2.jpg");
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-image3.jpg");
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-movie1.mp4");
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-movie2.mp4");
mediaUrls.add("https://your-bucket.oss-cn-shanghai.aliyuncs.com/your-movie3.mp4");
int targetCount = 1;
for (int i = 0; i < targetCount; i++) {
Collections.shuffle(mediaUrls);
List<String> subImageUrls = mediaUrls.subList(0, 15);
produceSingleVideo(musicUrl, subImageUrls);
}
}
public void produceSingleVideo(String musicUrl, List<String> imageUrls) throws Exception {
System.out.println("Start produce!!!!");
// 提交节奏检测任务
String input = musicUrl;
SubmitIProductionJobRequest submitIProductionJobRequest = new SubmitIProductionJobRequest();
submitIProductionJobRequest.setFunctionName("AudioBeatDetection");
submitIProductionJobRequest.setInput(input);
submitIProductionJobRequest.setOutput("oss://" + bucket + ".oss-" + regionId + ".aliyuncs.com/iproduction/{source}-{timestamp}.txt");
SubmitIProductionJobResponse submitIProductionJobResponse = mpsClient.submitIProductionJob(submitIProductionJobRequest);
// 等待任务完成
String jobId = submitIProductionJobResponse.body.jobId;
String result;
while (true) {
QueryIProductionJobRequest queryIProductionJobRequest = new QueryIProductionJobRequest();
queryIProductionJobRequest.setJobId(jobId);
QueryIProductionJobResponse queryIProductionJobResponse = mpsClient.queryIProductionJob(queryIProductionJobRequest);
System.out.println("IProductionJob Info : " + JSONObject.toJSONString(queryIProductionJobResponse.body));
if ("Success".equals(queryIProductionJobResponse.body.state)) {
result = queryIProductionJobResponse.body.result;
break;
}
Thread.sleep(5000);
}
// 获取节奏检测任务结果
String file = JSONObject.parseObject(JSONObject.parseObject(result).getString("Data")).getJSONArray("result").getJSONObject(0).getString("file");
System.out.println("result file : " + file);
String resultContent = getObjectContent(file);
System.out.println("result content : " + resultContent);
JSONArray beatList = JSONObject.parseObject(resultContent).getJSONArray("BeatList");
// 封装时间线
JSONArray videoTrackClips = new JSONArray();
float transitionDur = 0.3f;
float lastBeat = 0;
for (int i = 0; i < imageUrls.size(); i++) {
float beat = Float.parseFloat(beatList.getString(i));
float beatDuration = beat - lastBeat;
float duration = beatDuration;
String url = imageUrls.get(i);
JSONObject clip = new JSONObject();
clip.put("MediaURL", url);
if (beatDuration > transitionDur) {
clip.put("Effects", JSONArray.parse("[{\"Type\": \"Transition\",\"SubType\": \"linearblur\",\"Duration\": " + transitionDur + "},{\"Type\":\"Background\",\"SubType\":\"Blur\"}]"));
duration += transitionDur;
} else {
clip.put("Effects", JSONArray.parse("[{\"Type\":\"Background\",\"SubType\":\"Blur\"}]"));
}
if (url.endsWith(".jpg")) {
clip.put("Duration", duration);
clip.put("Type", "Image");
} else {
clip.put("Out", duration);
}
videoTrackClips.add(clip);
lastBeat = beat;
}
String timeline = "{\"VideoTracks\":[{\"VideoTrackClips\":" + videoTrackClips.toJSONString() + "}]," +
"\"AudioTracks\":[{\"AudioTrackClips\":[{\"MediaURL\":\"" + musicUrl + "\"}]}]," +
"}";
System.out.println("timeline : " + timeline);
// 提交合成任务
SubmitMediaProducingJobRequest submitMediaProducingJobRequest = new SubmitMediaProducingJobRequest();
submitMediaProducingJobRequest.setTimeline(timeline);
String outputPath = IceUtil.getRandomOutputPath();
String mediaURL = "https://" + bucket + ".oss-" + regionId + ".aliyuncs.com/" + outputPath + ".mp4";
submitMediaProducingJobRequest.setOutputMediaConfig("{\"MediaURL\":\"" + mediaURL + "\",\"Width\":720,\"Height\":1280}");
Client iceClient = TestClientInstance.getInstance().getIceClient();
SubmitMediaProducingJobResponse submitMediaProducingJobResponse = iceClient.submitMediaProducingJob(submitMediaProducingJobRequest);
System.out.println("job created, jobId : " + submitMediaProducingJobResponse.body.jobId + ", requestId : " + submitMediaProducingJobResponse.body.getRequestId() + ", mediaURL : " + mediaURL);
// 等待合成任务完成
while (true) {
GetMediaProducingJobRequest getMediaProducingJobRequest = new GetMediaProducingJobRequest();
getMediaProducingJobRequest.setJobId(submitMediaProducingJobResponse.body.jobId);
GetMediaProducingJobResponse getMediaProducingJobResponse = iceClient.getMediaProducingJob(getMediaProducingJobRequest);
System.out.println("GetMediaProducingJobResponse : " + JSONObject.toJSONString(getMediaProducingJobResponse.body));
String status = getMediaProducingJobResponse.getBody().getMediaProducingJob().getStatus();
if ("Success".equals(status)) {
break;
}
Thread.sleep(5000);
}
System.out.println("Produce succeed : " + mediaURL);
}
public com.aliyun.mts20140618.Client createMpsClient() throws Exception {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret);
config.endpoint = "mts." + regionId + ".aliyuncs.com";
return new com.aliyun.mts20140618.Client(config);
}
public OSS createOssClient() throws Exception {
String endpoint = "http://oss-" + regionId + ".aliyuncs.com";
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
return ossClient;
}
public String getObjectContent(String object) throws Exception {
OSSObject obj = ossClient.getObject(bucket, object);
InputStream stream = obj.getObjectContent();
String result = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
ossClient.shutdown();
return result;
}
public Client createIceClient() throws Exception {
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret);
config.endpoint = "ice." + regionId + ".aliyuncs.com";
return new Client(config);
}
}
欢迎访问官方文档查看更多示例:
欢迎加入智能媒体生产ICE官方答疑群咨询交流。