关于ios:让任何AudioPlayer播放或播放音乐

Getting any AudioPlayer to stream or play music

在这里有点无所适从。我正在使用Xcode 7和Swift 2,并且正在尝试查找和实际运行的流音频的示例。

本质上,我希望能够进行流传输,但是我也尝试了这样做:Swift Radio Streaming AVPlayer

使用AVAudioPlayer。 github代码可以工作,但是将代码复制到具有相同音频文件的项目中会使应用程序崩溃:由于未捕获的异常\\'NSInvalidArgumentException \\',终止应用程序,原因:\\'-[SwiftBasicMVC_Git.AudioStreamProxy copyWithZone:]:无法识别的选择器发送到实例0x14fdea100 \\'<铅>

我还尝试了一些ACPlayer示例和AVQueuePlayer,但没有成功播放任何音乐。


我将Singleton用于我的基本广播应用程序,因为需要在每个页面上进行处理。创建起来是如此简单,让我们从声明单例和变量开始。我将逐步解释所有内容;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import AVKit
import AVFoundation

private var radioContext: UInt8 = 1

final class RadioModel: NSObject {
   
    dynamic var radioItem:AVPlayerItem? = nil
    dynamic var radioPlayer:AVPlayer? = nil
    static let sharedModel = RadioModel()
    private final let dataModel = BasicRadioApp.DataManager
   
    private override init() {
        super.init()
       
        do {
            _ = try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            _ = try AVAudioSession.sharedInstance().setActive(true)
            UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
        } catch {
            print("Error")
        }
    }

如果要将AirPlay模式用于播客或广播,则必须使用KVO(键值观察器)控制声音对象,并且它不支持Swift。因此,我们的AVPlayerItemAVPlayer对象由dynamic定义,这意味着根据文档;

Apply this modifier to any member of a class that can be represented by Objective-C. When you mark a member declaration with the dynamic modifier, access to that member is always dynamically dispatched using the Objective-C runtime. Access to that member is never inlined or devirtualized by the compiler.

Because declarations marked with the dynamic modifier are dispatched using the Objective-C runtime, theya€?re implicitly marked with the objc attribute.

我正在从我的另一个Singleton对象BasicRadioApp中的服务器获取数据。

我需要控制我的音频会话与其他人(例如Apple Watch)的交互,因此需要设置您的AVAudioSessionCategory。我也想在Apple Watch音乐应用程序的锁屏和Glance上远程播放音频。

AVAudioSession类中,该函数这样声明;

1
2
/* set session category */
    public func setCategory(category: String) throws

如您所见,它们会引发异常,因此您需要将它们设置为错误处理,例如> Swift 2.0的do/try概念。

让我们尝试播放声音;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final func playRadio() {
       
         do {
                try removeObserverFromRadioItem()
            } catch {
                print("Can not remove observer from AVPlayerItem object!")
            }
           
            radioItem = AVPlayerItem(URL: NSURL(string: dataModel.radioStreamUrl)!)
            radioPlayer = AVPlayer(playerItem: radioItem!)
            radioItem!.addObserver(self, forKeyPath:"status", options: [.New, .Initial, .Old], context: &radioContext)
       
        if MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo == nil {
           
            let albumArt = MPMediaItemArtwork(image: UIImage(named:"AlbumArt")!)
            let songInfo: Dictionary = [MPMediaItemPropertyTitle:"Now listening Hard Rock Bla Bla FM",
                MPMediaItemPropertyArtist:"My Radyo",
                MPMediaItemPropertyAlbumTitle:"105.5 FM",
                MPMediaItemPropertyArtwork: albumArt]
           
            MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = songInfo
        }
    }

在声明我的AVPlayerItem之前,如果它存在,我需要删除它上的KVO。接下来,您将看到removeObserverFromRadioItem()在做什么。我需要将我的AVPlayerItem注册到KVO对象,它可以播放或不播放。如我所说,我需要在锁屏上远程播放音频,因此我正在使用MPNowPlayingInfoCenter

1
2
3
4
5
6
7
8
final func stopRadio() {
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = nil
        radioPlayer?.pause()
    }

final func removeObserverFromRadioItem() throws {
        radioItem?.removeObserver(self, forKeyPath:"status", context: &radioContext)
    }

最重要的部分在这里;

1
2
3
4
5
6
7
8
9
10
11
12
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if object as? AVPlayerItem == radioPlayer?.currentItem && keyPath! =="status" {
            if self.radioPlayer?.currentItem!.status == .Failed {
                print("STREAM FAILED")
            } else if self.radioPlayer?.currentItem!.status == .ReadyToPlay {
                self.radioPlayer!.play()
                radioDelegate?.RadioDidStartPlay()
            }
            return
        }
        super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
    }

您如何从任何班级开始广播?

1
2
3
4
5
6
7
8
9
10
11
12
13
class RadioController: UIViewController {
   
    let sharedRadioPlayer = RadioModel.sharedModel
    ..

override func viewDidLoad() {
        super.viewDidLoad()
       
        // check if radio is NOT playing
        if sharedRadioPlayer.radioPlayer?.rate < 1 {
            sharedRadioPlayer.playRadio()
        }
    }

最后一步是我的应用程序从锁屏远程运行。停下来玩
对于远程,我从AppDelegateRadioModel中调用我的方法。在AppDelegate中;

1
2
3
override func remoteControlReceivedWithEvent(event: UIEvent?) {
        RadioModel.sharedModel.handleRemoteControlEvent(event!)
    }

RadioModel;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func handleRemoteControlEvent(responseEvent: UIEvent) {
        if responseEvent.type == UIEventType.RemoteControl {
            switch(responseEvent.subtype) {
            case UIEventSubtype.RemoteControlPlay:
                playRadio()
                break
            case UIEventSubtype.RemoteControlPause:
                stopRadio()
                break
            case UIEventSubtype.RemoteControlTogglePlayPause:
                if self.radioPlayer!.rate > 0 {
                    stopRadio()
                } else {
                    playRadio()
                }
                break
            default:
                print("Remote Error")
            }
        }
    }

别忘了在应用程序中的"目标">"功能">上打开"背景模式",然后选择"音频"," AirPlay"和"画中画"。然后检查您的info.plist并搜索"需要后台模式"。如果不存在,请创建一个新数组作为" Array",并设置" App使用AirPlay播放音频或流音频/视频"的项目。就这样,现在您可以播放airPlay广播/流了。


不要忘记添加:应用程序传输安全设置
从XCode 7.x和iOS 9.x

开始

在pList设置中需要:将任意添加为YES