本文為您介紹如何通過智能媒體服務SDK,調用SubmitMediaProducingJob接口來完成音視頻批量混剪。
前置概念
批量混剪
通過少量的音視頻素材和一段口播的不同組合,合成出多段不一樣的視頻成片。
場景介紹
進入5G時代,越來越多的商家選擇短視頻平臺做營銷推廣,將廣告制作成短視頻投放在多個KOL或營銷號。但由于各大短視頻平臺有去重機制(防止視頻盜用),內容相同的營銷視頻會被做屏蔽處理,這就要求投放在不同賬號的視頻,在內容、結構上要有差別,即需要完成音視頻的批量混剪。您可以通過定制SubmitMediaProducingJob中的Timeline參數,來完成不同的混剪需求。
費用說明
在提交剪輯合成作業任務時,可能會產生多個計費項計費的情況。詳情請參見智能生產制作。
使用說明
將一個或多個視頻、音頻、圖片、字幕素材合成為成品,可以通過不同的Timeline參數配置提交剪輯,然后調用SubmitMediaProducingJob - 提交剪輯合成作業來完成。
時間線是將素材按照視頻創意進行編排和特效設計的產物,時間線主要包含軌道、素材、效果3種對象,詳情請參見Timeline配置說明。
智能生產制作支持直播流、點播和OSS素材文件的剪輯合成、特效渲染、模板生產等功能,更多詳情請參見智能生產制作概述。
前提條件
已開通智能媒體服務。
已創建RAM用戶并獲取對應的AccessKey信息,具體操作請參見創建RAM用戶并授權。
步驟一 創建OSS bucket
登錄OSS控制臺,單擊Bucket 列表 > 創建 bucket,地域選擇華東2(上海),請記錄Bucket名稱,此處創建的Bucket會作為剪輯完成后的視頻輸出地址,后續代碼中將會使用。
步驟二 運行批量混剪代碼
環境要求:
操作系統:Windows 8.1 及以上、macOS、Linux。
下載并安裝 IntelliJ IDEA 2020.1 及以上版本。
JDK 1.8及以上。
新建Maven工程以運行代碼。
導入依賴的第三方包。
說明以下示例代碼中引入的服務端SDK版本號僅供參考,獲取最新的版本請參見服務端SDK。
<dependencies> <dependency> <groupId>com.aliyun</groupId> <artifactId>ice20201109</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.9</version> </dependency> </dependencies>
將AccessKey ID和AccessKey Secret的值配置到配置文件,具體操作請參見管理訪問憑據,更多鑒權方式請參見管理訪問憑據。
說明訪問憑據的更多管理方式請參見管理訪問憑據。
新建Java Class,貼入以下代碼示例。
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.aliyun.ice20201109.Client; import com.aliyun.ice20201109.models.*; import com.aliyun.teaopenapi.models.Config; import java.util.*; // 成功視頻 // http://oushu-test-shanghai.oss-cn-shanghai.aliyuncs.com/ice_output/46b29eb5775f4f758846171ab79bfca7.mp4 /** * 需要maven引入二方包依賴: * <dependency> * <groupId>com.aliyun</groupId> * <artifactId>ice20201109</artifactId> * <version>2.1.0</version> * </dependency> * <dependency> * <groupId>com.alibaba</groupId> * <artifactId>fastjson</artifactId> * <version>1.2.9</version> * </dependency> */ public class BatchProduceAlignment { static final String regionId = "cn-shanghai"; static final String bucket = "第三步創建的bucket"; private Client iceClient; public static void main(String[] args) { try { BatchProduceAlignment batchProduce = new BatchProduceAlignment(); batchProduce.initClient(); batchProduce.run(); } catch (Exception e) { System.out.println("Produce failed. Exception: " + e.toString()); } } public void initClient() throws Exception { // 阿里云賬號AccessKey擁有所有API的訪問權限,建議您使用RAM用戶進行API訪問或日常運維。 // 本示例以將AccessKey ID和 AccessKey Secret保存在環境變量為例說明。配置方法請參見:http://bestwisewords.com/zh/sdk/developer-reference/v2-manage-access-credentials?spm=a2c4g.11186623.0.0.423350fbOTFdOB#2a38e5c14b4em com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client(); Config config = new Config(); config.setCredential(credentialClient); // 如需硬編碼AccessKey ID和AccessKey Secret,代碼如下,但強烈建議不要把AccessKey ID和AccessKey Secret保存到工程代碼里,否則可能導致AccessKey泄露,威脅您賬號下所有資源的安全。 // config.accessKeyId = <第二步創建的AccessKey ID>; // config.accessKeySecret = <第二步創建的AccessKey Secret>; config.endpoint = "ice." + regionId + ".aliyuncs.com"; config.regionId = regionId; iceClient = new Client(config); } public void run() throws Exception { // 文字素材 String text = "人們懂得用五味雜陳形容人生,因為懂得味道是每個人心中固守的情懷。在這個時代,每一個人都經歷了太多的苦痛和喜悅,人們總會將苦澀藏在心里,而把幸福變成食物,呈現在四季的餐桌之上"; // 視頻素材 String[] videoArray = new String[]{ "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f1.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f2.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f3.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f4.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f5.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f6.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f7.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f8.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f9.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f10.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f11.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f12.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f13.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f14.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f15.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f16.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f17.mp4", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/food/f18.mp4" }; // 背景音樂素材 String[] bgMusicArray = new String[]{ "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/music/m1.wav", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/music/m2.wav", "https://ice-document-materials.oss-cn-shanghai.aliyuncs.com/test_media/music/m3.wav" }; // 視頻標題 String title = "舌尖上的美食"; // 生成的成片數 int produceCount = 3; // 批量提交任務 List<String> jobIds = new ArrayList<String>(); for (int i = 0; i < produceCount; i++) { String jobId = produceSingleVideo(title, text, videoArray, bgMusicArray); jobIds.add(jobId); } // 輪詢任務狀態直到全部結束 System.out.println("waiting job finished..."); while (true) { Thread.sleep(3000); boolean allFinished = true; for (int i = 0; i < jobIds.size(); i++) { String jobId = jobIds.get(i); GetMediaProducingJobRequest req = new GetMediaProducingJobRequest(); req.setJobId(jobId); GetMediaProducingJobResponse response = iceClient.getMediaProducingJob(req); GetMediaProducingJobResponseBody.GetMediaProducingJobResponseBodyMediaProducingJob mediaProducingJob = response.getBody().getMediaProducingJob(); String status = mediaProducingJob.getStatus(); System.out.println("jobId: " + mediaProducingJob.getJobId() + ", status:" + status); if ("Failed".equalsIgnoreCase(status)) { System.out.println("jobfailed. jobInfo: " + JSONObject.toJSONString(mediaProducingJob)); throw new Exception("Produce failed. jobid: " + mediaProducingJob.getJobId()); } if (!"Success".equalsIgnoreCase(status)) { allFinished = false; break; } } if (allFinished) { System.out.println("all job finished."); break; } } } public String produceSingleVideo(String title, String text, String[] videoArray, String[] bgMusicArray) throws Exception { text = text.replace(",", "。"); text = text.replace("\n", "。"); String[] sentenceArray = text.split("。"); JSONArray videoClipArray = new JSONArray(); JSONArray audioClipArray = new JSONArray(); List<String> videoList = Arrays.asList(videoArray); Collections.shuffle(videoList); for (int i = 0; i < sentenceArray.length; i++) { String sentence = sentenceArray[i]; String clipId = "clip" + i; String videoUrl = videoList.get(i); String videoClip = "{\"MediaURL\":\""+videoUrl+"\",\"ReferenceClipId\":\""+clipId+"\",\"Effects\":[{\"Type\":\"Background\",\"SubType\":\"Blur\",\"Radius\":0.1}]}"; videoClipArray.add(JSONObject.parseObject(videoClip)); String audioClip = "{\"Type\":\"AI_TTS\",\"Content\":\"" + sentence + "\",\"Voice\":\"zhichu\",\"ClipId\":\""+clipId+"\",\"Effects\":[{\"Type\":\"AI_ASR\",\"Font\":\"Alibaba PuHuiTi\",\"Alignment\":\"TopCenter\",\"Y\":0.75,\"FontSize\":55,\"FontColor\":\"#ffffff\",\"AdaptMode\":\"AutoWrap\",\"TextWidth\":0.8,\"Outline\":2,\"OutlineColour\":\"#000000\"}]}"; audioClipArray.add(JSONObject.parseObject(audioClip)); } String subtitleTrack = "{\"SubtitleTrackClips\":[{\"Type\":\"Text\",\"Font\":\"HappyZcool-2016\",\"Content\":\""+title+"\",\"FontSize\":80,\"FontColor\":\"#ffffff\",\"Y\":0.15,\"Alignment\":\"TopCenter\",\"EffectColorStyle\":\"CS0004-000005\",\"FontFace\":{\"Bold\":true,\"Italic\":false,\"Underline\":false}}]}"; int bgMusicIndex = (int)(Math.random() * bgMusicArray.length); String bgMusicUrl = bgMusicArray[bgMusicIndex]; String timeline = "{\"VideoTracks\":[{\"VideoTrackClips\":"+videoClipArray.toJSONString()+"}],\"AudioTracks\":[{\"AudioTrackClips\":"+audioClipArray.toJSONString()+"},{\"AudioTrackClips\":[{\"MediaURL\":\""+bgMusicUrl+"\"}]}],\"SubtitleTracks\":[" + subtitleTrack + "]}"; // String targetFileName = UUID.randomUUID().toString().replace("-", ""); String outputMediaUrl = "http://" + bucket + ".oss-" + regionId + ".aliyuncs.com/ice_output/" + targetFileName + ".mp4"; int width = 720; int height = 1280; String outputMediaConfig = "{\"MediaURL\":\"" + outputMediaUrl + "\",\"Width\":"+width+",\"Height\":"+height+"}"; SubmitMediaProducingJobRequest request = new SubmitMediaProducingJobRequest(); request.setTimeline(timeline); request.setOutputMediaConfig(outputMediaConfig); SubmitMediaProducingJobResponse response = iceClient.submitMediaProducingJob(request); System.out.println("start job. jobid: " + response.getBody().getJobId() + ", outputMediaUrl: " + outputMediaUrl); return response.getBody().getJobId(); } }
請將代碼中的bucket替換成第二步所創建的值。
單擊運行,即可生成視頻。代碼的整體流程是:
生成好時間線 -》提交視頻合成任務 -》輪詢任務狀態 -》控制臺打印相應日志。
outputMediaUrl為輸出地址,當任務狀態為Success時,打開outputMediaUrl對應的文件,即可預覽成片視頻。因為新建的bucket是私有的,鏈接無法直接打開,可以到OSS控制臺復制文件url,得到鑒權后的鏈接進行訪問。
代碼中的produceCount為輸出成片數,修改produceCount即可輸出不同數量的視頻。代碼中的文案和素材視頻,開發者可以修改成自己想要的內容進行合成,更多內容請查看Timeline配置說明文檔。