让任何AudioPlayer流式传输或播放音乐

时间:2015-12-16 15:14:52

标签: ios swift audio streaming avaudioplayer

这里有点不知所措。我在Xcode 7和Swift 2上,我正在尝试查找实际有效的流音频示例。

基本上我希望能够流式传输,但我也试过了:Swift Radio Streaming AVPlayer

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

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

2 个答案:

答案 0 :(得分:1)

不要忘记添加:App Transport Security Settings 从XCode 7.x和iOS 9.x开始

pList设置中需要:将“任意”添加为“是”

答案 1 :(得分:0)

我使用单例作为我的基本广播应用程序,因为需要在每个页面上访问和处理它。创建起来非常简单,让我们从声明单例和变量开始。我会一步一步地解释一切;

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定义,这意味着根据文档;

  

将此修饰符应用于可由Objective-C表示的类的任何成员。使用动态修饰符标记成员声明时,始终使用Objective-C运行时动态调度对该成员的访问。编译器永远不会内联或虚拟化对该成员的访问。

     

因为使用Objective-C运行时调度使用dynamic修饰符标记的声明,所以使用objc属性隐式标记它们。

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

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

AVAudioSession类中,此函数声明为那样;

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

如您所见,它们会抛出异常,因此您需要在错误处理中设置它们,例如> Swift 2.0的do/try概念。

让我们尝试播放声音;

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

final func stopRadio() {
        MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = nil
        radioPlayer?.pause()
    }

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

最重要的部分在这里;

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)
    }

如何从任何课程开始收音机?

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()
        }
    }

最后一步是从锁屏远程我的应用程序。停止,玩耍等 对于远程用户,我在RadioModel AppDelegate调用我的方法。在AppDelegate;

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

并在RadioModel;

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")
            }
        }
    }

请勿在您的应用中忘记目标&gt;能力&gt;切换背景模式并选择音频,AirPlay和画中画。然后检查您的info.plist并搜索&#34;需要背景模式&#34;。如果它不存在,请创建一个新的作为数组并设置项目&#34;应用播放音频或使用AirPlay播放音频/视频&#34;。现在你可以播放airPlay收音机/流。