在之前的文章中,我介绍了如何使用 AVPlayer 制作一个简单的音乐播放器(点击查看1点击查看2)。虽然这个播放器也可以播放网络音频,但其实际上是将音频文件下载到本地后再播放的。
本文演示如何使用第三方的 StreamingKit 库,来实现网络流音频的播放。

一、StreamingKit介绍和配置

1,基本介绍

(1)StreamingKit 是一个适用于 iOS 和 Mac OSX 的音频播放流媒体库。StreamingKit 提供了一个简洁的面向对象 API,用于在 CoreAudio 框架下进行音频的解压和播放(采用硬件或软件编解码器)处理。
(2)StreamingKit 的主要机制是对从播放器输入的数据源进行解耦,从而使高级定制的数据源可以进行诸如基于流媒体的渐进式下载、编码解码、自动恢复、动态缓冲之类的处理。StreamingKit 是唯一支持不同格式音频文件无缝播放的音频播放流媒体库。

2,主要特点

  • 免费开源
  • 简洁的 API
  • 可读性很强的源代码
  • 精心使用多线程提供了一个快速响应的 API,既能防止线程阻塞,又能保证缓冲流畅
  • 缓冲并无缝播放所有不同格式的音频文件
  • 容易实现的音频数据源(支持本地、HTTP、AutoRecovering HTTP 作为数据源)
  • 容易 kuo 扩展数据源以支持自动缓冲、编码等
  • 低耗电和低 CPU 使用率(CPU 使用率 0%,流式处理时使用率为 1%)
  • 优化线性数据源,仅随机访问数据源需要搜索
  • StreamingKit0.2.0 使用 AudioUnit API 而不是速度较慢的音频队列 API,允许对原始 PCM 数据进行实时截取以获得并行测量、EQ 等特征
  • 电能计量
  • 内置的均衡器(iOS5.0 及以上版本、OSX10.9 及以上版本)支持音频播放的同时动态改变、启用、禁用均衡器
  • 提供了 iOS 和 Mac OSX 应用实例

3,安装配置

(1)将源码包下载下来后,将其中的 StreamingKit/StreamingKit 文件夹复制到项目中来。
(2)创建桥接头文件,内容如下:
1
#import "STKAudioPlayer.h"

二、制作一个网络音频播放器

1,效果图

(1)程序运行后自动开始播放音乐(整个队列一个有 3 首歌曲,默认先播放第一首)
(2)点击“上一曲”“下一曲”按钮可以切换当前播放歌曲。
(3)歌曲播放过程中进度条会随之变化,进度条右侧会显示出当前歌曲播放时间。
(4)进度条可以拖动,拖动结束后自动播放该时间点的音乐。
(5)点击“暂停”按钮可以交替切换播放器暂停、继续状态。
(6)点击“结束”按钮,结束整个播放器的音乐播放。

2,实现步骤

(1)在 info.plist 中添加如下配置以支持 http 传输。
1
2
3
4
5
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

(2)为了让播放器能在后台持续播放,我们需要将 Targets -> Capabilities -> BackgroundModes 设为 ON,同时勾选“Audio, AirPlay, and Picture in Picture”。

同时还要在 AppDelegate.swift 中注册后台播放。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import UIKit
import AVFoundation
 
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
    var window: UIWindow?
 
 
    func application(_ application: UIApplication, didFinishLaunchingWithOptions
        launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
         
        // 注册后台播放
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setActive(true)
            try session.setCategory(AVAudioSessionCategoryPlayback)
        } catch {
            print(error)
        }
         
        return true
    }
 
    func applicationWillResignActive(_ application: UIApplication) {
    }
 
    func applicationDidEnterBackground(_ application: UIApplication) {
    }
 
    func applicationWillEnterForeground(_ application: UIApplication) {
    }
 
    func applicationDidBecomeActive(_ application: UIApplication) {
    }
 
    func applicationWillTerminate(_ application: UIApplication) {
    }
}

(3)主视图代码(ViewController.swift)

import UIKit
 
class ViewController: UIViewController {
     
    //显示歌曲标题
    @IBOutlet weak var titleLabel: UILabel!
     
    //暂停按钮
    @IBOutlet weak var pauseBtn: UIButton!
     
    //可拖动的进度条
    @IBOutlet weak var playbackSlider: UISlider!
     
    //当前播放时间标签
    @IBOutlet weak var playTime: UILabel!
     
    //更新进度条定时器
    var timer:Timer!
     
    //音频播放器
    var audioPlayer: STKAudioPlayer!
     
    //播放列表
    var queue = [Music(name: "歌曲1",
                       url: URL(string: "http://mxd.766.com/sdo/music/data/3/m10.mp3")!),
                 Music(name: "歌曲2",
                       url: URL(string: "http://mxd.766.com/sdo/music/data/3/m12.mp3")!),
                 Music(name: "歌曲3",
                       url: URL(string: "http://mxd.766.com/sdo/music/data/3/m13.mp3")!)]
     
    //当前播放音乐索引
    var currentIndex:Int = -1
     
    //是否循环播放
    var loop:Bool = false
     
    //当前播放状态
    var state:STKAudioPlayerState = []
 
    override func viewDidLoad() {
        super.viewDidLoad()
         
        //设置进度条相关属性
        playbackSlider!.minimumValue = 0
        playbackSlider!.isContinuous = false
         
        //重置播放器
        resetAudioPlayer()
         
        //开始播放歌曲列表
        playWithQueue(queue: queue)
         
        //设置一个定时器,每三秒钟滚动一次
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self,
                    selector: #selector(tick), userInfo: nil, repeats: true)
    }
     
    //重置播放器
    func resetAudioPlayer() {
        var options = STKAudioPlayerOptions()
        options.flushQueueOnSeek = true
        options.enableVolumeMixer = true
        audioPlayer = STKAudioPlayer(options: options)
         
        audioPlayer.meteringEnabled = true
        audioPlayer.volume = 1
        audioPlayer.delegate = self
    }
     
    //开始播放歌曲列表(默认从第一首歌曲开始播放)
    func playWithQueue(queue: [Music], index: Int = 0) {
        guard index >= 0 && index < queue.count else {
            return
        }
        self.queue = queue
        audioPlayer.clearQueue()
        let url = queue[index].url
        audioPlayer.play(url)
         
        for i in 1 ..< queue.count {
            audioPlayer.queue(queue[Int((index + i) % queue.count)].url)
        }
        currentIndex = index
        loop = false
    }
     
    //停止播放
    func stop() {
        audioPlayer.stop()
        queue = []
        currentIndex = -1
    }
     
    //单独播放某个歌曲
    func play(file: Music) {
        audioPlayer.play(file.url)
    }
     
    //下一曲
    func next() {
        guard queue.count > 0 else {
            return
        }
        currentIndex = (currentIndex + 1) % queue.count
        playWithQueue(queue: queue, index: currentIndex)
    }
     
    //上一曲
    func prev() {
        currentIndex = max(0, currentIndex - 1)
        playWithQueue(queue: queue, index: currentIndex)
    }
     
    //下一曲按钮点击
    @IBAction func nextBtnTapped(_ sender: Any) {
        next()
    }
     
    //上一曲按钮点击
    @IBAction func prevBtnTapped(_ sender: Any) {
        prev()
    }
     
    //暂停继续按钮点击
    @IBAction func pauseBtnTapped(_ sender: Any) {
        //在暂停和继续两个状态间切换
        if self.state == .paused {
            audioPlayer.resume()
        }else{
            audioPlayer.pause()
        }
    }
 
    //结束按钮点击
    @IBAction func stopBtnTapped(_ sender: Any) {
        stop()
    }
     
    //定时器响应,更新进度条和时间
    func tick() {
        if state == .playing {
            //更新进度条进度值
            self.playbackSlider!.value = Float(audioPlayer.progress)
             
            //一个小算法,来实现00:00这种格式的播放时间
            let all:Int=Int(audioPlayer.progress)
            let m:Int=all % 60
            let f:Int=Int(all/60)
            var time:String=""
            if f<10{
                time="0\(f):"
            }else {
                time="\(f)"
            }
            if m<10{
                time+="0\(m)"
            }else {
                time+="\(m)"
            }
            //更新播放时间
            self.playTime!.text=time
        }
    }
     
    //拖动进度条改变值时触发
    @IBAction func playbackSliderValueChanged(_ sender: Any) {
        //播放器定位到对应的位置
        audioPlayer.seek(toTime: Double(playbackSlider.value))
        //如果当前时暂停状态,则继续播放
        if state == .paused
        {
            audioPlayer.resume()
        }
    }
     
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
 
//Audio Player相关代理方法
extension ViewController: STKAudioPlayerDelegate {
 
    //开始播放歌曲
    func audioPlayer(_ audioPlayer: STKAudioPlayer,
                     didStartPlayingQueueItemId queueItemId: NSObject) {
        if let index = (queue.index { $0.url == queueItemId as! URL }) {
            currentIndex = index
        }
    }
     
    //缓冲完毕
    func audioPlayer(_ audioPlayer: STKAudioPlayer,
        didFinishBufferingSourceWithQueueItemId queueItemId: NSObject) {
        updateNowPlayingInfoCenter()
    }
     
    //播放状态变化
    func audioPlayer(_ audioPlayer: STKAudioPlayer,
                     stateChanged state: STKAudioPlayerState,
                     previousState: STKAudioPlayerState) {
        self.state = state
        if state != .stopped && state != .error && state != .disposed {
        }
        updateNowPlayingInfoCenter()
    }
     
    //播放结束
    func audioPlayer(_ audioPlayer: STKAudioPlayer,
                     didFinishPlayingQueueItemId queueItemId: NSObject,
                     with stopReason: STKAudioPlayerStopReason,
                     andProgress progress: Double, andDuration duration: Double) {
        if let index = (queue.index {
            $0.url == audioPlayer.currentlyPlayingQueueItemId() as! URL
        }) {
            currentIndex = index
        }
         
        //自动播放下一曲
        if stopReason == .eof {
            next()
        } else if stopReason == .error {
            stop()
            resetAudioPlayer()
        }
    }
     
    //发生错误
    func audioPlayer(_ audioPlayer: STKAudioPlayer,
                     unexpectedError errorCode: STKAudioPlayerErrorCode) {
        print("Error when playing music \(errorCode)")
        resetAudioPlayer()
        playWithQueue(queue: queue, index: currentIndex)
    }
     
    //更新当前播放信息
    func updateNowPlayingInfoCenter() {
        if currentIndex >= 0 {
            let music = queue[currentIndex]
            //更新标题
            titleLabel.text = "当前播放:\(music.name)"
             
            //更新暂停按钮名字
            let pauseBtnTitle = self.state == .playing ? "暂停" : "继续"
            pauseBtn.setTitle(pauseBtnTitle, for: .normal)
             
            //设置进度条相关属性
            playbackSlider!.maximumValue = Float(audioPlayer.duration)
        }else{
            //停止播放
            titleLabel.text = "播放停止!"
            //更新进度条和时间标签
            playbackSlider.value = 0
            playTime.text = "--:--"
        }
    }
}
 
//歌曲类
class Music {
    var name:String
    var url:URL
     
    //类构造函数
    init(name:String, url:URL){
        self.name = name
        self.url = url
    }
}
 

源码下载:hangge_1667.zip

原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_1667.html

Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)的更多相关文章

  1. Hide-Music-Player 一个完整的音乐播放器《IT蓝豹》

    Hide-Music-Player 一个完整的音乐播放器 Hide-Music-Player 一个完整的音乐播放器,本例子主要包括几个点 (1)摇一摇进入播放器 (2)下拉展开新视图(扫描音乐) (3 ...

  2. vue——一个页面实现音乐播放器

    请忽略下面这段文字年关将至,时间好歹又多出了些许.却不敢过度消遣.岁月未曾饶过我,我亦不想饶过岁月.且将它塞得膨胀,让这一年看似加更充实.不曾料想我一个爱些风花雪月.研墨行歌之人,却做起了碼农这一行当 ...

  3. 如何用vue打造一个移动端音乐播放器

    写在前面 没错,这就是慕课网上的那个vue音乐播放器,后台是某音乐播放器的线上接口扒取,虽然这类项目写的人很多,但不得不说这还是个少有的适合vue提升的好项目,做这个项目除了想写一个比较大并且功能复杂 ...

  4. 用pyqt5做一个简易的音乐播放器

    需求 要求可以读取音频文档,有播放和暂停的功能 附上代码(1)UI界面 # -*- coding: utf-8 -*- # Form implementation generated from rea ...

  5. JS写一个漂亮的音乐播放器

    先放上效果图: 正如图中所展示的播放器那样,我们用HTML+CSS+JS将这个效果实现出来. HTML页面布局 <div class="music"> <div ...

  6. 使用余弦定理制作磁盘形状h5音乐播放器

    目录 [1]功能实现 [2]效果展示 [3]原理说明 旋转原理 余弦定理 [4]代码实现 HTML CSS JS [5]源码查看 功能实现 [1]歌曲播放进度转换成视觉的旋转角度 [2]点击磁盘任意位 ...

  7. Ubuntu 下一个可用的音乐播放器

    参考:http://www.pairsdoll.com/install-audacious-music-palyer-in-ubuntu.html/ 方法:打开terminal,sudo apt-ge ...

  8. Simple2D-16(音乐播放器)ImGui 库介绍

    什么是 ImGui IMGUI (Immediate Mode Graphical User interface),下载地址. ImGui 是一种比较新颖的 GUI 实现模式,适用于显示区域实时刷新的 ...

  9. HTML+纯JS制作音乐播放器

    该篇文章会教你通过JavaScript制作一个简单的音乐播放器.包括播放.暂停.上一曲和下一曲. 阅读本文章你需要对HTML.CSS和Javascript有基本的了解. 话不多说,先上图. emmm. ...

随机推荐

  1. VS2015启动显示ID为XXXX的进程当前未运行

    解决办法:在启动项目根目录下用文本编辑器打开Web项目下的{X}.csproj文件,然后查找 <WebProjectProperties>,将这一对标签之间的内容全部删除,然后再打开项目就 ...

  2. Vim常用又容易忘的命令

    一篇讲的不错的教程 :noh 取消搜索高亮 x 删当前光标所在的一个字符. :wq 存盘 + 退出 dd 删除当前行,并把删除的行存到剪贴板里 p 粘贴剪贴板 a → 在光标后插入 /pattern ...

  3. javaweb 之 代理模式

    一.动态代理 1.1.代理模式 什么是代理模式及其作用 Proxy Pattern(即:代理模式),23种常用的面向对象软件的设计模式之一 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问 ...

  4. 关于电脑安装新硬盘,出现无法是识别设备,03F0问题解答。

    问题说明:在添加新的硬盘,切确定硬盘没有坏的情况下,无法识别出新的硬盘. 解决方案: 1.检查bios系统里的安全模式,是否处于开启中.因为在windows 8.1以上的版本中,不开启的情况下只能读取 ...

  5. STL编程:C++的忠告!

    Copy别人的,有少量修改,可以做为一下参考! C++之父Bjarne Stroustrup 写的 The C++ Programming Language (Special Edition) 中各章 ...

  6. Kafka学习笔记(7)----Kafka使用Cosumer接收消息

    1. 什么是KafkaConsumer? 应用程序使用KafkaConsul'le 「向Kafka 订阅主题,并从订阅的主题上接收消息.Kafka的消息读取不同于从其他消息系统读取数据,它涉及了一些独 ...

  7. CDR快速制作苹果手机照片小图标

    本篇教程用CorelDRAW快速制作苹果手机照片小图标,在实现的过程中主要使用了旋转复制的方法,加之一些常用工具的用法处理,最后加上透明效果下的合并模式就好了,现在跟小编一起来看看详细的操作吧! 1. ...

  8. POI操作Excel的三种Workbook的发展和区别

    POI的一些使用方法: 创建流程:(上级为下级的载体) 1.创建Workbook(工作薄): 2.创建Sheet(表单,可以创建多个): 3.创建Row(行): 4.创建Cell(单元格) 接下来分别 ...

  9. BZOJ 5508: [Tjoi2019]甲苯先生的字符串 矩阵乘法_思维

    求 $f[i][j]=∑f[i−1][k]$,$'a'<=k<='z'$ . 用矩阵乘法转移一波即可. 竟然独自想出来了QAQ Code: #include <bits/stdc++ ...

  10. 【BZOJ2806】【CTSC2012】Cheat - 广义后缀自动机+单调队列优化DP

    题意: Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文 Output N行 ...