回顾
GPUImage源码解析、图片模糊、视频滤镜、视频水印、文字水印和动态图片水印GPUImage的大多数功能已经介绍完毕,这次的demo是源于简书的一位简友问我如何用GPUImage进行混音,他需要对视频添加水印和背景音乐。
经过一番研究,找到了一个解决方案,下面我们按照这个方案进行实践,并学习如何进行混音。
知识储备
1、AVFoundation
-
AVAssetReader
从原始数据里获取音视频数据 -
AVAssetReaderTrackOutput
读取每帧的CMSampleBufferRef -
AVAssetTrack
视频轨迹,视频来源 -
AVAsset
主要用于获取多媒体信息,抽象类不能直接使用 -
AVURLAsset
AVAsset
的子类,根据URL路径创建包含媒体信息的AVURLAsset对象 -
AVPlayerItem
媒体资源管理对象,管理视频的基本信息和状态
2、GCD
-
dispatch_group_notify
等待group里面所有任务结束后调用 -
dispatch_group_enter
开始一个group任务 -
dispatch_group_leave
结束一个group任务
用处:在dispatch_queue中开始一个group任务,当group里面所有任务完成调用再执行最后的任务。
在demo主要用于等待异步加载Reader和等待视频合并完成。
核心思路
- 1、异步初始化音视频的AVAssetReader;
- 2、调用dispatch_group_leave通知异步加载完成;
- 3、通过dispatch_group_notify开始录制;
- 4、开始异步合并视频信息、音频信息;
- 5、调用dispatch_group_leave通知合并完成;
-
6、录制结束,并回调completionBlock;
具体细节
1、音频流解析
- 1、根据movieFile的url创建AVAsset对象;
- 2、根据asset加载轨道信息;
- 3、分别添加AVMutableCompositionTrack到AVMutableComposition对象上;
- 4、为音频assetReader设置mixComposition对象;
-
5、把assetAudioReaderTrackOutput设置为音频信息的输出;
2、视频流解析
3、THImageMovieWriter对象解析
THImageMovieWriter对象和GPUImageMovieWriter非常类似,核心的逻辑也是分为音频信息写入和视频信息写入。
代码解析
右边是代码地址。
THImageMovie
添加了renderNextFrame方法。如果还有下一帧视频信息,那么返回Yes,如果没有则返回No。
- (BOOL)renderNextFrame {
__unsafe_unretained THImageMovie *weakSelf = self;
if (reader.status == AVAssetReaderStatusReading && (!_shouldRepeat || keepLooping))
{
return [weakSelf readNextVideoFrameFromOutput:readerVideoTrackOutput];
}
if (reader.status == AVAssetWriterStatusCompleted) {
NSLog(@"movie: %@ reading is done", self.url.lastPathComponent);
[reader cancelReading];
[weakSelf endProcessing];
}
return NO;
}
THImageMovieWriter
下面是核心的逻辑,设置多个音轨的合并信息,并通过AVMutableComposition设置为AVAssetReader的输入。
/**
* 设置读取音频信息的Reader
*/
- (void)setupAudioAssetReader {
NSMutableArray *audioTracks = [NSMutableArray array];
for(THImageMovie *movie in self.movies){
AVAsset *asset = movie.asset;
if(asset){
NSArray *_audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
if(_audioTracks.count > 0){
[audioTracks addObject:_audioTracks.firstObject];
}
}
}
AVMutableComposition* mixComposition = [AVMutableComposition composition];
for(AVAssetTrack *track in audioTracks){
if(![track isKindOfClass:[NSNull class]]){
NSLog(@"track url: %@ duration: %.2f", track.asset, CMTimeGetSeconds(track.asset.duration));
AVMutableCompositionTrack *compositionCommentaryTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionCommentaryTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, track.asset.duration)
ofTrack:track
atTime:kCMTimeZero error:nil];
}
}
self.assetAudioReader = [AVAssetReader assetReaderWithAsset:mixComposition error:nil];
self.assetAudioReaderTrackOutput =
[[AVAssetReaderAudioMixOutput alloc] initWithAudioTracks:[mixComposition tracksWithMediaType:AVMediaTypeAudio]
audioSettings:nil];
[self.assetAudioReader addOutput:self.assetAudioReaderTrackOutput];
}
总结
为什么GPUImage没有支持音轨合并?
GPUImage的核心是响应链,通过GPU对图像进行加工,并且download下来。
而音频信息没有这么流畅的操作,作者没有进行支持。
苹果的官方有纯AVFoundation实现的视频合并和音频合并,但是学习的成本非常高,研究了几天还是没有吃透。而且和GPUImage没有关系,就不写入本次教程,留待以后单开一篇。
AVFoundation的内容还不够熟悉,这次很多时间是花在理解和消化音轨相关的知识。
留下一个思考题:
GPUImage做出来的视频有时候会遇到视频特别长,或者是没有声音的情况,可能是什么原因导致的?