生活中我們經(jīng)常可能碰到這么一種需求摘仅,你有兩段視頻靶庙,你想將兩段視頻組合成一個(gè)視頻,你還想為組合的這段新視頻添加背景音樂(lè)实檀,這就要用到媒體組合技術(shù)惶洲,AVMutableComposition是這個(gè)技術(shù)的一個(gè)核心的類(lèi)按声,他繼承于AVComposition類(lèi),AVComposition類(lèi)又繼承于AVAsset資源類(lèi)恬吕。
現(xiàn)在有四段視頻签则,比如視頻1,視頻2铐料,視頻3渐裂,視頻4,實(shí)現(xiàn)功能钠惩,將視頻1柒凉,視頻2,視頻3組合一段視頻篓跛,視頻3要保證視頻3的視頻數(shù)據(jù)和音頻數(shù)據(jù)保持一致膝捞,提取視頻4的音頻數(shù)據(jù)作為新視頻的前段的背景音樂(lè)。
@property(nonatomic,strong)AVMutableComposition *mutableComposition;
@property(nonatomic,strong)AVAsset *nebual1Asset;
@property(nonatomic,strong)AVAsset *nebual3Asset;
@property(nonatomic,strong)AVAsset *backhole2Asset;
@property(nonatomic,strong)AVAsset *vidAsset;
@property(nonatomic,strong)AVMutableCompositionTrack *mutableVideoTrack;
@property(nonatomic,strong)AVMutableCompositionTrack *mutableAudioTrack;
@property(nonatomic,strong)AVAsset *compositionAsset;
@property(nonatomic,strong)AVPlayerItem *playerItem;
@property(nonatomic,strong)AVPlayer *player;
@property(nonatomic,strong)AVPlayerLayer *playerLayer;
@property(nonatomic,strong)NSString *storePath;
初始化相關(guān)數(shù)據(jù)
-(void)setAssetInfo
{
NSURL *nebula1Url = [[NSBundle mainBundle] URLForResource:@"01_nebula" withExtension:@"mp4"];
NSURL *nebula3Url = [[NSBundle mainBundle] URLForResource:@"03_nebula" withExtension:@"mp4"];
NSURL *backholeUrl = [[NSBundle mainBundle] URLForResource:@"02_blackhole" withExtension:@"mp4"];
NSURL *vidUrl = [[NSBundle mainBundle] URLForResource:@"video" withExtension:@"mp4"];
self.storePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.storePath = [self.storePath stringByAppendingPathComponent:@"cm.mp4"];
self.nebual1Asset = [AVURLAsset URLAssetWithURL:nebula1Url options:nil];
self.nebual3Asset = [AVURLAsset URLAssetWithURL:nebula3Url options:nil];
self.backhole2Asset = [AVURLAsset URLAssetWithURL:backholeUrl options:nil];
self.vidAsset = [AVURLAsset URLAssetWithURL:vidUrl options:nil];
self.mutableComposition = [AVMutableComposition composition];
//添加音頻軌道和視頻軌道
self.mutableVideoTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
self.mutableAudioTrack = [self.mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
}
組合各視頻和音頻
- (IBAction)btnClicked:(id)sender
{
CMTime startTime = kCMTimeZero;
CMTime duration = self.nebual1Asset.duration;
AVAssetTrack *video1Track = [[self.nebual1Asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
// 向視頻軌道中添加媒體片段
[self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(startTime, duration) ofTrack:video1Track atTime:kCMTimeZero error:nil];
AVAssetTrack *video2Track = [[self.nebual3Asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
// 向視頻軌道中添加媒體片段
[self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.nebual3Asset.duration) ofTrack:video2Track atTime:CMTimeAdd(startTime, duration) error:nil];
AVAssetTrack *vidVideoTrack = [[self.vidAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
// 向視頻軌道中添加媒體片段
[self.mutableVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.vidAsset.duration) ofTrack:vidVideoTrack atTime:CMTimeAdd(self.nebual3Asset.duration, self.nebual1Asset.duration) error:nil];
AVAssetTrack *audioTrack = [[self.backhole2Asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
// 向音頻軌道中添加媒體片段
[self.mutableAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, CMTimeAdd(self.nebual3Asset.duration, self.nebual1Asset.duration)) ofTrack:audioTrack atTime:kCMTimeZero error:nil];
AVAssetTrack *vidAudioTrack = [[self.vidAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
// 向音頻軌道中添加媒體片段
[self.mutableAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, self.vidAsset.duration) ofTrack:vidAudioTrack atTime:CMTimeAdd(self.nebual1Asset.duration, self.nebual3Asset.duration) error:nil];
//導(dǎo)出
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:self.mutableComposition presetName:AVAssetExportPresetMediumQuality];
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.outputURL = [NSURL fileURLWithPath:self.storePath];
[exportSession exportAsynchronouslyWithCompletionHandler:^{
//導(dǎo)出完成
if (exportSession.status == AVAssetExportSessionStatusCompleted ) { //橫屏播放
[self switchScreen];
//簡(jiǎn)單播放
[self playerCompostionVideo];
}
}];
}
橫屏實(shí)現(xiàn)愧沟,強(qiáng)制橫屏主要用當(dāng)前的設(shè)備的setOrientation方法蔬咬,當(dāng)然首先需要判斷是否能夠響應(yīng)該方法,如何可以就調(diào)用沐寺,這里使用了NSInvocation調(diào)用對(duì)象林艘,其實(shí)NSInvocation封裝了調(diào)用函數(shù)所需的所有的信息,比如那個(gè)對(duì)象發(fā)起調(diào)用-target混坞,調(diào)用哪個(gè)方法-selector狐援,調(diào)用的參數(shù)setArgument設(shè)置,如何有返回值也可以通過(guò)getReturn方法得到究孕,這里需要注意的是使用setArgument設(shè)置參數(shù)時(shí)參數(shù)的索引之從2開(kāi)始啥酱,其實(shí)這一點(diǎn)一點(diǎn)都不奇怪,我們要知道IMP函數(shù)指針指向的函數(shù)前兩個(gè)參數(shù)就是隱藏的target蚊俺,selector懈涛,所有從2開(kāi)始。
-(void)switchScreen
{
dispatch_async(dispatch_get_main_queue(), ^{
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
{
SEL selector=NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation =[NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val =UIInterfaceOrientationLandscapeRight;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
});
}
playerCompostionVideo簡(jiǎn)單的播放
-(void)playerCompostionVideo
{
NSFileManager *fileMange = [NSFileManager defaultManager];
if ([fileMange fileExistsAtPath:self.storePath]) {
NSURL *videoUrl = [NSURL fileURLWithPath:self.storePath];
self.compositionAsset = [AVAsset assetWithURL:videoUrl];
self.playerItem = [[AVPlayerItem alloc] initWithAsset:self.compositionAsset];
//kvo跟蹤playerItem的status狀態(tài)
[self.playerItem addObserver:self forKeyPath:@"status" options:0 context:nil];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
dispatch_async(dispatch_get_main_queue(), ^{
self.playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:self.playerLayer];
});
}
}
使用KVO觀察AVPlayerItem的status狀態(tài)泳猬,當(dāng)AVPlayerItemStatusReadyToPlay處于準(zhǔn)備播放狀態(tài)時(shí),開(kāi)始播放
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
AVPlayerItem *playerItem = (AVPlayerItem*)object;
if (playerItem.status == AVPlayerItemStatusReadyToPlay) {
//播放
[self.player play];
}
}
至此宇植,媒體組合的功能就是實(shí)現(xiàn)了得封,媒體組合技術(shù)還是蠻使用的,很多媒體編輯的軟件都用得到指郁。