iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >iOS基于AVFoundation制作用于剪辑视频项目
  • 472
分享到

iOS基于AVFoundation制作用于剪辑视频项目

2024-04-02 19:04:59 472人浏览 八月长安
摘要

目录项目效果图功能实现一、选取视频并播放二、按帧获取缩略图初始化视频轨道三、视频指定时间跳转四、播放器监听五、导出视频最近做了一个剪辑视频的小项目,踩了一些小坑,但还是有惊无险的实现

最近做了一个剪辑视频的小项目,踩了一些小坑,但还是有惊无险的实现了功能。

其实 Apple 官方也给了一个 UIVideoEditController 让我们来做视频的处理,但难以进行扩展或者自定义,所以咱们就用 Apple 给的一个框架 AVFoundation 来开发自定义的视频处理。

而且发现网上并没有相关的并且比较系统的资料,于是写下了本文,希望能对也在做视频处理方面的新手(比如我)能带来帮助。

项目效果图

项目的功能大概就是对视频轨道的撤销、分割、删除还有拖拽视频块来对视频扩展或者回退的功能

功能实现

一、选取视频并播放

通过 UIImagePickerController 选取视频并且跳转到自定义的编辑控制器

这一部分没什么好说的

示例:


 //选择视频
       @objc func selectVideo() {
           if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
               //初始化图片控制器
               let imagePicker = UIImagePickerController()
               //设置代理
               imagePicker.delegate = self
               //指定图片控制器类型
               imagePicker.sourceType = .photoLibrary
               //只显示视频类型的文件
               imagePicker.mediaTypes = [kUTTypeMovie as String]
               //弹出控制器,显示界面
               self.present(imagePicker, animated: true, completion: nil)
           }
           else {
               print("读取相册错误")
           }
       }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        //获取视频路径(选择后视频会自动复制到app临时文件夹下)
        guard let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL else {
            return
        }
        let pathString = videoURL.relativePath
        print("视频地址:\(pathString)")

        //图片控制器退出
        self.dismiss(animated: true, completion: {
            let editorVC = EditorVideoViewController.init(with: videoURL)

            editorVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen
            self.present(editorVC, animated: true) {

            }
        })
    }

二、按帧获取缩略图初始化视频轨道

CMTime

在讲实现方法之前先介绍一下 CMTime,CMTime 可以用于描述更精确的时间,比如我们想表达视频中的一个瞬间例如 1:01 大多数时候你可以用 NSTimeInterval t = 61.0 这是没有什么大问题的,但浮点数有个比较严重的问题就是无法精确的表达10的-6次方比如将一百万个0.0000001相加,运算结果可能会变成1.0000000000079181,在视频流传输的过程中伴随着大量的数据加减,这样就会造成误差,所以我们需要另一种表达时间的方式,那就是 CMTime

CMTime是一种C函数结构体,有4个成员。

typedef struct {

CMTimeValue value; // 当前的CMTimeValue 的值

CMTimeScale timescale; // 当前的CMTimeValue 的参考标准 (比如:1000)

CMTimeFlags flags;

CMTimeEpoch epoch;

} CMTime;

比如说平时我们所说的如果 timescale = 1000,那么 CMTimeValue = 1000 * 1 = 100

CMTimeScale timescale: 当前的CMTimeValue 的参考标准,它表示1秒的时间被分成了多少份。因为整个CMTime的精度是由它控制的所以它显的尤为重要。例如,当timescale为1的时候,CMTime不能表示1秒一下的时间和1秒内的增长。相同的,当timescale为1000的时候,每秒钟便被分成了1000份,CMTime的value便代表了多少毫秒。

实现方法

调用方法 generateCGImagesAsynchronously(forTimes requestedTimes: [NSValue], completionHandler handler: @escaping AVAssetImageGeneratorCompletionHandler)


 
    open func generateCGImagesAsynchronously(forTimes requestedTimes: [NSValue], completionHandler handler: @escaping AVAssetImageGeneratorCompletionHandler)

浏览官方的注释,可以看出需要传入两个参数 :

requestedTimes: [NSValue]:请求时间的数组(类型为 NSValue)每一个元素包含一个 CMTime,用于指定请求视频的时间。

completionHandler handler: @escaping AVAssetImageGeneratorCompletionHandler: 图像请求完成时将调用的块,由于方法是异步调用的,所以需要返回主线程更新 UI。

示例:


func splitVideoFileUrlFps(splitFileUrl:URL, fps:Float, splitCompleteClosure:@escaping (Bool, [UIImage]) -> Void) {
        var splitImages = [UIImage]()
		
		//初始化 Asset
        let optDict = NSDictionary(object: NSNumber(value: false), forKey: AVURLAssetPreferPreciseDurationAndTimingKey as NSCopying)
        let urlAsset = AVURLAsset(url: splitFileUrl, options: optDict as? [String : Any])

        let cmTime = urlAsset.duration
        let durationSeconds: Float64 = CMTimeGetSeconds(cmTime)

        var times = [NSValue]()
        let totalFrames: Float64 = durationSeconds * Float64(fps)
        var timeFrame: CMTime

		//定义 CMTime 即请求缩略图的时间间隔
        for i in 0...Int(totalFrames) {
            timeFrame = CMTimeMake(value: Int64(i), timescale: Int32(fps))
            let timeValue = NSValue(time: timeFrame)

            times.append(timeValue)
        }

        let imageGenerator = AVAssetImageGenerator(asset: urlAsset)
        imageGenerator.requestedTimeToleranceBefore = CMTime.zero
        imageGenerator.requestedTimeToleranceAfter = CMTime.zero

        let timesCount = times.count
		
		//调用获取缩略图的方法
        imageGenerator.generateCGImagesAsynchronously(forTimes: times) { (requestedTime, image, actualTime, result, error) in

        var isSuccess = false
        switch (result) {
        case AVAssetImageGenerator.Result.cancelled:
            print("cancelled------")

        case AVAssetImageGenerator.Result.failed:
            print("failed++++++")

        case AVAssetImageGenerator.Result.succeeded:

            let framImg = UIImage(cgImage: image!)

            splitImages.append(self.flipImage(image: framImg, orientaion: 1))
            if (Int(requestedTime.value) == (timesCount-1)) { //最后一帧时 回调赋值
                isSuccess = true
                splitCompleteClosure(isSuccess, splitImages)
                print("completed")
            }
        }
        }
    }

				//调用时利用回调更新 UI
self.splitVideoFileUrlFps(splitFileUrl: url, fps: 1) { [weak self](isSuccess, splitImgs) in
            if isSuccess {
                //由于方法是异步的,所以需要回主线程更新 UI
                DispatchQueue.main.async {
                
                    }
                print("图片总数目imGCount:\(String(describing: self?.imageArr.count))")
            }
        }

三、视频指定时间跳转


 
    open func seek(to time: CMTime, toleranceBefore: CMTime, toleranceAfter: CMTime)

三个传入的参数 time: CMTime, toleranceBefore: CMTime, tolearnceAfter: CMTime ,time 参数很好理解,即为想要跳转的时间。那么后面两个参数,按照官方的注释理解,简单来说为“误差的容忍度”,他将会在你拟定的这个区间内跳转,即为 [time-toleranceBefore, time+toleranceAfter] ,当然如果你传 kCMTimeZero(在我当前的版本这个参数被被改为了 CMTime.zero),即为精确搜索,但是这会导致额外的解码时间。

示例:


	let totalTime = self.avPlayer.currentItem?.duration
	let scale = self.avPlayer.currentItem?.duration.timescale
	
	//width:跳转到的视频轨长度 videoWidth:视频轨总长度
 	let process = width / videoWidth
 	
      //快进函数
 	self.avPlayer.seek(to: CMTimeMake(value: Int64(totalTime * process * scale!), timescale: scale!), toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)

四、播放器监听

通过播放器的监听我们可以改变控制轨道的移动,达到视频播放器和视频轨道的联动



    open func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

比较重要的一个参数是 interval: CMTime 这决定了代码回调的间隔时间,同时如果你在这个回调里改变视频轨道的 frame 那么这也会决定视频轨道移动的流畅度

示例:


//player的监听
        self.avPlayer.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 120), queue: DispatchQueue.main) { [weak self](time) in
                //与轨道的联动操作
        }

与快进方法冲突的问题

这个监听方法和第三点中的快进方法会造成一个问题:当你拖动视频轨道并且去快进的时候也会触发这个回调于是就造成了 拖动视频轨道 frame (改变 frame) -> 快进方法 -> 触发回调 -> 改变 frame 这一个死循环。那么就得添加判断条件来不去触发这个回调。

快进方法与播放器联动带来的问题

播放视频是异步的,并且快进方法解码视频需要时间,所以就导致了在双方联动的过程中带来的时间差。并且当你认为视频已经快进完成的时候,想要去改变视频轨道的位置,由于解码带来的时间,导致了在回调的时候会传入几个错误的时间,使得视频轨道来回晃动。所以当前项目的做法是,回调时需要判断将要改变的 frame 是否合法(是否过大、过小)

ps:如果关于这两个问题有更好的解决办法,欢迎一起讨论!

五、导出视频


 
    open func insertTimeRange(_ timeRange: CMTimeRange, of track: AVAssetTrack, at startTime: CMTime) throws

 传入的三个参数:

timeRange: CMTimeRange:指定要插入的视频的时间范围

track: AVAssetTrack:指定要插入的视频轨道。仅支持AVURLAssets和AVCompositions的AvassetTrack(从MacOS X 10.10和iOS 8.0开始的AVCompositions)。

starTime: CMTime: 指定合成视频插入的时间点。可以传递kCMTimeInvalid 参数,以指定视频应附加到前一个视频的末尾。

示例:


	let composition = AVMutableComposition()
            //合并视频、音频轨道
    let videoTrack = composition.addMutableTrack(
                        withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())
  	let audioTrack = composition.addMutableTrack(
                        withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())


 	let asset = AVAsset.init(url: self.url)

	var insertTime: CMTime = CMTime.zero

 	let timeScale = self.avPlayer.currentItem?.duration.timescale

			//循环每个片段的信息
	for clipsInfo in self.clipsInfoArr {
            
            //片段的总时间
		let clipsDuration = Double(Float(clipsInfo.width) / self.videoWidth) * self.totalTime
            
            //片段的开始时间
		let startDuration = -Float(clipsInfo.offset) / self.perSecondLength

      	do {
           	try videoTrack?.insertTimeRange(CMTimeRangeMake(start: CMTimeMake(value: Int64(startDuration * Float(timeScale!)), timescale: timeScale!), duration:CMTimeMake(value: Int64(clipsDuration * Double(timeScale!)), timescale: timeScale!)), of: asset.tracks(withMediaType: AVMediaType.video)[0], at: insertTime)
                } catch _ {}

		do {
         	try audioTrack?.insertTimeRange(CMTimeRangeMake(start: CMTimeMake(value: Int64(startDuration * Float(timeScale!)), timescale: timeScale!), duration:CMTimeMake(value: Int64(clipsDuration * Double(timeScale!)), timescale: timeScale!)), of: asset.tracks(withMediaType: AVMediaType.audio)[0], at: insertTime)
      		 } catch _ {}
   		insertTime = CMTimeAdd(insertTime, CMTimeMake(value: Int64(clipsDuration * Double(timeScale!)), timescale: timeScale!))
            }

  		videoTrack?.preferredTransfORM = CGAffineTransform(rotationAngle: CGFloat.pi / 2)

            //获取合并后的视频路径
  		let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask,true)[0]

  		let destinationPath = documentsPath + "/mergeVideo-\(arc4random()%1000).mov"
     	print("合并后的视频:\(destinationPath)")

end:通过这几个 API 再加上交互的逻辑就能实现完整的剪辑功能啦!如果文中有不足的地方,欢迎指出!

到此这篇关于iOS基于AVFoundation 制作用于剪辑视频项目的文章就介绍到这了,更多相关iOS AVFoundation 剪辑视频内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: iOS基于AVFoundation制作用于剪辑视频项目

本文链接: https://www.lsjlt.com/news/159417.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • iOS基于AVFoundation制作用于剪辑视频项目
    目录项目效果图功能实现一、选取视频并播放二、按帧获取缩略图初始化视频轨道三、视频指定时间跳转四、播放器监听五、导出视频最近做了一个剪辑视频的小项目,踩了一些小坑,但还是有惊无险的实现...
    99+
    2022-11-12
  • iOS基于AVFoundation 制作用于剪辑视频项目
    目录项目效果图功能实现一、选取视频并播放二、按帧获取缩略图初始化视频轨道三、视频指定时间跳转四、播放器监听五、导出视频最近做了一个剪辑视频的小项目,踩了一些小坑,但还是有惊无险的实现...
    99+
    2022-05-29
    iOS AVFoundation 剪辑 视频
  • iOS基于AVFoundation怎样制作用于剪辑视频项目
    iOS基于AVFoundation怎样制作用于剪辑视频项目,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。最近做了一个剪辑视频的小项目,踩了一些小坑,但还是有惊无...
    99+
    2023-06-21
  • 如何用手机制作属于自己的精美相册MV?学会这些,让你清爽剪辑视频
    随着现在科技的发展,各大厂商的手机基本没有千万像素级别的都不好意思宣传,像国产手机四巨头之一的OPPO手机,就是凭借强大的拍照功能而坐拥一大批粉丝。可是有了好看的照片,那么如何剪辑这些照片成了我们应该思考的问题。    ...
    99+
    2023-06-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作