編輯視頻
短視頻SDK提供視頻編輯功能,支持視頻圖片素材混合導(dǎo)入、濾鏡、配音、時(shí)間特效、畫中畫等豐富的編輯效果。本文介紹iOS端短視頻SDK視頻編輯的流程及方法。
版本支持
版本 | 是否支持 |
專業(yè)版 | 支持所有功能。 |
標(biāo)準(zhǔn)版 | 部分支持,支持除字幕、動(dòng)態(tài)貼紙、MV外的其他功能。 |
基礎(chǔ)版 | 不支持。 |
相關(guān)類功能
操作 | 類名 | 功能 |
初始化 | 視頻編輯核心類。 | |
開(kāi)始編輯 | 視頻源管理器。 | |
媒體片段。 | ||
工程配置里的主流軌道。 | ||
工程配置里的主流片段。 | ||
預(yù)覽控制 | 播放協(xié)議。 | |
播放狀態(tài)回調(diào)協(xié)議。 | ||
設(shè)置視頻特效 | 濾鏡效果model類。 | |
時(shí)間特效model類。 | ||
轉(zhuǎn)場(chǎng)效果的基類。 | ||
MV效果model類。 | ||
濾鏡管理器,可管理lut濾鏡和靜態(tài)濾鏡。 | ||
lut濾鏡控制器。 | ||
lut濾鏡Model。 | ||
靜態(tài)濾鏡控制器。 | ||
靜態(tài)濾鏡Model。 | ||
設(shè)置音樂(lè)及音效 | 音樂(lè)。 | |
配音。 | ||
工程配置里的音軌。 | ||
工程配置里的音軌片段。 | ||
設(shè)置畫中畫 | 畫中畫管理類。 | |
畫中畫軌道控制器。 | ||
畫中畫片段控制器。 | ||
畫中畫數(shù)據(jù)模型。 | ||
工程配置中的畫中畫片段。 | ||
工程配置中的畫中畫軌道。 | ||
畫面增強(qiáng)信息。 | ||
音頻相關(guān)信息。 | ||
邊框相關(guān)信息。 | ||
設(shè)置字幕及貼紙 | 貼紙及字幕管理器。 | |
字幕控制器。 | ||
動(dòng)圖控制器。 | ||
靜圖控制器。 | ||
字幕數(shù)據(jù)模型。 | ||
動(dòng)圖數(shù)據(jù)模型。 | ||
靜圖數(shù)據(jù)模型。 | ||
工程配置里的動(dòng)圖軌道。 | ||
工程配置里的靜圖軌道。 | ||
工程配置里的字幕軌道。 | ||
草稿箱 | 工程配置。 | |
草稿對(duì)象。 | ||
本地草稿管理器。 | ||
草稿資源加載任務(wù)。 | ||
草稿上傳任務(wù)。 | ||
資源對(duì)象。 | ||
加載任務(wù)中更具體的資源模型。 | ||
其他設(shè)置 | 涂鴉畫布視圖。 | |
畫筆。 |
編輯視頻流程
階段 | 流程 | 說(shuō)明 | 示例代碼 |
基礎(chǔ) | 1 | 創(chuàng)建并初始化編輯器。 | |
2 | 在編輯過(guò)程中動(dòng)態(tài)裁剪視頻、動(dòng)態(tài)更換視頻源、動(dòng)態(tài)調(diào)整視頻轉(zhuǎn)場(chǎng)時(shí)間及轉(zhuǎn)場(chǎng)效果。 | ||
3 | 設(shè)置編輯器的預(yù)覽播放。 | ||
進(jìn)階 | 4 | 設(shè)置濾鏡、轉(zhuǎn)場(chǎng)及MV特效。 | |
5 | 設(shè)置背景音樂(lè)、配音及音效。 | ||
6 | 設(shè)置畫中畫。 | ||
7 | 設(shè)置字幕、花字、文字氣泡及貼紙。 | ||
8 | 支持編輯草稿箱中的視頻、或?qū)⒕庉嬐瓿傻囊曨l保存至草稿箱。 | ||
9 | 設(shè)置涂鴉。 |
初始化
創(chuàng)建并初始化編輯器。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
// 1.任務(wù)路徑:每一個(gè)媒體編輯狀態(tài)會(huì)生成一個(gè)任務(wù),編輯初始化時(shí)需要提供任務(wù)存儲(chǔ)的路徑,任務(wù)必須先初始化
NSString *taskPath = @"xxx"; // taskPath為導(dǎo)入視頻的地址,通常為本地媒體資源導(dǎo)入或草稿箱導(dǎo)入
// 2.預(yù)覽視圖:設(shè)置預(yù)覽視圖后,編輯過(guò)程中,每一個(gè)操作都會(huì)實(shí)時(shí)地展示在一個(gè)預(yù)覽視圖上
UIView *preview = xxx;
// 3.實(shí)例化
AliyunEditor *editor = [[AliyunEditor alloc] initWithPath:taskPath preview:preview];
為了保證預(yù)覽效果與輸出效果保持一致,建議您的預(yù)覽視圖大小與最終輸出視頻的分辨率保持一樣的寬高比。
視頻管理
視頻編輯器里的視頻和圖片最終會(huì)由視頻源管理器AliyunIClipConstructor統(tǒng)一管理,通過(guò)AliyunIClipConstructor修改編輯器里的視頻和圖片后,需要下一次調(diào)用[editor startEdit]
編輯后才能生效。
代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
// 1. 獲取視頻源管理器
id<AliyunIClipConstructor> contructor = [editor getClipConstructor];
// 2. 添加片段
// 2.1 先停止編輯
[editor stopEdit];
// 2.2 創(chuàng)建視頻
AliyunClip *clip = [[AliyunClip alloc] initWithVideoPath:videoPath startTime:2 duration:6 animDuration:0];
// 2.3.1 把視頻追加到最后
[contructor addMediaClip:clip];
// 2.3.2 或者把視頻加到指定位置
[contructor addMediaClip:clip atIndex:1];
// 2.4 開(kāi)始編輯
[editor startEdit];
// 3. 更新片段(替換片段)
[contructor updateMediaClip:clip atIndex:1];
// 4. 刪除片段
// 4.1 刪除最后一個(gè)片段
[contructor deleteLastMediaClip];
// 4.2 刪除指定的一個(gè)片段
[contructor deleteMediaClipAtIndex:1];
// 4.3 刪除所有片段
[contructor deleteAllMediaClips];
// 5. 獲取當(dāng)前所有片段
NSArray<AliyunClip *> *mediaClips = contructor.mediaClips;
預(yù)覽控制
在視頻編輯過(guò)程中,提供一系列對(duì)當(dāng)前視頻的播放控制操作,如播放、暫停、獲取當(dāng)前時(shí)長(zhǎng)等。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
播放控制
// 獲取預(yù)覽播放器
id<AliyunIPlayer> player = [editor getPlayer];
//開(kāi)始播放
[player play];
//繼續(xù)播放
[player resume];
// 暫停播放
[player pause];
// 跳轉(zhuǎn)到指定位置
[player seek:timeInSecond];
// 靜音
[editor setMute:YES];
// 設(shè)置音量
[editor setVolume:50];
//獲取當(dāng)前流的時(shí)間 - 不受時(shí)間特效影響
double currentTime = [player getCurrentStreamTime];
// 獲取當(dāng)前播放的時(shí)間 - 受時(shí)間特效影響
double currentTime = [player getCurrentTime];
// 獲取流時(shí)長(zhǎng) - 不受時(shí)間特效影響
double duration = [player getStreamDuration];
// 獲取播放總時(shí)長(zhǎng) - 受時(shí)間特效影響
double duration = [player getDuration];
播放回調(diào)
// 監(jiān)聽(tīng)播放狀態(tài)
editor.playerCallback = self;
// 協(xié)議
- (void)playerDidEnd {
// 播放結(jié)束回調(diào)
}
- (void)playProgress:(double)playSec streamProgress:(double)streamSec {
// 播放進(jìn)度回調(diào)
}
- (void)playError:(int)errorCode {
// 播放錯(cuò)誤回調(diào)
}
設(shè)置視頻特效
支持設(shè)置的視頻特效包括濾鏡、轉(zhuǎn)場(chǎng)、MV及時(shí)間特效。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
濾鏡
濾鏡分為lut濾鏡、靜態(tài)濾鏡和動(dòng)效濾鏡。
lut濾鏡:使用Lookup Table方式進(jìn)行像素替換。
靜態(tài)濾鏡:通過(guò)編寫著色語(yǔ)言方式進(jìn)行像素計(jì)算,不支持帶動(dòng)畫效果。
動(dòng)效濾鏡:通過(guò)編寫著色語(yǔ)言方式進(jìn)行像素計(jì)算,帶動(dòng)畫效果。
濾鏡支持自定義制作,制作方法請(qǐng)參見(jiàn)濾鏡及轉(zhuǎn)場(chǎng)。
lut濾鏡
// 1 添加lut濾鏡
AliyunLutFilterController *controller = [[editor getFilterManager] applyLutFilterWithPath:path intensity:intensity];
if (!controller) {
// 添加LutFilter失敗
}
// 2 更新intensity
controller.model.intensity = 0.5;
// 3 刪除lut濾鏡
[[editor getFilterManager] removeFilter:controller];
靜態(tài)濾鏡
// 1 添加靜態(tài)濾鏡
AliyunShaderFilterController *controller = [[editor getFilterManager] applyShadeFilterWithPath:path];
if (!controller) {
// 添加LutFilter失敗
}
// 2 刪除靜態(tài)濾鏡
[[editor getFilterManager] removeFilter:controller];
動(dòng)效濾鏡
// 1 添加動(dòng)效濾鏡
AliyunEffectFilter *animationFilter = [[AliyunEffectFilter alloc] initWithFile:filterFolder];
animationFilter.startTime = 2;
animationFilter.endTime = 10;
// ... 其他屬性請(qǐng)參考接口文檔
[editor applyAnimationFilter:animationFilter];
// 2 修改動(dòng)效濾鏡
animationFilter.startTime = 4;
animationFilter.endTime = 12;
// ... 其他屬性請(qǐng)參考接口文檔
[editor updateAnimationFilter:animationFilter];
// 3 刪除動(dòng)效濾鏡
[editor removeAnimationFilter:animationFilter];
轉(zhuǎn)場(chǎng)
轉(zhuǎn)場(chǎng)支持自定義制作,制作方法請(qǐng)參見(jiàn)濾鏡及轉(zhuǎn)場(chǎng)。
目前短視頻SDK提供的轉(zhuǎn)場(chǎng)效果包括:AliyunTransitionEffectTypeCircle(圓形打開(kāi))、AliyunTransitionEffectTypeFade(淡入淡出)、AliyunTransitionEffectTypePolygon(五角星)、AliyunTransitionEffectTypeShuffer(百葉窗)、AliyunTransitionEffectTypeTranslate(平移)。接口參數(shù)請(qǐng)參考AliyunTransitionEffectType。
轉(zhuǎn)場(chǎng)位置
轉(zhuǎn)場(chǎng)需要設(shè)置在兩個(gè)片段中間,所以如果只有一個(gè)視頻片段,不能添加轉(zhuǎn)場(chǎng),轉(zhuǎn)場(chǎng)位置從0開(kāi)始,位置含義如下:
[----A視頻段----] [----B視頻段----] [----C視頻段----]...[----N段視頻----]
^ ^ ^
位置: 0 1 N-1 -->
設(shè)置轉(zhuǎn)場(chǎng)
//添加
//注意:使用此接口前,需要先調(diào)用[editor stopEdit],然后調(diào)用此接口,接著調(diào)用[editor startEdit]才會(huì)生效。
AliyunTransitionEffect *transition = [[AliyunTransitionEffect alloc] initWithPath:transitionFolder];
[editor applyTransition:transition atIndex:0];
//更新
//注意:轉(zhuǎn)場(chǎng)的時(shí)長(zhǎng)不能超過(guò)前后兩段視頻中最短的視頻時(shí)長(zhǎng)
transition.overlapDuration = 1;
// ... 其他更多屬性請(qǐng)參考API文檔
[editor updateTransition:transition atIndex:0];
//刪除
[editor removeTransitionAtIndex:0];
MV
MV支持自定義制作,制作方法請(qǐng)參見(jiàn)MV。
// 添加MV
AliyunEffectMV *mv = [[AliyunEffectMV alloc] initWithFile:mvFolder];
[editor applyMV:mv];
// MV靜音
[editor removeMVMusic];
// 刪除MV
[editor removeMV];
時(shí)間特效
目前短視頻SDK提供的時(shí)間特效包括:TimeFilterTypeSpeed(變速)、TimeFilterTypeRepeat(反復(fù))、TimeFilterTypeInvert(倒放)。接口參數(shù)請(qǐng)參考TimeFilterType。
設(shè)置倒放時(shí),如果視頻的GOP過(guò)大 (35) (可通過(guò)AliyunNativeParser來(lái)獲取GOP),則需要先對(duì)視頻做一次轉(zhuǎn)碼后(可通過(guò)視頻裁剪來(lái)轉(zhuǎn)碼),再使用倒放特效。
// 1. 添加時(shí)間特效
// 1.1 變速
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeSpeed;
timeFilter.param = 0.67;
timeFilter.startTime = 2;
timeFilter.endTime = 10;
// ...其他屬性請(qǐng)參考API文檔
[editor applyTimeFilter:timeFilter];
// 1.2 反復(fù)
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeRepeat;
timeFilter.param = 3; // 例如重復(fù)3次
timeFilter.startTime = 2;
timeFilter.endTime = 10;
// ...其他屬性請(qǐng)參考API文檔
[editor applyTimeFilter:timeFilter];
// 1.3 倒放
AliyunEffectTimeFilter *timeFilter = [[AliyunEffectTimeFilter alloc] init];
timeFilter.type = TimeFilterTypeInvert;
// ...其他屬性請(qǐng)參考API文檔
[editor applyTimeFilter:timeFilter];
// 2. 刪除時(shí)間特效
[editor removeTimeFilter:timeFilter];
設(shè)置音樂(lè)及音效
支持添加音樂(lè)和配音,給音頻添加各種音效(淡入淡出效果、變聲)。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
音樂(lè)
音樂(lè)分為背景音樂(lè)和配音。背景音樂(lè)不受時(shí)間特效影響(變速、重復(fù)、倒放等),而配音會(huì)受到時(shí)間特效的影響。
背景音樂(lè)
// 1. 添加背景音樂(lè)
AliyunEffectMusic *music =[[AliyunEffectMusic alloc] initWithFile:musicFilePath];
music.duration = 3;
music.audioMixWeight = 50; // 音量大小(混音權(quán)重)
// ... 其他更多屬性請(qǐng)參考API文檔
[editor applyMusic:music];
// 2. 刪除背景音樂(lè)
[editor removeMusic:music];
配音
// 1. 添加配音
AliyunEffectDub *dub =[[AliyunEffectDub alloc] initWithFile:dubFilePath];
dub.startTime = 2;
dub.audioMixWeight = 50; // 音量大小(混音權(quán)重)
dub.audioDenoiseWeight = 50; // 降噪程度
// ... 其他更多屬性請(qǐng)參考API文檔
[editor applyDub:dub];
// 2. 刪除配音
[editor removeDub:dub];
音效
淡入淡出:支持AliyunAudioFadeShapeLinear(線性曲線)、AliyunAudioFadeShapeSin(正弦函數(shù)曲線),接口參數(shù)請(qǐng)參考AliyunAudioFadeShape。
變聲效果:接口參數(shù)請(qǐng)參考AliyunAudioEffectType。
AliyunAudioEffectLolita(蘿莉)
AliyunAudioEffectUncle(大叔)
AliyunAudioEffectReverb(混響)
AliyunAudioEffectEcho(回聲)
AliyunAudioEffectRobot(機(jī)器人)
AliyunAudioEffectBigDevil(大魔王)
AliyunAudioEffectMinions(小黃人)
AliyunAudioEffectDialect(方言)
淡入淡出
// 1. 添加音頻前指定淡入效果(淡出類似,屬性為fadeOut)
AliyunAudioFade *fadeIn = [[AliyunAudioFade alloc] init];
fadeIn.shape = AliyunAudioFadeShapeLinear;
fadeIn.duration = 2;
music.fadeIn = fadeIn; // 配音也一樣設(shè)置
// 2. 添加音頻后修改淡入效果(淡出類似,函數(shù)為 setAudioFadeOutShape:duration:streamId:)
[editor setAudioFadeInShape:AliyunAudioFadeShapeLinear duration:2 streamId:music.effectVid];
// 3. 添加音頻后刪除淡入效果(淡出類似,函數(shù)為 removeAudioFadeOutWithStreamId:)
[editor removeAudioFadeInWithStreamId:music.effectVid]; // 配音也一樣調(diào)用
變聲
// 1. 添加音頻前設(shè)置變聲
AliyunAudioEffect *audioEffect = [[AliyunAudioEffect alloc] init];
audioEffect.type = AliyunAudioEffectLolita;
audioEffect.weight = 50;
[dub.audioEffects addObject:audioEffect]; // 注意:暫時(shí)只支持添加一個(gè)
// 2. 添加音頻后設(shè)置變聲
[editor setAudioEffect:AliyunAudioEffectLolita weight:50 streamId:dub.effectVid];
// 3. 添加音頻后刪除變聲
[editor removeAudioEffect:AliyunAudioEffectLolita streamId:dub.effectVid];
設(shè)置畫中畫
畫中畫功能允許在現(xiàn)有主軌道的基礎(chǔ)上,添加一個(gè)或者多個(gè)畫中畫。
主軌道:編輯頁(yè)面默認(rèn)軌道,有且僅有一個(gè)主軌道,一個(gè)主軌道可以有多個(gè)視頻流。
畫中畫:允許添加多個(gè)畫中畫,畫中畫允許設(shè)置位置,縮放,旋轉(zhuǎn)等。創(chuàng)建畫中畫默認(rèn)創(chuàng)建一個(gè)畫中畫軌道。畫中畫可以在不同畫中畫軌道中移動(dòng)。
代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
//獲取畫中畫管理器,畫中畫管理器負(fù)責(zé)畫中畫的增刪改查
AliyunPipManager * pipManager = [editor getPipManager];
//添加畫中畫
// 1. 在最上層添加一個(gè)畫中畫軌道,把畫中畫片段添加進(jìn)該軌道
// 1.1 添加視頻片段
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithType:AliyunPipClipTypeVideo path:xxVideoPath error:&error];
// 1.2 添加圖片片段
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithType:AliyunPipClipTypeImage path:xxImagePath error:&error];
// 2. 把畫中畫插入到指定一個(gè)畫中畫軌道
// 2.1 創(chuàng)建畫中畫片段
AliyunPipClip *pipClip = [[AliyunPipClip alloc] initWithClipType:AliyunPipClipTypeVideo clipPath:xxxVideoPath];
// ... 更多畫中畫片段設(shè)置請(qǐng)參考文檔:https://alivc-demo-cms.alicdn.com/versionProduct/doc/shortVideo/iOS_cn/Classes/AliyunPipClip.html
// 2.2 插入到指定的軌道中
NSError *error = nil;
AliyunPipClipController *pipClipController = [pipManager addClipWithModel:pipClip toTrack:pipManager.trackControllers.firstObject error:&error]; // 例如添加到第一條軌道里
//刪除畫中畫
NSError *error = nil;
[pipManager removePipClipController:pipClipController error:&error];
//切換畫中畫軌道
[pipManager movePipClipController:pipController toTrack:pipManager.trackControllers.firstObject withStartTime:0]; // 例如移動(dòng)到第一條軌道的開(kāi)始位置
//修改畫中畫片段
// 例如直接修改畫中畫位置
pipController.clip.center = CGPointMake(100, 100);
// 例如批量修改位置、大小、旋轉(zhuǎn)等
[pipController beginEdit];
pipController.clip.center = CGPointMake(100, 100);
pipController.clip.scale = 0.7;
pipController.clip.rotation = M_PI_2;
[pipController endEdit];
//點(diǎn)擊測(cè)試
// 例如獲取當(dāng)前時(shí)間的某個(gè)觸摸點(diǎn)最上層的畫中畫片段
double currentTime = [[editor getPlayer] getCurrentTime];
AliyunPipClipController *pipClipController = [pipManager hitTest:touchPoint withTime:currentTime];
設(shè)置字幕及貼紙
字幕及貼紙統(tǒng)一通過(guò)管理器 AliyunStickerManager進(jìn)行管理,字幕及貼紙本身的狀態(tài)分別通過(guò)字幕控制器AliyunCaptionStickerController和貼紙控制器AliyunStickerController進(jìn)行管理。而字幕控制器和渲染控制器都繼承于基礎(chǔ)渲染控制器AliyunRenderBaseController,所以修改字幕或貼紙也采用修改基礎(chǔ)元素屬性同樣的邏輯。
基于字幕,短視頻SDK還提供了花字及氣泡文字的特效。花字和氣泡文字支持自定義制作,制作的規(guī)范及方法請(qǐng)參見(jiàn)花字和動(dòng)圖。
貼紙分為靜態(tài)貼紙和動(dòng)態(tài)貼紙,動(dòng)態(tài)貼紙和氣泡文字類似,只不過(guò)缺少了文字部分。動(dòng)態(tài)貼紙支持自定義制作,制作規(guī)范及方法請(qǐng)參見(jiàn)動(dòng)圖。
代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
管理器
管理器負(fù)責(zé)字幕的及貼紙的增刪改查,接口參數(shù)請(qǐng)參考AliyunStickerManager。
字幕和貼紙都繼承于渲染模型AliyunRenderModel,有基礎(chǔ)渲染元素的屬性,您可以通過(guò)基于預(yù)覽坐標(biāo)系的點(diǎn)擊位置來(lái)查找某一時(shí)刻在最上層的字幕或貼紙的控制器。
//獲取管理器
AliyunStickerManager *stickerManager = [editor getStickerManager];
//添加
// 例如添加字幕
AliyunCaptionStickerController *captionController = [stickerManager addCaptionText:@"Hello" bubblePath:nil startTime:0 duration:5];
//刪除
[stickerManager remove:gifController];
//查找
// 例如獲取當(dāng)前時(shí)間的某個(gè)觸摸點(diǎn)最上層的字幕或貼紙
double currentTime = [[editor getPlayer] getCurrentTime];
AliyunRenderBaseController *controller = [stickerManager findControllerAtPoint:touchPoint atTime:currentTime];
字幕
//添加字幕
AliyunCaptionStickerController *captionController = [stickerManager addCaptionText:@"Hello" bubblePath:nil startTime:0 duration:5];
//修改字幕
[captionController beginEdit];
captionController.model.text = xxx;
captionController.model.outlineWidth = 3;
captionController.model.outlineColor = UIColor.redColor;
// ... 其他屬性修改請(qǐng)參考AliyunCaptionSticker接口文檔
[captionController endEdit];
花字
// 應(yīng)用花字
captionController.model.fontEffectTemplatePath = fontEffectFolder; // 花字特效資源包文件夾目錄
// 取消花字
captionController.model.fontEffectTemplatePath = nil;
文字氣泡
// 應(yīng)用氣泡
captionController.model.resourePath = bubbleEffectFolder; // 文字氣泡特效資源包文件夾目錄
// 取消氣泡
captionController.model.resourePath = nil;
動(dòng)態(tài)貼紙
//添加動(dòng)態(tài)貼紙
AliyunGifStickerController *gifController = [stickerManager addGif:gifFilePath startTime:0 duration:5];
//修改動(dòng)態(tài)貼紙
[gifController beginEdit];
gifController.gif.center = xxx;
// ...其他屬性修改請(qǐng)參考AliyunGifSticker接口文檔
[gifController endEdit];
靜態(tài)貼紙
//添加靜態(tài)貼紙
AliyunImageStickerController *imageController = [stickerManager addImage:imageFilePath startTime:0 duration:5];
//修改靜態(tài)貼紙
[imageController beginEdit];
imageController.image.center = xxx;
// ...其他屬性修改請(qǐng)參考AliyunImageSticker接口文檔
[imageController endEdit];
草稿箱
每次編輯會(huì)生成一個(gè)編輯任務(wù),以工程配置的形式記錄下來(lái)。如果您不想馬上導(dǎo)出當(dāng)前編輯結(jié)果,可以將編輯狀態(tài)保存為草稿,下次通過(guò)草稿加載就能夠恢復(fù)上次的編輯狀態(tài)繼續(xù)編輯。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
工程配置
編輯狀態(tài)是以草稿對(duì)象的形式,以時(shí)間線的結(jié)構(gòu)來(lái)描述的,每一個(gè)編輯動(dòng)作最終會(huì)體現(xiàn)到這個(gè)工程配置上的狀態(tài)改變。建議您將工程配置跟您的編輯界面相關(guān)聯(lián)。草稿恢復(fù)時(shí)使用該工程配置恢復(fù)您的編輯狀態(tài)對(duì)應(yīng)的界面狀態(tài)。
// 編輯中獲取工程配置對(duì)象
AliyunEditorProject *project = [editor getEditorProject];
草稿對(duì)象
編輯狀態(tài)可以保存為一個(gè)草稿對(duì)象AliyunDraft。您可以按以下方式保存當(dāng)前的編輯狀態(tài)。
// 0. 保存為草稿前,您需要定義草稿應(yīng)該保存在哪里,我們提供了一個(gè)草稿管理對(duì)象作為草稿存儲(chǔ)管理的容器
AliyunDraftManager *draftManager; // 草稿管理的介紹請(qǐng)參考下方文檔說(shuō)明
// 1. 指定草稿的標(biāo)題保存,需要指定保存到哪里,返回草稿對(duì)象
AliyunDraft *draft = [editor saveToDraft:draftManager withTitle:@"your draft title"];
// 2.1 不指定草稿標(biāo)題(使用上一次草稿標(biāo)題)
AliyunDraft *draft = [editor saveToDraft:draftManager];
// 2.2 可以在獲得草稿對(duì)象后修改標(biāo)題
[draft renameTitle:@"your draft title"];
為了能更好地標(biāo)識(shí)一個(gè)草稿,助力您開(kāi)展本地或云端草稿業(yè)務(wù),短視頻SDK預(yù)留了一個(gè)ID用于標(biāo)識(shí)一個(gè)草稿,您可以通過(guò)接口設(shè)置為您業(yè)務(wù)標(biāo)識(shí)的ID。
[draft changeProjectId:@"your custom project id"];
草稿封面
編輯過(guò)程中,會(huì)自動(dòng)生成一個(gè)合適的封面圖(通常為視頻的第一幀畫面)。如果您覺(jué)得不合適,也可以通過(guò)接口替換為自定義的封面。
[editor updateCover:yourCoverImage];
// 也可以通過(guò)設(shè)置為空,讓我們?yōu)槟詣?dòng)生成(默認(rèn))
[editor updateCover:nil];
在獲得草稿對(duì)象后也支持更換封面圖。
[draft updateCover:yourCoverImage];
如果需要自定義草稿封面,建議盡早設(shè)置,因?yàn)橹付樽远x草稿封面后將不再自動(dòng)去更新封面內(nèi)容,因此,盡早設(shè)置后會(huì)減少不必要的更新動(dòng)作,從而提高編輯性能。
草稿管理
由于草稿管理在初始化時(shí)會(huì)解析出所管理的所有草稿對(duì)象,所以有一定的性能損耗,建議作為一個(gè)全局的對(duì)象不要頻繁地創(chuàng)建和銷毀。
//初始化
//為更好地實(shí)現(xiàn)用戶隔離,以ID作為草稿管理的標(biāo)識(shí),建議您使用用戶的標(biāo)識(shí)作為草稿管理的標(biāo)識(shí)
NSString *draftManagerId;
//實(shí)例化
AliyunDraftManager *draftManager = [[AliyunDraftManager alloc] initWithId:draftManagerId];
//草稿列表
// 獲取草稿列表
NSArray<AliyunDraft *> *draftList = draftManager.draftList;
// 也可以監(jiān)聽(tīng)草稿列表的變化
draftManager.delegate = yourListener;
// 實(shí)現(xiàn) AliyunDraftManagerDelegate協(xié)議 即可
// - (void) onAliyunDraftManager:(AliyunDraftManager *)mgr listDidChange:(NSArray<AliyunDraft *> *)list;
//刪除草稿
[draftManager deleteDraft:targetDraft];
//復(fù)制草稿
[draftManager copyDraft:fromDraft toPath:newDraftTaskPath withTitle:@"your new draft title"];
草稿加載
由于編輯過(guò)程可能會(huì)用到多種資源,出于性能和存儲(chǔ)空間的考慮,我們一般不會(huì)把用到的資源都拷貝到任務(wù)目錄下,因此在從草稿恢復(fù)編輯狀態(tài)前我們需要確保此次編輯中用到的資源都處于準(zhǔn)備好的狀態(tài)。所以,在草稿恢復(fù)到編輯狀態(tài)前,您必須要處理一些資源的加載任務(wù),以確保編輯用到的資源都已經(jīng)準(zhǔn)備好了。以下兩類資源建議您特別關(guān)注:
相冊(cè)中的媒體資源:需要確保開(kāi)始編輯之前擁有讀取的權(quán)限。
動(dòng)態(tài)自定義的字體資源:需要確保開(kāi)始編輯之前對(duì)應(yīng)的字體已經(jīng)注冊(cè)到系統(tǒng)里,至少注冊(cè)到當(dāng)前app會(huì)話中。
[draft load:^(NSArray<AliyunDraftLoadTask *> *tasks) {
for (AliyunDraftLoadTask *task in tasks) {
// 資源加載任務(wù)處理
// 1. 獲取當(dāng)前任務(wù)的資源模型
AEPResourceModel *resource = task.resource;
// 2. 處理...
// 更多資源類型的處理可以參考官方Demo里的 AliyunDraftLoader.m
// 3. 標(biāo)記處理結(jié)果
// 3.1.1 成功:忽略結(jié)果
[task onIgnore];
// 3.1.2 成功:需要修改資源模型的屬性
AEPSource *resultSoruce = [resource.source createWithPath:@"result path"]; // 例如加載后資源路徑發(fā)生了改變
[task onSuccess:resultSoruce];
// 3.2.1 失敗:把對(duì)應(yīng)的節(jié)點(diǎn)刪除掉繼續(xù)加載
[task onFailToRemove]; // 例如某個(gè)字幕的字體加載不了,選擇把該字幕刪除掉繼續(xù)加載打開(kāi)
// 3.2.2 失敗:標(biāo)記整體加載失敗,把下面的error作為整體的加載失敗原因
NSError *error = xxxx; // 需要您給出具體加載失敗的原因
[task onFailToStopWithError:error]; // 發(fā)生這種失敗時(shí),建議您主動(dòng)停止其他的加載任務(wù)
}
} completion:^(NSString *taskPath, AliyunEditorBaseProject *project, NSError *error) {
if (!taskPath || !project || error) {
// 加載失敗處理...
// 一般情況下,error.localizedDescription中會(huì)有詳細(xì)的失敗原因
return;
}
// 加載成功
// 可以使用 taskPath創(chuàng)建Editor以獲得恢復(fù)后的編輯狀態(tài),請(qǐng)參考編輯視頻的初始化文檔
}];
草稿上傳
編輯狀態(tài)主要由工程配置和資源組成。所以只要把這兩部分同步到云端即可實(shí)現(xiàn)云草稿。接口參數(shù)請(qǐng)參考AliyunDraftProjectUploadTask。
為了性能和存儲(chǔ)空間,默認(rèn)不會(huì)拷貝編輯用到的所有資源,但草稿會(huì)記錄所有用到資源的描述。為了更好地索引資源,提供了AEPSource.path(本地路徑)、AEPSource.sourceId(資源ID)等多種方式來(lái)描述一個(gè)資源,詳細(xì)可參考 AEPSource。在草稿資源的加載、上傳、下載等過(guò)程中,除了會(huì)提供資源的描述,還會(huì)提供當(dāng)前加載的該資源所在的節(jié)點(diǎn)對(duì)象、時(shí)間線所屬模塊等信息,詳細(xì)可參考AEPResourceModel。
草稿上傳分為如下兩個(gè)過(guò)程:
上傳編輯中所有用到的資源,這個(gè)過(guò)程中會(huì)修改工程配置中資源的描述。
上傳資源描述修改后的工程配置文件。
[draft uploadWithResourceUploader:^(NSArray<AliyunDraftLoadTask *> *tasks) {
for (AliyunDraftLoadTask *task in tasks) {
// 上傳任務(wù)處理
// 1. 獲取當(dāng)前任務(wù)的資源模型
AEPResourceModel *resource = task.resource;
// 2. 處理...
// 更多資源類型的處理可以參考官方Demo里的 AliyunDraftLoader.m
// 3. 標(biāo)記處理結(jié)果
// 3.1.1 成功:忽略結(jié)果
[task onIgnore]; // 例如業(yè)務(wù)內(nèi)置資源不需要上傳處理
// 3.1.2 成功:需要修改資源模型的屬性
AEPSource *resultSoruce = [resource.source createWithURL:@"resource URL"]; // 例如上傳資源后獲得網(wǎng)絡(luò)鏈接
[task onSuccess:resultSoruce];
// 3.2.1 失敗:把對(duì)應(yīng)的節(jié)點(diǎn)刪除掉繼續(xù)上傳
[task onFailToRemove]; // 例如某個(gè)字幕的字體上傳失敗了,選擇把該字幕刪除掉繼續(xù)上傳其他的
// 3.2.2 失敗:標(biāo)記整體上傳失敗,把下面的error作為最后的整體上傳失敗原因
NSError *error = xxx; // 需要您給出具體上傳失敗的原因
[task onFailToStopWithError:error]; // 發(fā)生這種失敗時(shí),建議您主動(dòng)停止其他的上傳任務(wù)
}
} projectUploader:^(AliyunDraftProjectUploadTask *projTask) {
// 添加云草稿處理
// 1.1 獲取當(dāng)前草稿的工程配置文件路徑
NSString *projectFilePath = projTask.projectFilePath;
// 1.2 處理工程配置文件
NSString *projectUrl = [yourUploader upload:projectFilePath]; // 例如上傳配置文件,返回網(wǎng)絡(luò)路徑
// 2.1 同步云草稿
NSString *projectId = [yourApi addCloudDraft:projectUrl]; // 例如調(diào)用您的業(yè)務(wù)服務(wù)返回一個(gè)標(biāo)識(shí)ID
// 2.2 更新草稿的工程ID
[projTask.draft changeProjectId:projectId];
// 3. 標(biāo)記處理結(jié)果
// 3.1 成功
[projTask onSuccess];
// 3.2 失敗
NSError *error = xxx; // 需要您給出添加失敗的原因
[projTask onFailWithError:error];
} completion:^(NSError *error) {
if (error) {
// 上傳失敗處理...
return;
}
// 上傳成功處理...
}];
草稿下載
因?yàn)椴莞宓墓こ膛渲糜涗浿杏玫降馁Y源描述,所以只要提供工程配置文件就能枚舉出所有的資源,把資源同步到本地就能完成草稿的下載。
跟保存草稿一樣,需要確定草稿保存到哪里,所以下載接口定義在了本地草稿管理器AliyunDraftManager。
// 0. 通過(guò)您的業(yè)務(wù)服務(wù)獲取云端草稿對(duì)應(yīng)的工程配置文件和工程ID
NSString *projectFilePath = xxx;
NSString *projectId = xxx;
// 1. 下載草稿
[draftManager downloadDraftWithProjectFile:projectFilePath resourceDownloader:^(NSArray<AliyunDraftLoadTask *> *tasks) {
for (AliyunDraftLoadTask *task in tasks) {
// 下載任務(wù)處理
// 1. 獲取資源
AEPResourceModel *resource = task.resource;
// 2. 處理...
NSString *localPath = [yourDownloader download:resource.source.URL]; // 例如網(wǎng)絡(luò)下載
// 3. 標(biāo)記處理結(jié)果
AEPSource *localSource = [resource.source createWithPath:localPath]; // 例如下載到本地了
[task onSuccess:localSource];
// 更多結(jié)果標(biāo)記參考 加載任務(wù)
}
} completion:^(AliyunDraft *draft, NSError *error) {
if (error || !draft) {
// 下載失敗處理...
return;
}
// 下載成功處理...
[draft changeProjectId:projectId]; // 建議同步一下云端的ID
// 其他更多處理...
}];
其他設(shè)置
涂鴉
短視頻SDK封裝了一套涂鴉接口,包含畫板、畫筆等,整個(gè)涂鴉操作由涂鴉畫布視圖(AliyunICanvasView)完成。代碼中需要使用的參數(shù)詳情,請(qǐng)參考接口文檔。接口鏈接請(qǐng)參見(jiàn)相關(guān)類功能。
// 1. 創(chuàng)建畫筆
AliyunIPaint *paint = [[AliyunIPaint alloc]initWithLineWidth:5.0 lineColor:UIColor.whiteColor];
// 2. 添加涂鴉視圖
AliyunICanvasView *paintView = [[AliyunICanvasView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) paint:paint];
[yourView addSubview:paintView];
// 3. 涂鴉
// 觸摸視圖即能進(jìn)行涂鴉
// 4. 其他操作
// 4.1 撤銷一步
[paintView undo];
// 4.2 反撤銷一步
[paintView redo];
// 4.3 撤銷本次涂鴉所有的操作
[paintView undoAllChanges];
// 4.4 清空所有線條(不可恢復(fù))
[paintView remove];
// 5. 完成涂鴉
UIImage *image = [paintView complete];
NSString *paintPath = xxx;
[UIImagePNGRepresentation(image) writeToFile:paintPath atomically:YES];
// 6. 添加到編輯
AliyunEffectImage *paintImage = [[AliyunEffectImage alloc] initWithFile:paintPath];
[editor applyPaint:paintImage linesData:paintView.lines];
// 7. 刪除涂鴉
[editor removePaint:paintImage];