swift之视频播放AVKIT、AVPlayerViewController、音频录制和播放

AVFoundation框架简介AVFoundation框架是iOS中专门处理音视频的框架,其中集成了音频播放以及处理和视频播放处理以及采集等功能(实现录制,编辑和播放音视频功能);以及配置音频会话更新设备音视频环境等功能。AVKit框架基于此框架实现的。

在 AVFoundation 框架中使用的基本数据结构,如时间相关的或描述媒体数据的数据结构都声明在 CoreMedia 框架中。



AVFoundation 框架包含视频相关的接口以及音频相关的接口,与音频相关的类有 AVAudioPlayer、AVAudioRecorder、AVAudioSession。

AVFoundation 框架中最基本的类是 AVAsset ,它是一个或者多个媒体数据的集合,描述的是整个集合的属性,如标题、时长、大小等,并且没有特定的数据格式。集合的每一个媒体数据都是统一的数据类型,称之为 track。简单的情况是一种数据是音频数据,一种是视频数据,而较复杂的情况是一种数据交织着音频和视频数据,并且 AVAsset 是可能有元数据的。

另外,需要明白的是在 AVFoundation 中,初始化了 asset 及 track 后,并不意味着资源已经可用,因为若资源本身并不携带自身信息时,那么系统需要自己计算相关信息,这个过程会阻塞线程,所以应该使用异步方式进行获取资源信息后的操作。

AVFoundation 提供了丰富的方法来管理视听资源的播放,为了支持这些方法,它将描述 asset 的状态与 asset 本身分离,这就使得在同一个时刻,以不同的方式播放同一个 asset 中的不同的媒体数据变得可能。对于 asset 的状态是由 player 管理的,而 asset 中的 track 的状态是由 player tracker 管理的。使用这两个状态管理对象,可以实现诸如设置 asset 中视频部分的大小、设置音频的混合参数及与视频的合成或者将 asset 中的某些媒体数据置为不可用。

另外,还可以通过 player 将输出定位到 Core Animation 层中,或通过播放队列设置 player 集合的播放顺序。

AVFoundation 提供了多种方法来创建 asset ,可以简单的重编码已经存在的 asset ,这个过程可以使用 export session 或者使用 asset reader 和 asset writer 。

若要生成视频的缩略图,可以使用 asset 初始化一个 AVAssetImageGenerator 实例对象,它会使用默认可用的视频 tracks 来生成图片。

AVFoundation 中可以使用 compositions 将多个媒体数据(video/audio tracks)合成为一个 asset ,这个过程中,可以添加或移除 tracks ,调整它们的顺序,或者设置音频的音量和变化坡度,视频容量等属性。这些媒体数据的集合保存在内存中,直到使用 export session 将它导出到本地文件中。另外,还可以使用 asset writer 创建 asset 。

使用 capture session 协调从设备(如相机、麦克风)输入的数据和输出目标(如视频文件)。可以为 session 设置多个输入和输出,即使它正在工作,还可以通过它停止数据的流动。另外,还可以使用 preview layer 将相机记录的影像实时展示给用户。

在 AVFoundation 中的回调处理并不保证回调任务在某个特定的线程或队列中执行,其遵循两个原则,UI 相关的操作在主线程中执行,其他回调需要为其指定调用的队列。

swift 中通过AVFoundation来定制拍摄窗口

swift AVFoundation PlayBack——swift学习(十)

****** AVPlayerItem 一个媒体资源管理对象,管理者视频的一些基本信息和状态,如 播放进度、缓存进度等 。 一个AVPlayerItem对应着一个视频资源。

******AVPlayer 视频操作对象,但是无法显示视频,需要把自己添加到一个AVPlayerLayer 上

******AVPlayerLayer 用来显示视频的。

 

=========AVPlayer实现无控制界面======

 

 //定义一个视频文件路径

        let filePath = Bundle.main.path(forResource: "test", ofType: "mov")

        let videoURL = URL(fileURLWithPath: filePath!)

        //定义一个视频播放器,通过本地文件路径初始化

        let player = AVPlayer(url: videoURL)

        //设置大小和位置(全屏)

        let playerLayer = AVPlayerLayer(player: player)//显示视频的图层

        playerLayer.frame = self.view.bounds

        //添加到界面上

        self.view.layer.addSublayer(playerLayer)

        //开始播放

        player.play()

==============AVPlayer实现无控制界面===带通知功能===

 

//        我们可以定义一个 AVPlayerItem,当视频播放完毕后它会发送一个 AVPlayerItemDidPlayToEndTime 通知。我们可以监听这个通知,在响应事件中

        

        //定义一个视频文件路径

        let filePath = Bundle.main.path(forResource: "test", ofType: "mov")

        let videoURL = URL(fileURLWithPath: filePath!)//也可以播放网络资源URL(string: "http://baidu.com/demo.mp4")!

        //定义一个playerItem,并监听相关的通知

        let playerItem = AVPlayerItem(url: videoURL)

        NotificationCenter.default.addObserver(self,

            selector: #selector(playerDidFinishPlaying),

            name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,

                                               object: playerItem)

        //定义一个视频播放器,通过playerItem径初始化

        let player = AVPlayer(playerItem: playerItem)

        //设置大小和位置(全屏)

        let playerLayer = AVPlayerLayer(player: player)

        playerLayer.frame = self.view.bounds

        //添加到界面上

        self.view.layer.addSublayer(playerLayer)

        //开始播放

        player.play()

    }

    

        //视频播放完毕的通知响应

        @objc func playerDidFinishPlaying() {

            print("播放完毕!")

        }

  

 

==============AVKIt实现自定义视屏界面===========

参考oc:https://www.cnblogs.com/mzds/p/3711867.html

 

 

import UIKit

import AVFoundation

import AVKit

class LYBPhoneczVC: LYBBaseVC {

    var  link:CADisplayLink?

    var  playerItem:AVPlayerItem?

    var player:AVPlayer?

    var slide:UISlider?

    override func viewDidLoad() {

        super.viewDidLoad()

 

       navigationItem.title="话费充值"

        let autodioBtn:UIButton=UIButton.init(frame: CGRect.init(x: 0, y: 10, width: WIDTH, height: 50))

        autodioBtn.setTitle("音频", for: UIControlState.normal)

        autodioBtn.setTitleColor(UIColor.red, for: UIControlState.normal)

        autodioBtn.addTarget(self, action:#selector(audioSet), for: UIControlEvents.touchUpInside)

        view.addSubview(autodioBtn)

    }

 

    @objc func audioSet(){

        //自定义视屏播放界面

        //定义一个视频文件路径

        let filePath = Bundle.main.path(forResource: "test", ofType: "mov")

        let videoURL = URL(fileURLWithPath: filePath!)//也可以播放网络资源URL(string: "http://baidu.com/demo.mp4")!

        //定义一个playerItem,并监听相关的通知

         self.playerItem = AVPlayerItem(url: videoURL)

        // kvo监听缓冲进度改变

        self.playerItem?.addObserver(self, forKeyPath: "loadedTimeRanges", options: NSKeyValueObservingOptions.new, context: nil)

        // kvo监听状态改变

        self.playerItem?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)

        

        //定义一个视频播放器,通过playerItem径初始化

        player = AVPlayer(playerItem: self.playerItem)

        //设置大小和位置(全屏)

        let playerLayer = AVPlayerLayer(player: player)

        playerLayer.frame = self.view.bounds

        // 设置显示模式

        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect

        playerLayer.contentsScale = UIScreen.main.scale

        //添加到界面上

        self.view.layer.addSublayer(playerLayer)

        

        //播放按钮

        let playBtn:UIButton=UIButton.init(frame: CGRect.init(x: 0, y: 300, width: 50, height: 50))

        playBtn.setTitle("播放", for: UIControlState.normal)

        playBtn.setTitleColor(UIColor.red, for: UIControlState.normal)

        playBtn.addTarget(self, action: #selector(palyBtn), for: UIControlEvents.touchUpInside)

view.addSubview(playBtn)

        //进度条

    slide=UISlider.init(frame: CGRect.init(x: 80, y: 300, width: WIDTH-100, height: 50))

        slide?.backgroundColor=UIColor.red//进度条总的背景

        slide?.tintColor=UIColor.black//进度的颜色

        slide?.thumbTintColor=UIColor.yellow//点击后滑块的颜色

   

        slide?.minimumValue=1//最小值

        slide?.maximumValue=10//最大值

        

        slide?.setThumbImage(UIImage.init(named: "guanyu"), for: UIControlState.normal)//进度条上的滑块图片,如果是UIImage.init(),刚开始不显示,点击滑动时才显示,滑块的大小有提供的图片大小来控制

      slide?.setValue(5, animated: true)//设置进度的值(滑块的位置)

        // 按下的时候

        slide?.addTarget(self, action: #selector(sliderTouchDown(slide:)), for: UIControlEvents.touchDown)

        // 弹起的时候

        slide?.addTarget(self, action: #selector(sliderTouchUpOut(slide:)), for: UIControlEvents.touchUpOutside)

        slide?.addTarget(self, action: #selector(sliderTouchUpOut(slide:)), for: UIControlEvents.touchUpInside)

        slide?.addTarget(self, action: #selector(sliderTouchUpOut(slide:)), for: UIControlEvents.touchCancel)

        view.addSubview(slide!)

       

//        CADisplayLink 的执行次数相当于屏幕的帧数,iPhone 不卡顿的时候是每秒60次。把它加入主loop中,默认Mode 。差不多每秒执行60次。

    self.link = CADisplayLink(target: self, selector: #selector(update))

        self.link?.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)

    }

//    //观察这响应-

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        guard let playerItem = object as? AVPlayerItem else { return }

                if keyPath == "loadedTimeRanges"{

                    // 缓冲进度

                   // 通过监听AVPlayerItem的"loadedTimeRanges",可以实时知道当前视频的进度缓冲

                   

 

                }else if keyPath == "status"{

                    // 监听状态改变(一共三种状态unknown、readyToPlay、failed)

                    if playerItem.status == AVPlayerItemStatus.readyToPlay{

                        // 只有在readyToPlay这个状态下才能播放

                        print("播放")

                    }else{

                        print("加载异常")

                    }

                }

    }

   

//吧时间分转换成秒

    func formatPlayTime(secounds:TimeInterval)->String{

        if secounds.isNaN{

            return "00:00"

        }

        let Min = Int(secounds / 60)

        let Sec = Int(secounds .truncatingRemainder(dividingBy: 60))

        return String(format: "%02d:%02d", Min, Sec)

    }

    @objc func update(){

        let currentTime = CMTimeGetSeconds((player?.currentTime())!)//当前的时间进度

        let totalTime   = TimeInterval((playerItem?.duration.value)!) / TimeInterval((playerItem?.duration.timescale)!)//(playerItem?.duration.value)!) / TimeInterval((playerItem?.duration.timescale)!是吧获取的时间转换成秒

        let timeStr = "\(formatPlayTime(secounds: currentTime))/\(formatPlayTime(secounds: totalTime))"

        print(timeStr)

        print(player?.currentTime())

    }

    

    @objc func palyBtn(){

        //开始播放

        player?.play()

    }

    

    @objc  func sliderTouchDown(slide:UISlider){

        //当视频状态为AVPlayerStatusReadyToPlay时才处理

        if player?.status == AVPlayerStatus.readyToPlay{

            let duration = slide.value * Float(CMTimeGetSeconds((self.player?.currentItem!.duration)!))//player?.currentItem!.duration是播放总时间长度.

            let seekTime = CMTimeMake(Int64(duration), 1)//第一个参数是时间,第二个参数是倍率scale

            //下面是让播放器找到seelktime这个时间对应的资源

            player?.seek(to: seekTime, completionHandler: { (b) in

                

            //这里可以开始播放、暂停等操作,根据需要

            })

        }

    }

    //开始播放或者暂停都可以

  @objc  func sliderTouchUpOut(slide:UISlider){

    

    if player?.status == AVPlayerStatus.readyToPlay{

        player?.play()

    }

    }

    //获取缓冲区的进度

    func avalableDurationWithplayerItem()->TimeInterval{

        let loadedTimeRanges = player?.currentItem?.loadedTimeRanges//这是一个数组

        let first = loadedTimeRanges?.first

        let timeRange = first?.timeRangeValue//这是获取缓冲区

        let startSeconds = CMTimeGetSeconds((timeRange?.start)!)

        let durationSecound = CMTimeGetSeconds((timeRange?.duration)!)

        let result = startSeconds + durationSecound

        return result

    }

}

 

   

=============AVPlayerViewController实现视频播放系统自带控制按钮======

 

//定义一个视频文件路径

        let filePath = Bundle.main.path(forResource: "test", ofType: "mov")

        let videoURL = URL(fileURLWithPath: filePath!)//也可以播放网络资源URL(string: "http://baidu.com/demo.mp4")!

        //定义一个视频播放器,通过本地文件路径初始化

        let player = AVPlayer(url: videoURL)

        let playerViewController = AVPlayerViewController()

        playerViewController.player = player

        self.present(playerViewController, animated: true) {

            playerViewController.player!.play()

        }

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {

            player.pause()//暂停---player.play()//播放

            

           

        }

 

********音频录制和播放*******

/**
 音频---录音和播放
 */

import UIKit
import AVFoundation
@available(iOS 10.0, *)
class LYBAudioVC: UIViewController {
    
    var recorder: AVAudioRecorder?
    var player: AVAudioPlayer?
    let file_path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first?.appending("/recordAudio.wav")
    
    
    //开始录音
    func beginRecord() {
        // 获得音频会话对像,该对像属于单例模式,也就是说不用开发者而自行实例化,这个类在各种音频环境中起着非常重要的作用
        let session = AVAudioSession.sharedInstance()
        //设置session类型 设置音频操作类别,标示该应用仅支持音频的播放
        do {
            try  session.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.default, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
            
        } catch let err{
            print("设置类型失败:\(err.localizedDescription)")
        }
        //设置session动作,  启动音频会话的管理,此时会阻断后台音乐的播放
        do {
            try session.setActive(true)
        } catch let err {
            print("初始化动作失败:\(err.localizedDescription)")
        }
        //录音设置,注意,后面需要转换成NSNumber,如果不转换,你会发现,无法录制音频文件,我猜测是因为底层还是用OC写的原因
        let recordSetting: [String: Any] = [AVSampleRateKey: NSNumber(value: 16000),//采样率
            AVFormatIDKey: NSNumber(value: kAudioFormatLinearPCM),//音频格式
            AVLinearPCMBitDepthKey: NSNumber(value: 16),//采样位数
            AVNumberOfChannelsKey: NSNumber(value: 1),//通道数
            AVEncoderAudioQualityKey: NSNumber(value: AVAudioQuality.min.rawValue)//录音质量
        ];
        //开始录音
        do {
            let url = URL(fileURLWithPath: file_path!)
            recorder = try AVAudioRecorder(url: url, settings: recordSetting)
            recorder!.prepareToRecord()
            recorder!.record()
            print("开始录音")
        } catch let err {
            print("录音失败:\(err.localizedDescription)")
        }
    }
    
    
    //结束录音
    func stopRecord() {
        if let recorder = self.recorder {
            if recorder.isRecording {
                print("正在录音...:\(file_path!)")
            }else {
                print("没有录音")
            }
            recorder.stop()
            self.recorder = nil
        }else {
            print("未初始化")
        }
    }
    
    
    //播放
    func play() {
        do {
            //     设置应用程序支持接受远程控制事件
            UIApplication.shared.beginReceivingRemoteControlEvents()
            player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: file_path!))
            print("歌曲长度:\(player!.duration)")
            player!.prepareToPlay()
            //                设置音频播放对象的音量大小/
            player!.volume = 1.0
            //                设置音频的播放次数,-1为无限循环
            player!.numberOfLoops = -1
            player!.play()
        } catch let err {
            print("播放失败:\(err.localizedDescription)")
        }
    }
    
    
    
    
}



 

你可能感兴趣的:(iOS,/swift)