日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

編輯視頻

更新時(shí)間:

短視頻SDK提供視頻編輯功能,支持視頻圖片素材混合導(dǎo)入、濾鏡、配音、時(shí)間特效、畫中畫等豐富的編輯效果。本文介紹iOS端短視頻SDK視頻編輯的流程及方法。

版本支持

版本

是否支持

專業(yè)版

支持所有功能。

標(biāo)準(zhǔn)版

部分支持,支持除字幕、動(dòng)態(tài)貼紙、MV外的其他功能。

基礎(chǔ)版

不支持。

相關(guān)類功能

操作

類名

功能

初始化

AliyunEditor

視頻編輯核心類。

開(kāi)始編輯

AliyunIClipConstructor

視頻源管理器。

AliyunClip

媒體片段。

AEPVideoTrack

工程配置里的主流軌道。

AEPVideoTrackClip

工程配置里的主流片段。

預(yù)覽控制

AliyunIPlayer

播放協(xié)議。

AliyunIPlayerCallback

播放狀態(tài)回調(diào)協(xié)議。

設(shè)置視頻特效

AliyunEffectFilter

濾鏡效果model類。

AliyunEffectTimeFilter

時(shí)間特效model類。

AliyunTransitionEffect

轉(zhuǎn)場(chǎng)效果的基類。

AliyunEffectMV

MV效果model類。

AliyunFilterManager

濾鏡管理器,可管理lut濾鏡和靜態(tài)濾鏡。

AliyunLutFilterController

lut濾鏡控制器。

AliyunLutFilter

lut濾鏡Model。

AliyunShaderFilterController

靜態(tài)濾鏡控制器。

AliyunShaderFilter

靜態(tài)濾鏡Model。

設(shè)置音樂(lè)及音效

AliyunEffectMusic

音樂(lè)。

AliyunEffectDub

配音。

AEPAudioTrack

工程配置里的音軌。

AEPAudioTrackClip

工程配置里的音軌片段。

設(shè)置畫中畫

AliyunPipManager

畫中畫管理類。

AliyunPipTrackController

畫中畫軌道控制器。

AliyunPipClipController

畫中畫片段控制器。

AliyunPipClip

畫中畫數(shù)據(jù)模型。

AEPPipVideoTrackClip

工程配置中的畫中畫片段。

AEPPipVideoTrack

工程配置中的畫中畫軌道。

AliyunClipAugmentationInfo

畫面增強(qiáng)信息。

AliyunClipAudioInfo

音頻相關(guān)信息。

AliyunPureColorBorderInfo

邊框相關(guān)信息。

設(shè)置字幕及貼紙

AliyunStickerManager

貼紙及字幕管理器。

AliyunCaptionStickerController

字幕控制器。

AliyunGifStickerController

動(dòng)圖控制器。

AliyunImageStickerController

靜圖控制器。

AliyunCaptionSticker

字幕數(shù)據(jù)模型。

AliyunGifSticker

動(dòng)圖數(shù)據(jù)模型。

AliyunImageSticker

靜圖數(shù)據(jù)模型。

AEPGifStickerTrack

工程配置里的動(dòng)圖軌道。

AEPImageStickerTrack

工程配置里的靜圖軌道。

AEPCaptionTrack

工程配置里的字幕軌道。

草稿箱

AliyunEditorProject

工程配置。

AliyunDraft

草稿對(duì)象。

AliyunDraftManager

本地草稿管理器。

AliyunDraftLoadTask

草稿資源加載任務(wù)。

AliyunDraftProjectUploadTask

草稿上傳任務(wù)。

AEPSource

資源對(duì)象。

AEPResourceModel

加載任務(wù)中更具體的資源模型。

其他設(shè)置

AliyunICanvasView

涂鴉畫布視圖。

AliyunIPaint

畫筆。

編輯視頻流程

階段

流程

說(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ù)覽播放。

預(yù)覽控制

進(jìn)階

4

設(shè)置濾鏡、轉(zhuǎn)場(chǎng)及MV特效。

設(shè)置視頻特效

5

設(shè)置背景音樂(lè)、配音及音效。

設(shè)置音樂(lè)及音效

6

設(shè)置畫中畫。

設(shè)置畫中畫

7

設(shè)置字幕、花字、文字氣泡及貼紙。

設(shè)置字幕及貼紙

8

支持編輯草稿箱中的視頻、或?qū)⒕庉嬐瓿傻囊曨l保存至草稿箱。

草稿箱

9

設(shè)置涂鴉。

其他設(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];
            
說(shuō)明

為了保證預(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

說(shuō)明

設(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];
說(shuō)明

如果需要自定義草稿封面,建議盡早設(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ò)程:

  1. 上傳編輯中所有用到的資源,這個(gè)過(guò)程中會(huì)修改工程配置中資源的描述。

  2. 上傳資源描述修改后的工程配置文件。

[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];