当前位置: 首页 > news >正文

七.音视频编辑-创建视频过渡-应用

引言

在上一篇博客中,我们已经介绍了创建视频过渡的实现方案,步骤非常繁琐,在生成AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction的计算也十分复杂,但其实还有一个创建视频组合的捷径。不过我们还是需要理解上一篇博客中我们所讨论的步骤,只有理解了那些步骤,才能发现学习这些对应的使用变得容易许多。

创建组合的捷径

AVVideoComposition定义了一个十分便捷的初始化方法init(propertiesOf asset: AVAsset),我们可以将AVCompostion作为参数来创建一个AVVideoComposition。该方法会为我们创建一个带有如下配置的AVVideoComposition对象:

  • instructions  属性包含一组完整的基于组合视频轨道(以及其中包含的片段空间布局)的组合和层指令。
  • renderSize  属性被设置为AVComposition对象的naturalSize,或者如果没有设置,则使用能够满足组合视频轨道中最大视频维度的尺寸值。
  • frameDuration  设置为组合视频轨道中最大nominalFrameRate的值。如果所有轨道的nominalFrameRate值都为0,则frameDuration设置成默认1/30秒(30FPS)。
  • renderScale  始终设置为1.0。

创建视频过渡

下面我们开始着手创建视频过渡,和之前的实现方案一样,先创建一个遵循PHComposition协议名为PHTransitionComposition的类,以及遵循PHCompositionBuilder协议名为PHTransitionCompositionBuilder的类。

PHTransitionComposition负责构建视频的可播放和可导出版本。

PHTransitionCompositionBuilder负责构建PHTransitionComposition,里面会创建用于视频编辑的AVMutableComposition,AVMutableAudioMix以及AVMutableVideoComposition。

PHTransitionComposition

代码实现如下:

import UIKit
import AVFoundationclass PHTransitionComposition: NSObject,PHComposition {/// 组合轨道var composition:AVMutableComposition!/// 视频轨道var videoComposition:AVMutableVideoComposition?/// 音频混合var audioMix:AVMutableAudioMix?init(composition: AVMutableComposition!, videoComposition: AVMutableVideoComposition?, audioMix: AVMutableAudioMix?) {self.composition = compositionself.videoComposition = videoCompositionself.audioMix = audioMix}func makePlayerItem() -> AVPlayerItem? {let playerItem = AVPlayerItem(asset: composition.copy() as! AVAsset)playerItem.videoComposition = videoCompositionplayerItem.audioMix = audioMixreturn playerItem}func makeAssetExportSession() -> AVAssetExportSession? {let exportSession = AVAssetExportSession(asset: composition.copy() as! AVAsset, presetName: AVAssetExportPresetHighestQuality)exportSession?.videoComposition = videoCompositionexportSession?.audioMix = audioMixreturn exportSession!}}

代码较以前的类相比多了一个AVMutableVideoComposition属性,用来实现视频的过渡效果。

PHTransitionCompositionBuilder

该类中的代码和以往一样主要目的是构建PHComposition,分成三个部分,添加视频到组合轨道,添加音频到组合轨道,添加背景音乐到组合轨道。

let defaultTransitionDuration = CMTime(value: 2, timescale: 1)class PHTransitionCompositionBuilder: NSObject,PHCompositionBuilder {/// 资源模型var timeLine:PHTimeLine!/// 组合轨道let composition = AVMutableComposition()init(timeLine: PHTimeLine!) {self.timeLine = timeLine}func buildComposition() -> PHComposition? {// 添加视频到组合轨道addVideoCompositionTrack()// 创建AVVideoCompositionlet videoComposition = buildVideoComposition()// 添加音频到组合轨道addAudioCompositionTrack()// 添加背景音乐到组合轨道let audioMix = addMusicCompositionTrack()return PHTransitionComposition(composition: composition, videoComposition: videoComposition, audioMix: audioMix)}/// 添加视频到组合轨道func addVideoCompositionTrack() {....}/// 创建AVVideoCompositionfunc buildVideoComposition() -> AVMutableVideoComposition? {....}/// 添加音频到组合轨道func addAudioCompositionTrack() {let _ = addCompositionTrack(mediaType: .audio, mediaItems: timeLine.audioItems)}/// 添加背景音乐到组合轨道func addMusicCompositionTrack() -> AVMutableAudioMix?{// 添加背景音乐var audioMix:AVMutableAudioMix? = nilif timeLine.musicItem != nil {let musicCompositionTrack =  addCompositionTrack(mediaType: .audio, mediaItems: [timeLine.musicItem!])let musicAudioMix = buildAudioMixWithTrack(track: musicCompositionTrack)audioMix = musicAudioMix}return audioMix}/// 私有方法-添加媒体资源轨道/// - Parameters:///  - mediaType: 媒体类型///  - mediaItems: 媒体媒体资源数组///  - Returns: 返回一个AVCompositionTrackprivate func addCompositionTrack(mediaType:AVMediaType,mediaItems:[PHMediaItem]?) -> AVMutableCompositionTrack? {if PHIsEmpty(array: mediaItems) {return nil}let trackID = kCMPersistentTrackID_Invalidguard let compositionTrack = composition.addMutableTrack(withMediaType: mediaType, preferredTrackID: trackID) else { return nil }//设置起始时间var cursorTime = CMTime.zeroguard let mediaItems = mediaItems else { return nil }for item in mediaItems {//这里默认时间都是从0开始guard let asset = item.asset else { continue }guard let assetTrack = asset.tracks(withMediaType: mediaType).first  else { continue }do {try compositionTrack.insertTimeRange(item.timeRange, of: assetTrack, at: cursorTime)} catch {print("addCompositionTrack error")}cursorTime = CMTimeAdd(cursorTime, item.timeRange.duration)}return compositionTrack}/// 创建音频混合器/// - Parameters:///  - musicTrack: 音乐轨道func buildAudioMixWithTrack(track:AVMutableCompositionTrack?) -> AVMutableAudioMix? {guard let track = track else { return nil }guard let musicItem = timeLine.musicItem else { return nil }let audioMix = AVMutableAudioMix()let audioMixParam = AVMutableAudioMixInputParameters(track: track)for volumeAutomaition in musicItem.volumeAutomations {audioMixParam.setVolumeRamp(fromStartVolume: volumeAutomaition.startVolume, toEndVolume: volumeAutomaition.endVolume, timeRange: volumeAutomaition.timeRange)}audioMix.inputParameters = [audioMixParam]return audioMix}}

关于添加音频和背景音乐以及创建AVAudioMix的部分,我们在前面的博客中已经进行过介绍,在这里就不再重复解释了,把重点放到添加视频到组合轨道以及创建AVVideoComposition上。

下面看一下添加视频到组合轨道的实现:

    /// 添加视频到组合轨道func addVideoCompositionTrack() {let compositionTrackA = self.composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)let compositionTrackB = self.composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)let videoTracks = [compositionTrackA,compositionTrackB]let transitionDuration = defaultTransitionDuration// 初始时间var cursorTime = CMTime.zerolet videoItems = timeLine.videoItmesfor (index,item) in videoItems.enumerated() {let trackIndex = index % 2let currentTrack = videoTracks[trackIndex]guard let asset = item.asset else { continue }guard let assetTrack = asset.tracks(withMediaType: .video).first else { continue }do {try currentTrack?.insertTimeRange(item.timeRange, of: assetTrack, at: cursorTime) } catch {print("insertTimeRange error")}// 更新cursorTimecursorTime = CMTimeAdd(cursorTime, item.timeRange.duration)cursorTime = CMTimeSubtract(cursorTime, transitionDuration)}}
  1. 首先从当前的AVMutableComposition中创建两个新的AVMutableCompositionTrack对象,两者都是.video类型,并添加到videoTracks数组中,提供所需的A-B轨道排列。
  2. 遍历视频资源,将视频资源交替插入到AB两个轨道中。
  3. 计算cursorTime,需要考虑到减去动画过程的时间,因为这段时间两个视频媒体是重叠的。

创建AVVideoComposition的实现如下:

    /// 创建AVVideoCompositionfunc buildVideoComposition() -> AVMutableVideoComposition? {let videoComposition = AVMutableVideoComposition(propertiesOf: self.composition)videoComposition.renderSize = CGSize(width: 1920, height: 1080)/// 获取instructionslet instructions = videoComposition.instructionsvar layerInstructionIndex = 1for instruction in instructions {guard let videoCompositionInstruction = instruction as? AVMutableVideoCompositionInstruction else { continue }let layerInstructions = videoCompositionInstruction.layerInstructions// 判断是否有两个layerInstructionsguard layerInstructions.count == 2 else { continue }// 创建过渡效果let fromeLayerInstruction = layerInstructions[1 - layerInstructionIndex] as! AVMutableVideoCompositionLayerInstructionlet toLayerInstruction = layerInstructions[layerInstructionIndex] as! AVMutableVideoCompositionLayerInstruction// 设置动画fromeLayerInstruction.setOpacityRamp(fromStartOpacity: 1.0, toEndOpacity: 0.0, timeRange: videoCompositionInstruction.timeRange)layerInstructionIndex = layerInstructionIndex == 1 ? 0 : 1}return videoComposition}
  1. 使用init(propertiesOf asset: AVAsset)创建一个新的AVVideoComposition实例,这个方法会自动创建所需的组合对象和层指令。并设置renderSize、renderScale、frameDuration熟悉为相应的值。(renderSize需要满足组合视频轨道中最大视频维度的尺寸值)
  2. 遍历videoComposition的instructions属性,判断videoCompositionInstruction的layerInstructions个数等于2的情况提取重叠的两个AVMutableVideoCompositionLayerInstruction。
  3. 为两个AVMutableVideoCompositionLayerInstruction添加一个渐变的过渡动画。
  4. 返回AVVideoComposition实例。

除了上面列出的最简单的渐隐过渡方式,还支持很多其它的过渡方式。

推入过渡效果:

            // 推入过渡效果let identityTransform = CGAffineTransform.identitylet videoWidth = videoComposition.renderSize.widthlet fromeTransform = CGAffineTransform(translationX: -videoWidth, y: 0)let toTransform = CGAffineTransform(translationX: videoWidth, y: 0)fromeLayerInstruction.setTransformRamp(fromStart: identityTransform, toEnd: fromeTransform, timeRange: videoCompositionInstruction.timeRange)toLayerInstruction.setTransformRamp(fromStart: toTransform, toEnd: identityTransform, timeRange: videoCompositionInstruction.timeRange)

擦除过渡效果:

            // 擦除过渡效果let width = videoComposition.renderSize.widthlet height = videoComposition.renderSize.heightlet startRect = CGRect(x: 0, y: 0, width: width, height: height)let endRect = CGRect(x: width, y: height, width: width, height: 0.0)fromeLayerInstruction.setCropRectangleRamp(fromStartCropRectangle: startRect, toEndCropRectangle: endRect, timeRange: videoCompositionInstruction.timeRange)

播放

创建PHTransitionCompositionBuilder实例,构建视频的可播放版本,进行播放。

 func player() {guard let delegate = self.delegate else { return }let compositionBuilder = PHTransitionCompositionBuilder(timeLine: timeLine)let composition = compositionBuilder.buildComposition()let playerItem = composition?.makePlayerItem()delegate.replaceCurrentItem(playerItem: playerItem)}

结语

代码中我们把工作的重点集中到了A-B轨道的方式交替添加视频资源,以及构建AVMutableVideoComposition并创建过渡效果上面。但事实上我们还需要注意到播放进度的显示,创建过渡后两个视频之间会有重叠部分,在呈现的时候需要减去重叠时间。

需要注意其它轨道的长度需要小于视频组合轨道的轨道长度。

另外最需要注意的是AVMutableVideoComposition的renderSize熟悉,一定要使用能够满足组合视频轨道中最大视频维度的尺寸值。

相关文章:

七.音视频编辑-创建视频过渡-应用

引言 在上一篇博客中,我们已经介绍了创建视频过渡的实现方案,步骤非常繁琐,在生成AVMutableVideoCompositionInstruction和AVMutableVideoCompositionLayerInstruction的计算也十分复杂,但其实还有一个创建视频组合的捷径。不过我…...

Android11 InputManagerService启动流程分析

InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…...

【计算机网络篇】数据链路层(8)共享式以太网的退避算法和信道利用率

文章目录 🛸共享式以太网的退避算法🥚截断二进制指数算法 🍔共享式以太网的信道利用率 🛸共享式以太网的退避算法 在使用CSMA/CD协议的共享总线以太网中,正在发送帧的站点一边发送帧一边检测碰撞,当检测到…...

wordpress主题 7B2 PRO主题5.4.2免授权直接安装

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 WordPress 资讯、资源、社交、商城、圈子、导航等多功能商用主题:B2 PRO 其设计风格专业且时尚,功能十分强大,包括多栏布局、自定义页面、强大的主…...

Dubbo基本使用

Dubbo基本使用 1.项目介绍2.开发步骤2.1 启动注册中心2.2 初始化项目2.3 添加 Maven 依赖2.3.1 父pom.xml2.3.1 consumer模块和provider模块pom.xml 2.4 定义服务接口2.5 定义服务端的实现2.6 配置服务端 Yaml 配置文件2.7 配置消费端 Yaml 配置文件2.8 基于 Spring 配置服务端…...

JS解密之新js加密实战(二)

前言 上次发了一篇关于新加密的,只解了前边两层,这中间家里各种事情因素影响,没有继续进一步研究,今天百忙之中抽空发布第二篇,关于其中的一小段加密片段,我认为分割成多个小片段是更容易被理解的。逻辑相…...

tsconfig 备忘清单

前言 ❝ Nealyang/blog0 使用 ts 已多年,但是貌似对于 tsconfig 总是记忆不清,每次都是 cv 历史项目,所以写了这篇备忘录,希望能帮助到大家。 本文总结整理自 Matt Pocock 的一篇文章3,加以个人理解,并做了…...

jmeter后置处理器提取到的参数因为换行符导致json解析错误

现象: {"message":"JSON parse error: Illegal unquoted character ((CTRL-CHAR, code 10)): has to be escaped using backslash to be included in string value; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Ill…...

栈与队列的实现

前言 本次博客将要实现一下栈和队列,好吧 他们两个既可以使用动态数组也可以使用链表来实现 本次会有详细的讲解 栈的实现 栈的基础知识 什么是栈呢? 栈的性质是后进先出 来画个图来理解 当然可不可以出一个进一个呢,当然可以了 比如…...

线性集合:ArrayList,LinkedList,Vector/Stack

共同点:都是线性集合 ArrayList ArrayList 底层是基于数组实现的,并且实现了动态扩容(当需要添加新元素时,如果 elementData 数组已满,则会自动扩容,新的容量将是原来的 1.5 倍),来…...

llama3 发布!大语言模型新选择 | 开源日报 No.251

meta-llama/llama Stars: 53.0k License: NOASSERTION llama 是用于 Llama 模型推理的代码。 提供了预训练和微调的 Llama 语言模型,参数范围从 7B 到 70B。可以通过下载脚本获取模型权重和 tokenizer。支持在本地快速运行推理,并提供不同规格的模型并…...

SpringBoot 具体是做什么的?

Spring Boot是一个用于构建独立的、生产级别的、基于Spring框架的应用程序的开源框架。它的目标是简化Spring应用程序的开发和部署过程,通过提供一种快速、便捷的方式来创建Spring应用程序,同时保持Spring的灵活性和强大特性。 1. 简化Spring应用程序开…...

Debian常用命令

Debian是一个开源的Unix-like操作系统,提供了大量的软件包供用户安装和使用。在Debian系统中,命令行界面(CLI)是用户与系统进行交互的重要工具。以下是Debian中一些常用的命令及其详细解释: 文件和目录操作命令&#x…...

常见的前端框架

常用的前端框架有以下几种: 模型 React:由Facebook开发的一款前端框架,采用虚拟DOM的概念,可高效地更新页面。Vue.js:一款轻量级的前端框架,易学易用,支持组件化开发和双向数据绑定。AngularJ…...

初学者如何选择ARM开发硬件?

在开始前我有一些资料,是我根据网友给的问题精心整理了一份「ARM的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!如果你没有ARM开发经验&#xff0…...

Mysql 多表查询,内外连接

内连接: 隐式内连接 使用sql语句直接进行多表查询 select 字段列表 from 表1 , 表2 where 条件 … ; 显式内连接 将‘,’改为 inner join 连接两个表的 on select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 … ; select emp.id, emp.name, …...

【C语言】函数

目录 一、函数的概念 二、库函数 2.1 ❥ 标准库 2.2 ❥ 库函数的使用方法 三、自定义函数 四、形参和实参 4.1 ❥ 实参(实际参数) 4.2 ❥ 形参(形式参数) 五、return语句 六、函数的调用 6.1 ❥ 传值调用 6.2 ❥ 传址调…...

【LeetCode】每日一题 2024_5_13 腐烂的橘子(经典多源 BFS)

文章目录 LeetCode?启动!!!题目:找出不同元素数目差数组题目描述代码与解题思路 每天进步一点点 LeetCode?启动!!! 好久没写每日一题题解了,今天重新起航 干…...

【Linux系统编程】第十七弹---进程理解

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、进程的基本概念 2、描述进程-PCB 2.1、什么是PCB 2.2、为什么要有PCB 3、task_ struct 3.1、启动进程 3.2、创建进程…...

【网络安全入门】你必须要有的学习工具(附安装包)零基础入门到进阶,看这一篇就够了!

工欲善其事必先利其器 在新入门网络安全的小伙伴而言。这些工具你必须要有所了解。本文我们简单说说这些网络安全工具吧! Web安全类 Web类工具主要是通过各种扫描工具,发现web站点存在的各种漏洞如sql注入、xss等。从而获取系统权限,常用的…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 ​二、实现思路 总体思路: 用户通过Gradio界面上…...

goreplay

1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具,可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长,测试它所需的工作量也会呈指数级增长。GoRepl…...

Canal环境搭建并实现和ES数据同步

作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap,但是由于很多朋友看不了解命令行格式,所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习,链接:https://wwhc.lanzoue.com/ifJY32ybh6vc…...