parent
244eacd306
commit
b73f4ddfd3
@ -0,0 +1,154 @@ |
|||||||
|
# iOS短视频篇:音视频编辑之音视频合成,添加水印及音视频导出 |
||||||
|
|
||||||
|
## 1.基本介绍 |
||||||
|
|
||||||
|
音视频编辑主要依靠AVFoundation框架,首先要有一个AVMutableComposition对象composition,一个composition可以简单的认为是一组轨道(tracks)的集合,这些轨道可以是来自不同媒体资源AVAsset,AVMutableComposition提供了接口来插入或者删除轨道,也可以调整这些轨道的顺序。 |
||||||
|
|
||||||
|
下面这张图反映了一个新的 mixComposition 是怎么从已有的 AVAsset 中获取对应的 track 并进行拼接形成新的 AVAsset |
||||||
|
|
||||||
|
![img](https://img-blog.csdnimg.cn/20190511151431700.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQzMzQ4MA==,size_16,color_FFFFFF,t_70) |
||||||
|
|
||||||
|
如下图所示,我们还可以使用 AVMutableVideoComposition 来直接处理 composition 中的视频轨道。处理一个单独的 video composition 时,你可以指定它的渲染尺寸、缩放比例、帧率等参数并输出最终的视频文件。通过一些针对 video composition 的指令(AVMutableVideoCompositionInstruction 等),我们可以修改视频的背景颜色、应用 layer instructions。这些 layer instructions(AVMutableVideoCompositionLayerInstruction 等)可以用来对 composition 中的视频轨道实施图形变换、添加图形渐变、透明度变换、增加透明度渐变。此外,你还能通过设置 video composition 的 animationTool 属性来应用 Core Animation Framework 框架中的动画效果。 |
||||||
|
|
||||||
|
![img](https://img-blog.csdnimg.cn/20190511152526982.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQzMzQ4MA==,size_16,color_FFFFFF,t_70) |
||||||
|
|
||||||
|
如下图所示,你可以使用 AVAssetExportSession 相关的接口来合并你的 composition 中的 audio mix 和 video composition。你只需要初始化一个 AVAssetExportSession 对象,然后将其 audioMix 和 videoComposition 属性分别设置为你的 audio mix 和 video composition 即可。 |
||||||
|
|
||||||
|
![img](https://img-blog.csdnimg.cn/20190511152942596.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQzMzQ4MA==,size_16,color_FFFFFF,t_70) |
||||||
|
|
||||||
|
## 2.创建Composition对象 |
||||||
|
|
||||||
|
当使用 AVMutableComposition 创建自己的 composition 时,最典型的,我们可以使用 AVMutableCompositionTrack 来向 composition 中添加一个或多个 composition tracks,比如下面这个简单的例子便是向一个 composition 中添加一个音频轨道和一个视频轨道: |
||||||
|
|
||||||
|
//音频视频合成体 |
||||||
|
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; |
||||||
|
|
||||||
|
//创建音频通道容器 |
||||||
|
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio |
||||||
|
preferredTrackID:kCMPersistentTrackID_Invalid]; |
||||||
|
//创建视频通道容器 |
||||||
|
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo |
||||||
|
preferredTrackID:kCMPersistentTrackID_Invalid]; |
||||||
|
|
||||||
|
当为 composition 添加一个新的 track 的时候,需要设置其媒体类型(media type)和 track ID,主要的媒体类型包括:音频、视频、字幕、文本等等。 |
||||||
|
|
||||||
|
这里需要注意的是,每个 track 都需要一个唯一的 track ID,比较方便的做法是:设置 track ID 为 kCMPersistentTrackID_Invalid 来为对应的 track 获得一个自动生成的唯一 ID。 |
||||||
|
|
||||||
|
## 3.向 Composition 添加音视频数据 |
||||||
|
|
||||||
|
要将音视频数据添加到一个 composition track 中需要访问媒体数据所在的 AVAsset,可以使用 AVMutableCompositionTrack 的接口将具有相同媒体类型的多个 track 添加到同一个 composition track 中。下面的例子便是从多个 AVAsset 中各取出一份 video asset track,再添加到一个新的 composition track 中去 |
||||||
|
|
||||||
|
CMTime totalDuration = kCMTimeZero; |
||||||
|
for (int i = 0; i < videosPathArray.count; i++) { |
||||||
|
// AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:videosPathArray[i]]]; |
||||||
|
//如果创建AVURLAsset时传入的AVURLAssetPreferPreciseDurationAndTimingKey值为NO(不传默认为NO),duration会取一个估计值,计算量比较小。反之如果为YES,duration需要返回一个精确值,计算量会比较大,耗时比较长 |
||||||
|
NSDictionary* options = @{AVURLAssetPreferPreciseDurationAndTimingKey:@YES}; |
||||||
|
AVAsset* asset = [AVURLAsset URLAssetWithURL:videosPathArray[i] options:options]; |
||||||
|
|
||||||
|
|
||||||
|
NSError *erroraudio = nil; |
||||||
|
//获取AVAsset中的音频 或者视频 |
||||||
|
AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject]; |
||||||
|
//向通道内加入音频或者视频 |
||||||
|
BOOL ba = [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) |
||||||
|
ofTrack:assetAudioTrack |
||||||
|
atTime:totalDuration |
||||||
|
error:&erroraudio]; |
||||||
|
|
||||||
|
NSLog(@"erroraudio:%@%d",erroraudio,ba); |
||||||
|
NSError *errorVideo = nil; |
||||||
|
AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]firstObject]; |
||||||
|
BOOL bl = [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) |
||||||
|
ofTrack:assetVideoTrack |
||||||
|
atTime:totalDuration |
||||||
|
error:&errorVideo]; |
||||||
|
|
||||||
|
NSLog(@"errorVideo:%@%d",errorVideo,bl); |
||||||
|
totalDuration = CMTimeAdd(totalDuration, asset.duration); |
||||||
|
} |
||||||
|
|
||||||
|
这里需要注意:如果创建AVURLAsset时传入的AVURLAssetPreferPreciseDurationAndTimingKey值为NO(不传默认为NO),duration会取一个估计值,计算量比较小。反之如果为YES,duration需要返回一个精确值,计算量会比较大,耗时比较长,一般我们对短视频的音视频编辑时都是将这个属性设置为YES,来获取准确的duration。 |
||||||
|
|
||||||
|
## 4.视频水印处理 |
||||||
|
|
||||||
|
处理音频是我们使用 AVMutableAudioMix,那么处理视频时,我们就使用 AVMutableVideoComposition,只需要一个 AVMutableVideoComposition 实例就可以为 composition 中所有的 video track 做处理,比如设置渲染尺寸、缩放、播放帧率等等。所有的 video composition 也必然对应一组 AVVideoCompositionInstruction 实例,每个 AVVideoCompositionInstruction 中至少包含一条 video composition instruction。我们可以使用 AVMutableVideoCompositionInstruction 来创建我们自己的 video composition instruction,通过这些指令,我们可以修改 composition 的背景颜色、后处理、layer instruction 等等。 |
||||||
|
|
||||||
|
我们也可以用 video composition instructions 来应用 video composition layer instructions。AVMutableVideoCompositionLayerInstruction 可以用来设置 video track 的图形变换、图形渐变、透明度、透明度渐变等等。一个 video composition instruction 的 layerInstructions 属性中所存储的 layer instructions 的顺序决定了 tracks 中的视频帧是如何被放置和组合的。 |
||||||
|
|
||||||
|
我们还能通过设置 video composition 的 animationTool 属性来使用 Core Animation Framework 框架的强大能力。比如:设置视频水印、视频标题、动画浮层等。 |
||||||
|
|
||||||
|
下面的代码展示了直接使用 Core Animation Layer 在视频帧中渲染动画效果的一个例子,在视频右上角添加水印: |
||||||
|
|
||||||
|
CGSize videoSize = [videoTrack naturalSize]; |
||||||
|
CALayer* aLayer = [CALayer layer]; |
||||||
|
aLayer.contents = (id)waterImg.CGImage; |
||||||
|
aLayer.frame = CGRectMake(videoSize.width - waterImg.size.width - 30, videoSize.height - waterImg.size.height*3, waterImg.size.width, waterImg.size.height); |
||||||
|
aLayer.opacity = 0.9; |
||||||
|
|
||||||
|
CALayer *parentLayer = [CALayer layer]; |
||||||
|
CALayer *videoLayer = [CALayer layer]; |
||||||
|
parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); |
||||||
|
videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); |
||||||
|
[parentLayer addSublayer:videoLayer]; |
||||||
|
[parentLayer addSublayer:aLayer]; |
||||||
|
AVMutableVideoComposition* videoComp = [AVMutableVideoComposition videoComposition]; |
||||||
|
videoComp.renderSize = videoSize; |
||||||
|
|
||||||
|
//表示 30 帧每秒 |
||||||
|
videoComp.frameDuration = CMTimeMake(1, 30); |
||||||
|
//应用 Core Animation Framework 框架中的动画效果。 |
||||||
|
videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; |
||||||
|
AVMutableVideoCompositionInstruction* instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; |
||||||
|
|
||||||
|
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mixComposition duration]); |
||||||
|
AVAssetTrack* mixVideoTrack = [[mixComposition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; |
||||||
|
AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mixVideoTrack]; |
||||||
|
//layerInstructions 属性中所存储的 layer instructions 的顺序决定了 tracks 中的视频帧是如何被放置和组合的。 |
||||||
|
instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction]; |
||||||
|
videoComp.instructions = [NSArray arrayWithObject: instruction]; |
||||||
|
|
||||||
|
## 5.音视频导出 |
||||||
|
|
||||||
|
主要是通过AVAssetExportSession这个类来导出音视频,它可以用来合并你添加了水印等的videoComposition和audioMix。 |
||||||
|
|
||||||
|
下面是使用AVAssetExportSession导出音视频的例子: |
||||||
|
|
||||||
|
NSURL *mergeFileURL = [NSURL fileURLWithPath:outpath]; |
||||||
|
|
||||||
|
|
||||||
|
//视频导出工具 |
||||||
|
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition |
||||||
|
presetName:AVAssetExportPreset1280x720]; |
||||||
|
exporter.videoComposition = videoComp; |
||||||
|
/* |
||||||
|
exporter.progress |
||||||
|
导出进度 |
||||||
|
This property is not key-value observable. |
||||||
|
不支持kvo 监听 |
||||||
|
只能用定时器监听了 NStimer |
||||||
|
*/ |
||||||
|
exporter.outputURL = mergeFileURL; |
||||||
|
exporter.outputFileType = AVFileTypeQuickTimeMovie; |
||||||
|
exporter.shouldOptimizeForNetworkUse = YES; |
||||||
|
[exporter exportAsynchronouslyWithCompletionHandler:^{ |
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{ |
||||||
|
|
||||||
|
[videoCamera stopCameraCapture]; |
||||||
|
|
||||||
|
EditVideoViewController* view = [[EditVideoViewController alloc]init]; |
||||||
|
view.width = _width; |
||||||
|
view.hight = _hight; |
||||||
|
view.bit = _bit; |
||||||
|
view.frameRate = _frameRate; |
||||||
|
view.videoURL = [NSURL fileURLWithPath:outpath];; |
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
||||||
|
|
||||||
|
// [[AppDelegate sharedAppDelegate] pushViewController:view animated:YES]; |
||||||
|
if (self.delegate&&[self.delegate respondsToSelector:@selector(pushCor:)]) { |
||||||
|
[self.delegate pushCor:view]; |
||||||
|
} |
||||||
|
[self removeFromSuperview]; |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
原文链接:https://blog.csdn.net/weixin_42433480/article/details/90109873 |
Loading…
Reference in new issue