Can't set MPMusicPlayerController queue with MPMusicPlayerMediaItemQueueDescriptor

前端 未结 3 1822
遇见更好的自我
遇见更好的自我 2021-01-28 06:12

This code results in silence:

let query = MPMediaQuery.songs()
let result = query.items
guard let items = result, items.count > 0 else {return}
let song = ite         


        
相关标签:
3条回答
  • 2021-01-28 06:22

    "It has been hosed since iOS 10.1 and it remains hosed in iOS 11.1." Seconded.

    I have been in the midst of a protracted fight just trying to a) get MPMusicPlayerController to simply work (finally managed that) and b) get it to play a song from an offset (still have not). And this is in an app that prior to iOS 10 worked 100% as expected.

    Right now, this is code that works for me (at least with regard to getting a song to play):

    let currentPlayer = MPMusicPlayerController.applicationQueuePlayer()
    currentPlayer.repeatMode = .none
    
    self.currentCollection = MPMediaItemCollection(items: [mediaItem])
    currentPlayer.beginGeneratingPlaybackNotifications()
    
    let query = MPMediaQuery.songs()
    let pred = MPMediaPropertyPredicate(value: mediaItem.persistentID, forProperty: MPMediaItemPropertyPersistentID)
    query.addFilterPredicate(pred)
    let desc = MPMusicPlayerMediaItemQueueDescriptor(query: query)
    currentPlayer.setQueueWith(desc)
    
    currentPlayer.prepareToPlay(completionHandler: {[unowned self] (error) in
        MPMusicPlayerController.applicationQueuePlayer().play()
    })
    

    It is a bit convoluted in that I had to use what I would consider the most complicated means based on available APIs. But several other methods cause the completion block to either never get called or get a completely unhelpful error. Right now I have 5 bugs reported to Apple with regard to MPMusicPlayerController so I am not exactly a happy developer at the moment. I will be opening one more related to this code as setStartTime & setEndTime does not appear to work any better than currentPlaybackTime for starting play at a specified offset.

    This code does dependably play synced and downloaded Apple Music items though.

    0 讨论(0)
  • 2021-01-28 06:39

    You've found a bug. It appears that the initializer MPMusicPlayerMediaItemQueueDescriptor(itemCollection:) is completely broken: it results in an unusable queue descriptor.

    The workaround, where possible, is to use the other initializer instead, namely MPMusicPlayerMediaItemQueueDescriptor(query:).

    For example, in this case, you could write (picking up in the last four lines of the original code):

    let coll = MPMediaItemCollection(items: [song])
    let predicate = MPMediaPropertyPredicate(
                    value: song.persistentID,
                    forProperty: MPMediaItemPropertyPersistentID)
    let query = MPMediaQuery(filterPredicates: [predicate])
    let q = MPMusicPlayerMediaItemQueueDescriptor(query: query)
    player.setQueue(with: q)
    player.play()
    

    Unfortunately, there are many circumstances where you can't form a single query that gets the MPMediaItemCollection you really wanted.

    In this particular example you could work around that, too, by setting the player's queue directly with an MPMediaItemCollection instead of a MPMusicPlayerMediaItemQueueDescriptor made from an MPMediaItemCollection.

    But alas, there are some commands (such as append(_:)) that require an MPMusicPlayerMediaItemQueueDescriptor, and for things like that, this entire API is basically hosed. It has been hosed since iOS 10.1 and it remains hosed in iOS 11.1.

    0 讨论(0)
  • 2021-01-28 06:44

    Swift 5

    I am a little late to the show, but this is what I came up with. (I was trimming songs for playback with descriptors).

    Make sure that you run every thing with Music Player on the Main Thread. If not you will run into weird bugs.

    Getting a song by persistentId:

    /// Retrieves a song in the MPMediaItem format using the persistentId passed.
    /// - Parameter persistentID: (UInt64) The songs persistentId.
    /// - Returns: (MPMediaItem?) Nil if not found other wise the MPMediaItem (song).
    private static func getSong(forId persistentID: UInt64) -> MPMediaItem? {
        let query = MPMediaQuery.songs()
        let predicate = MPMediaPropertyPredicate(value: persistentID, forProperty: MPMediaItemPropertyPersistentID)
        query.addFilterPredicate(predicate)
        return query.items?.first
    }
    

    Playing a song by persistentId:

    private var startTime: Double = 0
    private var endTime: Double = 0
    
    
    /// Plays the song with the identifier.
    /// - Note: Trims song to start and end time.
    /// - Parameter persistentId: (UInt64) The song persistent identifier.
    private func play(forId persistentId: UInt64) {
        DispatchQueue.main.async {
            if let song = Self.getSong(forId: persistentId) {
                let identifier = MPMediaItemPropertyPersistentID
                let predicate = MPMediaPropertyPredicate(value: persistentId, forProperty: identifier)
                let query = MPMediaQuery(filterPredicates: [predicate])
                let descriptor = MPMusicPlayerMediaItemQueueDescriptor(query: query)
                descriptor.setStartTime(self.startTime, for: song)
                descriptor.setEndTime(self.endTime, for: song)
                self.musicPlayer.setQueue(with: descriptor)
                self.musicPlayer.prepareToPlay()
                self.musicPlayer.repeatMode = .none
                self.musicPlayer.play()
            }
        }
    }
    

    Also I found that the way I initialize my music controller is important:

    private lazy var musicPlayer: MPMusicPlayerController = { MPMusicPlayerController.applicationQueuePlayer }()
    

    Please note: Some of the code isn't drag and drop. You would need to define things like start and end time variables.

    0 讨论(0)
提交回复
热议问题