iOS音视频播放-AVPlayer简单使用

按公司需求需要对音频文件进行后台播放,借此机会对音频播放做了个总结.主要针对 AVPlayer 进行详细说明.

iOS 各播放器比较

名称使用环境优点确点
System Sound ServicesAVFoundationC语言的底层写法,节省内存支持的格式有限,音量无法通过音量键控制,而且播放方式单一。
AVAudioPlayerAVFoundation抒写效率更高,基本上支持所有的音频格式,对播放的控制,如循环播放,声音大小,暂停等比较方便。对内存的消耗会多些。不支持流式,即无法播放在线音乐。
AVPlayerAVFoundation可以播放音视频,可播放在线音乐,使用灵活
MPMoviePlayerControllerMediaPlayer简单易用不可定制
AVPlayerViewControllerAVKit简单易用不可定制
IJKPlayerIJKMediaFramework定制度高,支持流媒体播放使用稍复杂

AVPlayer 使用

简介

AVPlayer 是iOS上常用的视频播放器组件,支持常见的音视频格式,支持流播放,可以播放在线音乐.
支持视频格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。
支持音频格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。

相关类

  • AVPlayer:播放器,控制播放器的播放,暂停,播放速度.
  • AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息.
  • AVPlayerItem:管理资源对象,提供播放数据源.
  • AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面.

简单使用

使用 url 创建 AVPlayer

let player = AVPlayer(url: URL(string: "http://www.xxxx.mp3"))

使用 AVPlayerItem 创建 AVPlayer

if let url = URL(string: "http://www.***.mp3") {
    let asset = AVAsset(url: url)
    guard asset.isPlayable else{
        // 检测文件是否可播放
        return
    }
    let playItem = AVPlayerItem(asset: asset)
    let player = AVPlayer(playerItem: playItem)
    player.play()
}

AVPlayer 控制播放

player.play() // 播放
player.pause() //暂停
player.rate = 1.0 // 播放速度 

通过通知监听播放状态变化

//播放完成
AVPlayerItemDidPlayToEndTimeNotification
//播放失败
AVPlayerItemFailedToPlayToEndTimeNotification
//异常中断
AVPlayerItemPlaybackStalledNotification

// eg: 播放结束通知
NotificationCenter.default.addObserver(self, selector: #selector(finish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)

监听播放进度

// 添加周期时间观察者 一秒执行一次 block
let timeObserver = player.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1), queue: DispatchQueue.main, using: { [weak self] (cmTime) in
    if let totalTime = self?.currentPlayItem?.duration {
        self?.delegate?.player(self!, currentTime: cmTime.seconds, totalTime: totalTime.seconds)
    }
})
// 不要忘记移除
player.removeTimeObserver(observer)

AVPlayerItem 创建

// 使用 AVAsset 创建
if let url = URL(string: "http://www.***.mp3") {
    let asset = AVAsset(url: url)
    guard asset.isPlayable else{
        // 检测文件是否可播放
        return
    }
    let playItem = AVPlayerItem(asset: asset)
}

// 使用 URL 创建
if let url = URL(string: "http://www.***.mp3") {
    let playItem = AVPlayerItem(url: url)
}

监听 AVPlayerItem 状态和缓存进度

// 监听 playerItem 状态变化
playItem.addObserver(self, forKeyPath: "status", options: .new, context: nil)
// 监听缓存时间
playItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: .new, context: nil)

// 移除监听
currentPlayItem?.removeObserver(self, forKeyPath: "status")
currentPlayItem?.removeObserver(self, forKeyPath: "loadedTimeRanges")
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    
    if object is AVPlayerItem {
        if keyPath == "status" {
            if let playerItem = object as? AVPlayerItem {
                switch playerItem.status {
                case .readyToPlay:
                    // 准备播放
                case .failed:
                    // 加载失败                    
                default:
                    // 未知状态
                }
            }
        }
        
        if keyPath == "loadedTimeRanges" {
            if let playerItem = object as? AVPlayerItem {
                if let timeRange = playerItem.loadedTimeRanges.first as? CMTimeRange {
                    let cache = timeRange.start.seconds + timeRange.duration.seconds // 缓存总时长
                }
            }
        }
    }

音频后台播放

开启所需后台模式

选中Targets–>Capabilities–>BackgroundModes–>ON,
并勾选Audio and AirPlay选项,如下图:
权限

或者 plist 文件添加如下字段:
plist

利用 AVAudioSession 申请后台播放权限

let session = AVAudioSession.sharedInstance()
do {
    try session.setActive(true)
    try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
    print(error)
}

在播放控制界面接受远程控制(Remote Control)

开启远程控制

// 声明接收Remote Control事件
UIApplication.shared.beginReceivingRemoteControlEvents()

设置 Remote Control 响应

// 响应 Remote Control事件
MPRemoteCommandCenter.shared().playCommand.addTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.addTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.addTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.addTarget(self, action: #selector(previous))

移除 Remote Control 响应

// 在关闭播放页面时记得移除
MPRemoteCommandCenter.shared().playCommand.removeTarget(self, action: #selector(play))
MPRemoteCommandCenter.shared().nextTrackCommand.removeTarget(self, action: #selector(next))
MPRemoteCommandCenter.shared().pauseCommand.removeTarget(self, action: #selector(pause))
MPRemoteCommandCenter.shared().previousTrackCommand.removeTarget(self, action: #selector(previous))
// 停止响应 Remote Control
UIApplication.shared.endReceivingRemoteControlEvents()

通过重写父类方法响应外部事件

  • 开启接受远程控制

  • 使当前页面成为第一响应者

  • 重写 remoteControlReceivedWithEvent 方法. UIEvent Type 取值:

    • UIEventSubtypeRemoteControlTogglePlayPause // 暂停
    • UIEventSubtypeRemoteControlPreviousTrack // 上一首
    • UIEventSubtypeRemoteControlNextTrack // 下一首
    • UIEventSubtypeRemoteControlPlay // 播放
    • UIEventSubtypeRemoteControlPause // 暂停
  • 关闭接受远程控制

锁屏页面显示播放信息(Now Playing Center)

使用 MPNowPlayingInfoCenter 设置锁屏页面音乐信息.

func setLockScreenPlayingInfo(_ info: YTTMediaInfo) {
    // Now Playing Center可以在锁屏界面展示音乐的信息,也达到增强用户体验的作用。
    // https://www.jianshu.com/p/458b67f84f27
    var infoDic: [String : Any] = [:]
    infoDic[MPMediaItemPropertyTitle] = info.title // 歌曲名
    infoDic[MPMediaItemPropertyArtist] = info.singer // 歌手
    if let img = info.image {
        infoDic[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(image: img) // 专辑图片
    }
    infoDic[MPMediaItemPropertyPlaybackDuration] = info.totalTime // 歌曲总时长
    infoDic[MPNowPlayingInfoPropertyElapsedPlaybackTime] = info.currentTime // 当前播放时间
    infoDic[MPNowPlayingInfoPropertyPlaybackRate] = 1.0 // 播放速度
    MPNowPlayingInfoCenter.default().nowPlayingInfo = infoDic
}

注意: MPNowPlayingInfoPropertyElapsedPlaybackTime 设置的并不是时时的,他是根据你设置的值进行计时的,如果想要在锁屏页面得到准确的时间,请及时刷新 MPNowPlayingInfoPropertyElapsedPlaybackTime 的值.当暂停时要暂停播放时间,只需将 MPNowPlayingInfoPropertyPlaybackRate 设置为 0.播放时设置回 1.

补充说明

iOS 对后台管理十分严格,任何 app 都有大约3分钟或者10分钟的后台执行时间.3分钟或者10分钟后, app 就会被强制挂起.使用 AVAudioSession 申请后台权限时,可以保证播放本地音乐能在后台长久播放,当播放网络音乐时就会出现不能播放情况,针对这情况使用了 beginBackgroundTask 设置后台任务 ID,通过这种方式我们大约可以获得额外的 10 分钟来执行后台任务.为了能无限后台播放网络音乐添加计时器,当即将挂起时再次申请后台任务 ID.

func applicationDidEnterBackground(_ application: UIApplication) {
    
    // 这样做,可以在按home键进入后台后 ,播放一段时间,几分钟吧。但是不能持续播放网络歌曲,若需要持续播放网络歌曲,还需要申请后台任务id
    bgTask = application.beginBackgroundTask(expirationHandler: nil)
    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}

@objc func timerAction() {
    timerCount = timerCount + 1
    if timerCount < 500 {
        return
    }
    timerCount = 0
    let newTask = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
    if bgTask != UIBackgroundTaskInvalid && newTask != UIBackgroundTaskInvalid {
        UIApplication.shared.endBackgroundTask(bgTask)
        bgTask = newTask
    }
    
}

遇到的坑

关于 rate 倍数播放

对于 AVPlayer 的暂停和播放是通过 rate 的值来判断的,但 rate 为 1 时.播放状态,但其为 0 时为暂停.每次调用 play() 方法时,rate 的值都将会置为 1,所以当调用 play 方法时,需要重新设置 rate 的值.

关于AVPlayerItem 切换

在 iOS9 以上系统测试时AVPlayerItem切换正常,但当在 iOS8 系统下,切换了AVPlayerItem 发现无法进行播放,针对这进行百度得到解决方式,iOS8 对于replaceCurrentItem方法支持不是很好,需要每次重新创建 AVPlayer.

其他

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值