閑話不多講,先上項目Github傳送門。
SDVideoCamera傳送門
前言
又是好久沒有更新博客了,哈哈哈,由于近來從公司離職,再加上近來要結婚的緣故,所以有大量充足的時間來整理以前寫的一個仿寫抖音錄制的三方庫。但是講真的,寫了將近一個月的時間,還有有很多細節沒有實現,只是實現其核心功能,還是有很多地方可以優化的。另外抖音是使用FFmpeg來實現音視頻處理的業務邏輯的,相比于AVFoundation,具有跨平臺、執行效率高的特點。所以作為一個菜雞,只能使用AVFoundation來實現視頻處理了。。。
下面我就SDVideoCamera分部分介紹使用方法、功能分解等部分。
使用方法
由于SDVideoCamera使用了原生的系統相機,麥克風以及相冊。所以我們需要在Info.plist配置如下信息。
<key>NSCameraUsageDescription</key>
<string>該項目想訪問你的攝像頭</string>
<key>NSMicrophoneUsageDescription</key>
<string>該項目想訪問你的麥克風</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>該項目想訪問你的相冊</string>
在 SDVideoCamera 中 所有配置項都是由SDVideoConfig這個類來完成的,在這個類中,你可以根據自己項目來配置一些適合場景的參數信息。如果你想修改SDVideoCamera的源碼來適應你的項目,你也可以通過這個類來快速找到修改入口。
這里我猜想有幾個屬性是你絕對想要設置的。
合成視頻返回時會調用的Block,在這個Block中,會返回合成視頻的路徑。
@property(nonatomic,copy)CameraReturnBlock returnBlock;
合成視頻之后需要返回到那個控制器,這個屬性是必須要設置的。
@property(nonatomic,weak)UIViewController *returnViewController;
該三方的主色,很多按鈕或者控件都會受到該屬性的影響。
@property(nonatomic,copy)NSString *tintColor;
配樂的數組,如果想要配樂,可以設置這個,如果感覺UI可能不符合你的項目需要,你可以自己通過這個屬性來找到修改的位置入口(SDVideoPreviewMusicView),進行修改。
@property(nonatomic,copy)NSArray <SDVideoMusicModel *>*recommendMusicList;
想要給視頻合成貼紙?需要設置這個貼紙圖片名稱數組。
@property(nonatomic,copy)NSArray <NSString *>*previewTagImageNameArray;
上面介紹了 SDVideoConfig 這個配置類,接下來我們就看一下我們該如何啟動SDVideoCamera,以SDVideoCameraDemo為例,我們只需要配置好 SDVideoConfig 這個類,然后presentViewController即可,示例代碼如下所示。
SDVideoConfig *config = [[SDVideoConfig alloc] init];
config.returnViewController = self;
config.returnBlock = ^(NSString *mergeVideoPathString) {
NSLog(@"合成路徑 %@",mergeVideoPathString);
};
SDVideoMusicModel *musicModel = [[SDVideoMusicModel alloc] init];
musicModel.musicTitle = @"你是人間四月天";
musicModel.musicImageName = @"music_image.jpg";
musicModel.musicWebURL = @"https://zhike-oss.oss-cn-beijing.aliyuncs.com/tmp/test_music.mp3";
SDVideoMusicModel *otherMusicModel = [[SDVideoMusicModel alloc] init];
otherMusicModel.musicTitle = @"郊外靜音";
otherMusicModel.musicImageName = @"music_image.jpg";
otherMusicModel.musicWebURL = @"https://zhike-oss.oss-cn-beijing.aliyuncs.com/tmp/test_music2.mp3";
config.recommendMusicList = @[musicModel,otherMusicModel];
config.previewTagImageNameArray = @[@"circle_add_friend_icon",@"circle_apply_friend_icon"];
SDVideoController *videoController = [[SDVideoController alloc] initWithCameraConfig:config];
[self presentViewController:videoController animated:YES completion:nil];
上面我們對 SDVideoCamera 的接入有了一定的了解,接下來我就來分部分來說明不同功能模塊。
錄制功能
不叨叨別的。直接上效果圖,我們來看一下實現的效果,主要是就分段錄制,分段刪除,長按錄制,短按錄制等。
視頻的錄制功能沒有采用GPUImage 這個庫,本來一開始想使用的,但是一想打包起來將近10M的靜態庫,所以就暫時放棄了,至于想實現美顏功能的童鞋,請根據下面的說明自行進行添加修改。
我們看一下該模塊下所有相關的類。該功能主要存在于 Camera 和 Helpers 目錄下,所有相關類如下圖所示。
接下來,我們看一下每一個類的說明介紹,如下列表所示。
類名 | 功能說明 |
---|---|
SDVideoCameraController | 該類是錄制的主控制器,也是SDVideoController的下屬控制器 |
SDVideoCameraHeader | 定義了一下常用的宏設定和枚舉,例如寬高信息 |
SDVideoCameraMenuView | 錄制界面的菜單視圖,包括左邊菜單以及下部按鈕的所有控件。 |
SDVideoCameraPlayButton | 錄制按鈕的控件,由于錄制按鈕功能較多,對應的就是需要添加的手勢較多,所以這里需自定義控件來實現功能。 |
SDVideoMenuDataModel | 側邊按鈕的數據模型,用戶可以在SDVideoConfig中對menuDataArray,進行自定義即可,可以修改菜單按鈕的順序,顯示等。 |
SDVideoCameraAuthoView | 相機和麥克風權限的請求視圖 |
SDVideoCaptureManager | 是攝像頭硬件管理類,包括相機、麥克風權限類型,錄制管理等。 |
SDVideoDataManager | 錄制的數據管理類 |
SDVideoDataModel | 該類是分段視頻的數據模型類 |
視頻錄制功能的核心是分段錄制,視頻錄制是沒有使用到視頻合成的,首先我們要了解合成一個視頻其實是一個比較耗時的操作,所以我們能不進行合成操作盡量不要合成視頻。在分段錄制過程中的所有視頻數據是以 SDVideoDataModel 模型存儲的。這個模型中存儲了分段錄制完成的視頻路徑,時長進度,時長,時長權重,同時還有一個刪除視頻文件的方法。這里需要對時長權重進行說明,時長權重就是該視頻占有總視頻時長的百分比。因為我們需要對進度條進行刪除動畫處理,所以我們需要這個值來判斷我們需要刪除的長度。該類如下所示。
@interface SDVideoDataModel : NSObject
// SDVideoDataModel 是分段視頻的數據模型
@property(nonatomic,strong)NSURL *pathURL;
@property(nonatomic,assign)float duration;//時長進度
@property(nonatomic,assign)float progress;//進度
@property(nonatomic,assign)float durationWeight;//時長權重
- (void)deleteLocalVideoFileAction;
@end
在錄制過程中所有的數據管理都是在 SDVideoDataManager的單例類中完成中,為什么使用單例類,這里做一下說明。在錄制過程中存在各種狀態的改變,例如當前錄制時長,錄制視頻個數等等。而其他視圖可以通過KVO觀察者模式來進行其屬性進行觀察,通過新值的變化而改變對應的UI。其實普通類也是滿足該需要,但是需要進行傳遞操作,為了避免傳值操作,所以這里使用了單例類,其實沒有性能上的優化,單純就是習慣而已。大家在修改源碼的時候可以自行根據習慣來修改。該類的屬性如下所示。
@interface SDVideoDataManager : NSObject
// SDVideoDataManager 是視頻數據管理單例類
+ (SDVideoDataManager *)defaultManager;
@property(nonatomic,strong)SDVideoConfig *config; // 用戶的配置項
@property(nonatomic,assign)VideoCameraState cameraState;//相機的狀態
@property(nonatomic,assign,readonly)float videoSecondTime;//最大錄制時長,根據用戶配置項獲得
@property(nonatomic,assign)float totalVideoTime; //錄制的總計時長
@property(nonatomic,assign)float progress; //進度
@property(nonatomic,assign)NSInteger videoNumber; //視頻的個數,沒法直接監聽數組元素的變化
@property(nonatomic,strong,readonly)NSMutableArray <SDVideoDataModel *>*videoDataArray;
/// 添加一條新的分段視頻
- (void)addVideoModel:(SDVideoDataModel *)videoModel;
/// 刪除最后的分段視頻
- (void)deleteLastVideoModel;
/// 刪除所有的分段視頻
- (void)deleteAllVideoModel;
@end
在上面說到我沒有使用GPUImage三方庫來實現美顏濾鏡效果,如果某位童鞋想在SDVideoCamera基礎上添加美顏路徑該怎么實現呢?這就需要對 SDVideoCaptureManager 這個設備管理類進行修改了。 SDVideoCaptureManager是騷棟基于系統相機寫的視頻錄制管理類,如果需要添加美顏濾鏡功能,需要修改這里面的代碼。
至于該模塊的UI部分,我就不過多進行訴述了。
視頻剪輯
上一個模塊我們簡單的聊了一下視頻錄制,這個模塊的入口是通過上傳視頻得到的,效果如下所示。
該模塊涉及到的類主要存放于 Album 和 Crop 以及 Helpers 中。
主要功能類功能簡介表格如下所示。其他類似于Cell的類這里就不過多的敘述了。
類名 | 功能說明 |
---|---|
SDVideoAlbumViewController | 選取視頻的主體控制器,選取模式有兩種,一種是選取完成Push出裁剪控制器,另外一種是選取完成返回到上一個界面,并且回調協議方法。這兩模式通過 isFinishReturn 屬性來控制。 |
SDVideoCropViewController | 視頻選取、裁剪的主體控制器,相冊的視頻合成功能這個是個主入口。 |
SDVideoCropBottomView | 多視頻合成、裁剪的操作視圖。包含 SDVideoCropFrameView 和 SDVideoCropItemListView 兩部分 |
SDVideoCropFrameView | 視頻幀預覽功能的主體視圖,也是視頻裁剪功能的主要操作區。 |
SDVideoCropItemListView | 視頻媒體列表展示的主體視圖,通過這個我們對合成視頻列表進行新增、刪除,變換位置等操作。 |
?SDVideoUtils? | 視頻合成操作工具類,這個類中有我們需要的各種視頻處理方法,也算是整個三方的核心了。 |
OK,上面的基本情況我們已經介紹完成了,接下來我們就一起來看看如何實現多視頻合成以及裁剪的。講真的,這個模塊本來是不想做的,因為相對于視頻錄制來說,功能比較多,而且抖音爸爸做的細節也比較多,每一個小細節都可能需要一兩天甚至更多的時間,但是一想反正有時間,不如就做吧,所以就把這塊給做了。
但是做的過程中遇到了不少的坑,例如合成的視頻大小不一致、視頻幀圖獲取需要時間等等問題。這里我就這些問題來說說視頻剪輯。讓想做這塊的童鞋降低踩坑的風險。該類童鞋可以著重的看一下視頻處理類 SDVideoUtils 中的實現。
首先,該模塊和視頻錄制模塊不同,雖然都是多視頻合成,但是其實有不同的,因為在這個模式下,相冊中的視頻并不一定都是錄制得來的,這就會造成一個問題,那就是合成錄制視頻可能不同,如果我們還是按照網上的那種粗糙的方式進行合成系統就會以一個視頻的尺寸為基準進行合成,從而造成合成視頻尺寸不正常。網上的資料也不是很多,這里參考了iOS 不同尺寸、比例、方向的視頻拼接播放來實現的該功能。
這里對其進行說明,首先我們要是到不管是視頻還是音頻都是由多條軌信息組成,一般的情況,視頻會有一條視頻軌和一條音頻軌,但是我們如果想解決視頻尺寸大小不一致的問題,我們需要指定renderSize,同時根據Apple官方提示,我們需要添加兩條視頻軌,然后 用 交替 添加的形式來進行添加分段視頻的視頻軌,這樣就能徹底的解決視頻大小不一致造成的視頻拉伸問題,然后問題又來了,那就是兩條視頻軌,我們如何讓播放器知道什么時刻播放哪條軌,什么時刻進行視頻軌的交替播放呢?這個就需要定義了 AVMutableVideoComposition 類來實現了。而且在播放的時候,我們只需要添加提供該信息即可。
結合上面的問題,我來對視頻合成操作方法進行說明解釋。該方法如下所示,是SDVideoUtils私有靜態方法。
+ (void)loadMeidaCompostion:(AVMutableComposition *)composition
videoComposition:(AVMutableVideoComposition *)videoComposition
audioMix:(AVMutableAudioMix *)audioMix
assetArray:(NSArray <AVAsset *>*)assetArray
selectTimeRange:(CMTimeRange)selectTimeRange
bgAudioAsset:(AVAsset *)bgAudioAsset
originalVolume:(float)originalVolume
bgAudioVolume:(float)bgAudioVolume;
先對參數進行說明一下 AVMutableComposition 是視頻軌和音頻軌管理類。videoComposition 是可以讓視頻源也就是視頻軌進行切換的信息類。AVMutableAudioMix 是混音管理類,用來管理合成音頻以及伴奏的聲音大小。assetArray是需要合成的視頻數據類。selectTimeRange是時間截取結構體。
首先我們通過外部傳入的方式,把對應的參數傳入到該方法中,然后我們需要情況來創建不同的音頻軌和視頻軌。同時指定 renderSize 的大小,如下所示。
// 初始化視頻軌和音頻軌,伴奏音頻軌
videoComposition.frameDuration = CMTimeMake(1, 30);
videoComposition.renderSize = SDVideoSize;
// 創建兩條視頻軌,處理不同尺寸視頻合成問題
AVMutableCompositionTrack *firstVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *secondVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
NSArray *videoTrackArray = @[firstVideoTrack,secondVideoTrack];
AVMutableCompositionTrack *audioTrack = nil;
if (originalVolume > 0) {
audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
}
AVMutableCompositionTrack *bgAudioTrack = nil;
if (bgAudioVolume > 0) {
bgAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
}
對于 selectTimeRange 這個參數主要是用來截取視頻用的,有很多童鞋可能剛剛了解這塊,我建議看一下CMTime 和 CMTimeRange 兩個結構體的使用與操作方法,這里就不過多敘述了。所以我們在截取視頻的時候需要知道從哪個視頻數據的那個時間點開始,那個視頻的那個時間點結束。這樣我們合成的時候只需要添加對應的時間位置和時間長度的視頻源即可。代碼如下。
CMTime firstAssetStartTime = kCMTimeZero;
CMTime endAssetDuration = assetArray.lastObject.duration;
NSInteger startIndex = 0;
NSInteger endIndex = assetArray.count - 1;
// 如果是沒有設置時間區間,那么直接認定全部選中
if (!CMTimeRangeEqual(selectTimeRange, kCMTimeRangeZero)) {
startIndex = -1;
endIndex = -1;
CMTime assetTotalTime = kCMTimeZero;
CMTime videoTotalTime = CMTimeAdd(selectTimeRange.start, selectTimeRange.duration);
for (int i = 0; i < assetArray.count; i++) {
AVAsset *asset = assetArray[i];
assetTotalTime = CMTimeAdd(assetTotalTime, asset.duration);
if (CMTIME_COMPARE_INLINE(CMTimeSubtract(assetTotalTime,selectTimeRange.start), >, selectTimeRange.start) && startIndex == -1) {
startIndex = i;
firstAssetStartTime = CMTimeSubtract(asset.duration, CMTimeSubtract(assetTotalTime,selectTimeRange.start));
}
if (CMTIME_COMPARE_INLINE(assetTotalTime, >=, videoTotalTime) && startIndex != -1 && endIndex == -1) {
endIndex = i;
endAssetDuration = CMTimeSubtract(asset.duration, CMTimeSubtract(assetTotalTime,videoTotalTime));
}
}
}
獲取到時間截取信息之后,我們就從 startIndex 開始遍歷,到 endIndex 結束 交替的往兩條視頻軌添加視頻軌,同時需要判斷是否需要添加視頻原生音頻軌信息。如果有原聲音頻軌信息,還需要添加混音信息。 同時需要存儲每個分段視頻在整個視頻中的timeRange信息。 整體代碼如下所示。
NSMutableArray *audioMixArray = [NSMutableArray arrayWithCapacity:16];
for (NSInteger i = startIndex; i <= endIndex; i++) {
AVAsset *asset = assetArray[i];
AVMutableCompositionTrack *videoTrack = videoTrackArray[i % 2];
AVAssetTrack *videoAssetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
if (videoAssetTrack == nil) {
continue;
}
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
if (i == startIndex) {
timeRange = CMTimeRangeMake(firstAssetStartTime, CMTimeSubtract(asset.duration, firstAssetStartTime));
}
if (i == endIndex) {
timeRange = CMTimeRangeMake(kCMTimeZero, endAssetDuration);
}
[videoTrack insertTimeRange:timeRange ofTrack:videoAssetTrack atTime:startTime error:nil];
passThroughTimeRanges[i] = CMTimeRangeMake(startTime, timeRange.duration);
if (originalVolume > 0) {
[audioTrack insertTimeRange:timeRange ofTrack:[asset tracksWithMediaType:AVMediaTypeAudio][0] atTime:startTime error:nil];
AVMutableAudioMixInputParameters *audioTrackParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:audioTrack];
[audioTrackParameters setVolume:originalVolume atTime:timeRange.duration];
[audioMixArray addObject:audioTrackParameters];
}
startTime = CMTimeAdd(startTime,timeRange.duration);
}
從上一個模塊中獲取到的視頻分段信息數組 passThroughTimeRanges 將用來修改視頻尺寸數據。這里仿寫了上面文章提到的修改視頻尺寸方法,整體代碼如下所示。
NSMutableArray *instructions = [NSMutableArray arrayWithCapacity:16];
for (NSInteger i = startIndex; i <= endIndex; i++) {
AVMutableCompositionTrack *videoTrack = videoTrackArray[i % 2];
AVMutableVideoCompositionInstruction * passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
passThroughInstruction.timeRange = passThroughTimeRanges[i];
AVMutableVideoCompositionLayerInstruction * passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
[SDVideoUtils changeVideoSizeWithAsset:assetArray[i] passThroughLayer:passThroughLayer];
passThroughInstruction.layerInstructions = @[passThroughLayer];
[instructions addObject:passThroughInstruction];
}
videoComposition.instructions = instructions;
// 處理視頻尺寸大小
+ (void)changeVideoSizeWithAsset:(AVAsset *)asset passThroughLayer:(AVMutableVideoCompositionLayerInstruction *)passThroughLayer {
AVAssetTrack *videoAssetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
if (videoAssetTrack == nil) {
return;
}
CGSize naturalSize = videoAssetTrack.naturalSize;
if ([SDVideoUtils videoDegressWithVideoAsset:asset] == 90) {
naturalSize = CGSizeMake(naturalSize.height, naturalSize.width);
}
if ((int)naturalSize.width % 2 != 0) {
naturalSize = CGSizeMake(naturalSize.width + 1.0, naturalSize.height);
}
CGSize videoSize = SDVideoSize;
if ([SDVideoUtils videoDegressWithVideoAsset:asset] == 90) {
CGFloat height = videoSize.width * naturalSize.height / naturalSize.width;
CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(videoSize.width, videoSize.height/2.0 - height/2.0);
CGAffineTransform scaleTransform = CGAffineTransformScale(translateToCenter, videoSize.width/naturalSize.width, height/naturalSize.height);
CGAffineTransform mixedTransform = CGAffineTransformRotate(scaleTransform, M_PI_2);
[passThroughLayer setTransform:mixedTransform atTime:kCMTimeZero];
} else {
CGFloat height = videoSize.width * naturalSize.height / naturalSize.width;
CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(0, videoSize.height/2.0 - height/2.0);
CGAffineTransform scaleTransform = CGAffineTransformScale(translateToCenter, videoSize.width/naturalSize.width, height/naturalSize.height);
[passThroughLayer setTransform:scaleTransform atTime:kCMTimeZero];
}
}
到了這一步,視頻尺寸大小不一致問題基本上就算解決完成了,接下來我們就根據情況看是否需要合成伴奏信息,同時對混音信息進行添加,整體代碼如下。
// 插入伴奏
if (bgAudioAsset != nil && bgAudioVolume > 0) {
AVAssetTrack *assetAudioTrack = [[bgAudioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
[bgAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, startTime) ofTrack:assetAudioTrack atTime:kCMTimeZero error:nil];
AVMutableAudioMixInputParameters *bgAudioTrackParameters = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:bgAudioTrack];
[bgAudioTrackParameters setVolume:bgAudioVolume atTime:startTime];
[audioMixArray addObject:bgAudioTrackParameters];
}
audioMix.inputParameters = audioMixArray;
OK,到這里很多童鞋說接下來我們是不是需要把這些信息合成一個視頻文件導出即可,NONONO,這里我想說,我們有這些信息就可以播放了,我們只需要創建一個 AVPlayerItem,讓它攜帶這些信息即可。這些代碼則在 mergeMediaPlayerItemActionWithAssetArray 方法中體現的。
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:composition];
playerItem.videoComposition = videoComposition;
playerItem.audioMix = audioMix;
合成的這塊邏輯基本說完了,接下來我講講第二個坑,就是關于視頻幀圖片問題,由于視頻幀圖片獲取需要時間,同時如果需要符合抖音的話,我們需要將視頻幀圖片與視頻的時長進行對應,也就說視頻幀圖片可以不一定都是一樣大小。在這里我做了部分優化,例如存儲視頻的幀圖片信息,如果以前有該時間的視頻幀圖片,直接取出,不再進行重新獲取等等,但是還是沒有做到抖音那樣流暢程度,所以這里如果有大佬可以指導一下,騷棟不勝感激了。
視頻合成
不管是視頻錄制還是視頻剪輯說真的都是沒有用到視頻合成,主要是視頻合成太耗時,而且像視頻剪輯,每一次都合成會造成相當的卡頓的。視頻合成在視頻界面的 下一步 按鈕才會有體現。其他位置都沒有使用視頻合成,借此我們就先看一下視頻預覽的效果圖。
該模塊涉及到的類主要存放于 Preview 目錄中。
在預覽模塊中除了合成背景音樂外還有另外的一個功能就是添加貼紙或者添加文字,這就需要用到 SDVideoUtils 中的如下靜態方法。
/// 給視頻添加貼圖信息
/// @param composition 視頻合成器
/// @param size 視頻尺寸
/// @param layerArray 圖層數組
+ (void)applyVideoEffectsWithComposition:(AVMutableVideoComposition *)composition
size:(CGSize)size
layerArray:(NSArray <CALayer *>*)layerArray;
方法中的操作比較簡單,我直接上代碼了。
if (layerArray.count == 0) {
return;
}
CALayer *overlayLayer = [CALayer layer];
overlayLayer.frame = CGRectMake(0, 0, size.width, size.height);
overlayLayer.masksToBounds = YES;
for (CALayer *subLayer in layerArray) {
[overlayLayer addSublayer:subLayer];
}
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
videoLayer.frame = CGRectMake(0, 0, size.width, size.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:overlayLayer];
composition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
在視頻合成的時候,操作和視頻剪輯調用的方法是一直的,只是需要創建一個 AVAssetExportSession 對象,用來導出視頻文件,這個操作是在 mergeMediaActionWithAssetArray 方法中,整體代碼如下所示。
AVAssetExportSession *exporterSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
exporterSession.outputFileType = AVFileTypeMPEG4;
exporterSession.outputURL = [NSURL fileURLWithPath:mergePath]; //如果文件已存在,將造成導出失敗
exporterSession.videoComposition = videoComposition;
exporterSession.audioMix = audioMix;
exporterSession.shouldOptimizeForNetworkUse = YES; //用于互聯網傳輸
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[exporterSession exportAsynchronouslyWithCompletionHandler:^{
switch (exporterSession.status) {
case AVAssetExportSessionStatusUnknown:
NSLog(@"exporter Unknow");
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"exporter Canceled");
break;
case AVAssetExportSessionStatusFailed:
NSLog(@"exporter Failed");
break;
case AVAssetExportSessionStatusWaiting:
NSLog(@"exporter Waiting");
break;
case AVAssetExportSessionStatusExporting:
NSLog(@"exporter Exporting");
break;
case AVAssetExportSessionStatusCompleted:
NSLog(@"exporter Completed");
break;
}
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
結語
前前后后寫了將近一個月的時間才把這個一期版本寫完,如果喜歡歡迎給個Star,感謝閱讀,如果有任何問題,歡迎評論區指導批評。最后再次附上SDVideoCamera地址。