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

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

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

概述

背景

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

本文基于智能媒体服务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快速上手——如何使用ossbrowser
本实验是对象存储OSS入门级实验。通过本实验,用户可学会如何用对象OSS的插件,进行简单的数据存、查、删等操作。
目录
相关文章
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
1717 14
|
8月前
|
数据采集 机器学习/深度学习 人工智能
大模型“驯化”指南:从人类偏好到专属AI,PPO与DPO谁是你的菜?
本文深入解析让AI“懂你”的关键技术——偏好对齐,对比PPO与DPO两种核心方法。PPO通过奖励模型间接优化,适合复杂场景;DPO则以对比学习直接训练,高效稳定,更适合大多数NLP任务。文章涵盖原理、实战步骤、评估方法及选型建议,并推荐从DPO入手、结合低代码平台快速验证。强调数据质量与迭代实践,助力开发者高效驯化大模型,实现个性化输出。
1084 8
|
6月前
|
安全 Go Windows
Goland 解决在windows上 Cannot run program “D:\atool\goexe\myApp.exe 无法进行正常调试问题
GoLand运行Go程序时遇“应用程序控制策略已阻止此文件”错误,主因是Windows安全机制拦截未签名的.exe。推荐两法:①右键属性→勾选“解除锁定”;②用gops关联已启动进程调试,彻底绕过拦截。(239字)
1083 4
Goland 解决在windows上 Cannot run program “D:\atool\goexe\myApp.exe 无法进行正常调试问题
|
9月前
|
JSON 搜索推荐 API
淘宝商品评论 API 返回数据参考(附解析与实战示例)
淘宝商品评论 API(核心接口如 taobao.item.review.get)是获取商品用户评价数据的官方通道,返回数据以 JSON 格式为主,结构规范且字段丰富,涵盖评论基础信息、用户画像、内容详情、多媒体信息等维度。本文将拆解通用返回结构、核心字段含义、多场景示例及解析注意事项,为开发者提供完整的数据参考指南。
|
XML 移动开发 前端开发
HTML跨年烟花
HTML跨年烟花
418 1
stm32f407探索者开发板(十九)——外部中断实验-EXIT
stm32f407探索者开发板(十九)——外部中断实验-EXIT
1801 0
|
人工智能 数据库管理 OLAP
Qwen3 + AnalyticDB+Dify on DMS 私有部署指导⽂档
Qwen3 + AnalyticDB+Dify on DMS 私有部署指导⽂档
3672 2
|
存储 负载均衡 关系型数据库
|
人工智能 安全 前端开发
千星计划如何解决平台出入金难题?3大杠杆+1套风控闭环
千星计划通过三大杠杆和一套风控闭环解决平台出入金难题。核心是平衡用户提现需求与企业现金流压力,规避资金池风险。收益链设计采用动态分账,实时结算订单利润,避免资金池压力;支付闭环绑定真实交易,确保资金自循环;提现规则绑定任务,阶梯解锁,确保现金流用于增长;AI风控拦截异常订单,复活机制过滤无效用户。该模式轻资产运作,将出入金压力转化为增长动力。