AVFoundation详细解析(一)视频合并与混音

回顾

在上一篇GPUImage详细解析(八)视频合并混音介绍了如何使用GPUImage进行视频的合并,以及混音。这次使用AVFoundation框架来实现这个功能。

概念

  • AVPlayer 视频播放类,本身不显示视频,需创建一个AVPlayerLayer层,添加到视图
  • AVAssetTrack 资源轨道,包括音频轨道和视频轨道
  • AVAsset 媒体信息
  • AVURLAsset 根据URL路径创建的媒体信息
  • AVPlayerItem媒体资源管理对象,管理视频的基本信息和状态
  • AVMutableVideoCompositionInstruction 视频操作指令
  • AVMutableVideoCompositionLayerInstruction视频轨道操作指令,需要添加到AVMutableVideoCompositionInstruction
  • AVMutableAudioMixInputParameters音频操作参数
  • AVMutableComposition 包含多个轨道的媒体信息,可以添加、删除轨道
  • AVMutableVideoComposition视频操作指令集合

效果

视频效果如下,音频效果可运行demo

核心思路

分别加载多个AVURLAsset,用GCD保证异步加载完成后回调,调用Editor类配置轨道信息、视频操作指令和音频指令参数。

具体细节

流程图如下

a、配置轨道信息

  • 1,计算变化的长度,确保变换的长度不大于最小的视频的长度的一半;

思考1:demo中是如何计算小于一半,为何要小于一半?

  • 2,添加两个视频轨道,两个音频轨道;
  • 3,在视频索引对应的轨道(%2),插入视频轨道信息和音频轨道信息;

思考2:当多个视频在同一个音轨插入多个信息,如何保证不重叠?

  • 4,计算直接播放和变换的时间;
    // 确保最后合并后的视频,变换长度不会超过最小长度的一半
    CMTime transitionDuration = self.transitionDuration;
    for (i = 0; i < clipsCount; i++ ) {
        NSValue *clipTimeRange = [self.clipTimeRanges objectAtIndex:i];
        if (clipTimeRange) {
            CMTime halfClipDuration = [clipTimeRange CMTimeRangeValue].duration;
            halfClipDuration.timescale *= 2;
            transitionDuration = CMTimeMinimum(transitionDuration, halfClipDuration);
        }
    }
    AVMutableCompositionTrack *compositionVideoTracks[2];
    AVMutableCompositionTrack *compositionAudioTracks[2];
    compositionVideoTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加视频轨道0
    compositionVideoTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加视频轨道1
    compositionAudioTracks[0] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音频轨道0
    compositionAudioTracks[1] = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 添加音频轨道1

b、配置视频操作指令

  • 1,新建视频操作指令集合;
  • 2,根据视频所在对应的轨道(%2),新建视频操作指令passThroughInstruction,长度为passThroughTimeRanges,同时定义passThroughLayer直接播放的视频轨道操作指令,并设置passThroughLayer为passThroughInstruction的视频轨道操作指令集合;
  • 3,根据视频所在对应轨道,新建视频操作指令transitionInstruction,长度为transitionTimeRanges,同时根据轨道定义视频轨道操作指令fromLayer和toLayer,并设置fromLayer和toLayer的变换方式与时间;
  • 4,把passThroughInstruction和transitionInstruction添加到视频指令集合;
        AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
        passThroughInstruction.timeRange = passThroughTimeRanges[i]; // 直接播放
        AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 视频轨道操作指令
        
        passThroughInstruction.layerInstructions = [NSArray arrayWithObject:passThroughLayer];
        [instructions addObject:passThroughInstruction]; // 添加到指令集合
        
        if (i+1 < clipsCount) { // 不是最后一个
            AVMutableVideoCompositionInstruction *transitionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; // 新建指令
            transitionInstruction.timeRange = transitionTimeRanges[i]; // 变换时间
            AVMutableVideoCompositionLayerInstruction *fromLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[alternatingIndex]]; // 视频轨道操作指令
            AVMutableVideoCompositionLayerInstruction *toLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTracks[1-alternatingIndex]]; // 新的轨道指令
            // 1 dao 0
            [fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:transitionTimeRanges[i]];
            // 目的轨道,从0到1
            [toLayer setOpacityRampFromStartOpacity:0.0 toEndOpacity:1.0 timeRange:transitionTimeRanges[i]];
            
            transitionInstruction.layerInstructions = [NSArray arrayWithObjects:toLayer, fromLayer, nil];
            [instructions addObject:transitionInstruction];
        }

c、配置音频轨道参数

  • 1,新建音频轨道参数集合;
  • 2,根据视频所在索引,新建当前音轨的参数trackMix1,设置变换时间内音量从1.0到0.0;
  • 3,根据视频所在索引,新建另外一条音轨的参数trackMix2,设置变换时间内音量从0.0到1.0;设置直接播放时间内音量一直为1.0;
  • 4,把参数trackMix1和trackMix2添加到音频轨道参数集合;
AVMutableAudioMixInputParameters *trackMix1 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[alternatingIndex]]; // 音轨0的参数
[trackMix1 setVolumeRampFromStartVolume:1.0 toEndVolume:0.0 timeRange:transitionTimeRanges[i]]; // 音轨0,变换期间音量从1.0到0.0
[trackMixArray addObject:trackMix1];
AVMutableAudioMixInputParameters *trackMix2 = [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:compositionAudioTracks[1 - alternatingIndex]]; // 音轨1的参数
[trackMix2 setVolumeRampFromStartVolume:0.0 toEndVolume:1.0 timeRange:transitionTimeRanges[i]]; // 变换期间音量从0.0到1.0           
[trackMixArray addObject:trackMix2];

总结

AVPlayer通过KVO监听rate属性,status属性,用notification来监听播放完成; AVPlayer和AVPlayerItem的使用不复杂,解析集中在SimpleEditor类如何配置轨道信息和音视频操作指令。 代码地址可以点这里

思考

思考1

通过timescale*2,再用CMTimeMinimum;处于中间的视频要经历两次变换,故而变换的长度不能大于最小视频长度的一半;

思考2

音轨插入的函数有开始点和持续时间,只要保证区间不重叠,音频就不会重叠;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

据说这篇总结覆盖了一般Python开发面试中可能会问到的大部分问题

? 原文标题:一名python web后端开发工程师的面试总结 先介绍下我的情况 通信背景,工作一年多不到两年。之前一直在做C++的MFC软件界面开发工作。公...

5206
来自专栏咸鱼不闲

科大讯飞语音识别和语音播放dome

首先登陆科大讯飞开发者平台,注册账号,(走你->http://www.xfyun.cn/) 可以根据功能(语音识别,语音播放等),平台(java,window等...

1.2K5
来自专栏源码之家

千脑中广告位使用方法

1894
来自专栏奇点大数据

算法之旅(2)——朴素的存取

上次我们说到算法最基本的处理规则和算法在计算机底层所藉由的工作方式。这次我们来说说计算机中最简单的算法,最朴素的数据存取。 也许有的朋友觉得这种问题太底层,简直...

2405
来自专栏云飞学编程

Python web开发!给零基础不知道方向的同学的一节课!

推荐下小编的Python学习群5421107414,不管你是小白还是大牛,小编我都欢迎,不定期分享干货,包括小编自己整理的一份2018最新的Python和0基础...

1291
来自专栏云飞学编程

十年开发经验老司机如何搞定动态加载!用Python爬取知乎粉丝信息

推荐下小编的Python学习群5421107414,不管你是小白还是大牛,小编我都欢迎,不定期分享干货,包括小编自己整理的一份2018最新的Python和0基础...

1251
来自专栏chenssy

美团面试经历,贡献出来一起学习

美团我是在拉勾网上投的简历,之前也投过一次,简历都没通过删选,后来让学姐帮我改了一下简历,重新投另一个部门,获得了面试机会。10月23日中午HR打电话过来预约了...

6802
来自专栏星流全栈

什么是「设计模式」?

1203
来自专栏AI研习社

如何在 i5 上实现 20 倍的 Python 运行速度?

Intel Distribution for Python 在今年二月进行了更新——英特尔发布了 Update 2 版本。以“加速”为核心的它,相比原生 Pyt...

43613
来自专栏牛客网

多篇面经集合,你不容错过的干货!

  5. 写一个单例模式,答主写的是双检查锁单例,问了为什么用 Volatile,synchronize 移到 方法最外面会怎么样?

1312

扫码关注云+社区

领取腾讯云代金券