短视频批量混剪-制作卡点相册

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000次 1年
简介: 随着短视频生态的发展,视频生产的质量和效率已经越来越重要,本篇介绍如何用一段音乐和多张图片,快速自动生成一段卡点相册视频。

随着短视频生态的发展,视频生产的质量和效率已经越来越重要,本篇介绍如何用一段音乐和多张图片,快速自动生成一段卡点相册视频。

概述

背景

卡点视频是短视频生产中常用的效果,将素材的开始结束时间卡在音乐的节奏点上,能够带动观众的感官和气氛。本篇介绍如何用一段音乐和多张图片,快速自动生成一段卡点相册视频。

本文基于智能媒体服务IMS,用户通过组装Timeline,即可对云端的音视频素材进行剪辑,最后附有示例代码,帮助大家更快速上手。

目标读者

有短视频批量制作的商家,或视频批量生产工具的开发者。

方案介绍

先来看成片示例

生产此视频的素材是一段音乐和一批图片。

整体流程:整体流程 1、调用智能生产接口识别音乐的节奏点信息。

2、根据节奏点信息封装Timeline。

3、调用剪辑合成接口,传入Timeline,合成最终成片。

其中,Timeline格式参考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官方答疑群咨询交流。二维码

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
4天前
|
API 数据安全/隐私保护
抖音视频,图集无水印直链解析免费API接口教程
该接口用于解析抖音视频和图集的无水印直链地址。请求地址为 `https://cn.apihz.cn/api/fun/douyin.php`,支持POST或GET请求。请求参数包括用户ID、用户KEY和视频或图集地址。返回参数包括状态码、信息提示、作者昵称、标题、视频地址、封面、图集和类型。示例请求和返回数据详见文档。
|
搜索推荐 算法 数据挖掘
体验个性化相册生成
众所周知,个性化相册生成是一项令人兴奋的技术,它利用先进的图像处理和数据分析算法,能够自动对相册中的照片进行标签、人脸和元数据的提取,实现相同人脸的聚类和时光轴排序。本文将通过对PDS(Personalized Album Generation System)的使用体验,分享其在个性化相册生成方面的使用心得。
101 1
体验个性化相册生成
|
存储 编解码 数据可视化
漏刻有时数据可视化语音留言墙开发日志(微信录音&七牛云amr转换成mp3存储转码)
漏刻有时数据可视化语音留言墙开发日志(微信录音&七牛云amr转换成mp3存储转码)
80 0
|
6月前
|
存储 JSON 5G
短视频批量制作-常用功能
越来越多的产品选择使用短视频作为内容承载,通过对媒体素材进行简单的剪辑,即可进行使用和投放,本文基于智能媒体服务IMS,介绍短视频剪辑中的常用功能,通过对不同功能的组合,方便客户组装自己的剪辑场景,进行短视频批量合成。
215 0
短视频批量制作-常用功能
|
移动开发 前端开发
【H5 音乐播放实例】第三节 音乐详情页制作(3)
【H5 音乐播放实例】第三节 音乐详情页制作(3)
|
移动开发 前端开发 容器
【H5 音乐播放实例】第二节 音乐详情页制作(2)
【H5 音乐播放实例】第二节 音乐详情页制作(2)
|
JavaScript 小程序 前端开发
微信小程序生成海报图片导出相册
小程序内通过静态模板和样式绘制 canvas ,导出图片,可用于生成分享图等场景
601 1
微信小程序生成海报图片导出相册
微博相册图片获取工具
下载微博相册,配合aria2工具,备份至阿里云盘
168 0
微博相册图片获取工具
|
前端开发 Java
在线相册的前后端交互
在线相册的前后端交互
180 0
在线相册的前后端交互
|
存储 编解码 前端开发
【微信开发】上传用户语音 并转码 分享
【微信开发】上传用户语音 并转码 分享
【微信开发】上传用户语音 并转码 分享