音视频一、AVAudioPlayer播放音频

什么是AVAudioPlayer

AVAudioPlayer是AVFoundation框架里面提供的类,专门用于播放本地或者内存数据中的音频数据。它支持循环播放、多文件同时播放、定点播放等功能。 详情参考这里

基本使用

初始化

self.player = try AVAudioPlayer(data: data)
self.player = try AVAudioPlayer(contentsOf: localFileUrl)

播放/暂停

[player play];
[player pause];

播放完成回调

设置player的代理并实现AVAudioPlayerDelegate协议

self.player?.delegate = self

// MARK: AVAudioPlayerDelegate

extension AudioPlayer: AVAudioPlayerDelegate {

    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        progressTimer.invalidate()
    }

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        progressTimer.invalidate()
        playFinished?(flag)
    }
}

播放网络音频文件

由于AVAudioPlayer只可以通过文件或者data实例化,因此可以先下载缓存本地再播放。推荐使用NSURLCache进行文件缓存管理,因为NSURLCache提供了一套API进行缓存保存和读取,方便使用。

/// 播放器管理及数据缓存管理
class AudioPlayerManager: NSObject {

    static let shared = AudioPlayerManager()

    private override init() {
        super.init()
    }

    /// about cache
    private lazy var downloadQueue = DispatchQueue.global()

    private let allowedDiskSize = 100 * 1024 * 1024

    private let diskCachePath = "mp3Cache"

    private lazy var cache = URLCache(memoryCapacity: 0, diskCapacity: allowedDiskSize, diskPath: diskCachePath)

    private lazy var playerHash = [String: AudioPlayer]()

    func prepareDataForPlay(_ path: String, ready: ((AudioPlayer?)->Void)? = nil) {
        guard !IsNilOrEmptyString(path) else {
            ready?(nil)
            return
        }
        downloadQueue.async {
            self.downloadContent(fromUrlString: path, completionHandler: { (result) in

                switch result {

                case .success(let data):
                    // handle data
                    let audioPlayer = AudioPlayer(data: data)
                    DispatchQueue.main.async {
                        self.playerHash[path] = audioPlayer
                        if ready == nil {
                            audioPlayer.play()
                        }
                        ready?(audioPlayer)
                    }

                case .failure(let error):
                    debugPrint(error.localizedDescription)
                    DispatchQueue.main.async {
                        ready?(nil)
                    }
                }
            })

        }
    }


    func stopPlayers() {
        playerHash.values.forEach { (player) in
            player.stop()
        }
    }

}


// MARK: cache
extension AudioPlayerManager {

    typealias DownloadCompletionHandler = (Result<Data, Error>) -> Void

    private func createAndRetrieveURLSession() -> URLSession {
        let sessionConfiguration = URLSessionConfiguration.default
        sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
        sessionConfiguration.urlCache = cache
        return URLSession(configuration: sessionConfiguration)
    }

    private func downloadContent(fromUrlString: String, completionHandler: @escaping DownloadCompletionHandler) {

        guard let downloadUrl = URL(string: fromUrlString) else { return }
        let urlRequest = URLRequest(url: downloadUrl)
        // First try to fetching cached data if exist
        if let cachedData = self.cache.cachedResponse(for: urlRequest) {
            print("Cached data in bytes:", cachedData.data)
            completionHandler(.success(cachedData.data))
        } else {
            // No cached data, download content than cache the data
            createAndRetrieveURLSession().dataTask(with: urlRequest) { (data, response, error) in

                if let error = error {
                    completionHandler(.failure(error))

                } else {

                    let cachedData = CachedURLResponse(response: response!, data: data!)
                    self.cache.storeCachedResponse(cachedData, for: urlRequest)

                    completionHandler(.success(data!))
                }

                }.resume()
        }
    }
}