AudioPlayer icon indicating copy to clipboard operation
AudioPlayer copied to clipboard

Preload next song in queue

Open smkhalsa opened this issue 9 years ago • 10 comments

It would be great if the next song in the queue was automatically buffered for gapless playback between tracks.

smkhalsa avatar Feb 22 '16 23:02 smkhalsa

Hi @smkhalsa Someone already opened an issue for this feature. I'm going to let it open for now but unfortunately that's definitely not a priority to me and honestly it feels like a lot of complexity to add. I'll think about it :)

delannoyk avatar Mar 10 '16 18:03 delannoyk

I agree with @delannoyk . It would have been easier if we used AVAudioItem instead of custom AudioItem. I will try it out and see if we can make an easy change. I won't consider performance in my solution though :)

han9over avatar Mar 18 '16 10:03 han9over

@smkhalsa and @delannoyk .. What do you guys think of below implementation? I have done these changes in my project files only

Add below line in AudioItem.swift

public dynamic var data: AVAsset?

In AudioPlayer.swift, replace below line

player = AVPlayer(URL: URLInfo.URL)

with

if currentItem.data != nil {
    player = AVPlayer(playerItem: AVPlayerItem(asset: currentItem.data!))

    if let currentItemDuration = currentItemDuration where currentItemDuration > 0 {
         updateNowPlayingInfoCenter()
         delegate?.audioPlayer(self, didFindDuration: currentItemDuration, forItem: currentItem)
    }
}
else {
    player = AVPlayer(URL: URLInfo.URL)
}

let nextIndex = currentItemIndexInQueue! + 1
if (enqueuedItems?.count)! > nextIndex {
       let temp = enqueuedItems![currentItemIndexInQueue!+1].item
       if temp.data == nil {
             preload(temp, index: nextIndex)
       }
}
else if enqueuedItems![0].item.data == nil {
       preload(enqueuedItems![0].item,index: 0)
}

Then add below function to the same file.. add anywhere inside player class. Haven't done rigorous testing, but this might work temporarily with minimal changes. As I mentioned above, haven't considered any performance or coding standards. I just wanted to think through of implementation :)

func preload(song: AudioItem, index: Int) {
        let asset = AVURLAsset(URL: song.highestQualityURL.URL)
        let keys = ["playable","tracks","duration"]

        asset.loadValuesAsynchronouslyForKeys(keys) {
            for key in keys {
                let status = asset.statusOfValueForKey(key, error: nil)
                if status == AVKeyValueStatus.Failed {
                    return
                }
            }

            dispatch_async(dispatch_get_main_queue()) {
                song.data = asset
                self.enqueuedItems?[index].item = song
            }
        }
    }

han9over avatar Mar 19 '16 23:03 han9over

To be short the logic is, when you start playing an item, if there is another item in queue, then I will initialize the asset and store that along with AudioItem. When that item plays and if asset data exists for it, then I will use that instead of URL to send to player. For testing I have added high quality urls only so that's why you will see that in AVURLAsset creation.

han9over avatar Mar 19 '16 23:03 han9over

Hi @han9over

Thank you for the leads in this! Did you get a chance to test it? I haven't checked yet but is there an opportunity to cancel a preloading-request? I'm wondering how the queue would be handled in case it changed suddenly.

In anyway, I'm currently working on some refactoring so I'll get a closer look a bit later.

delannoyk avatar Mar 22 '16 18:03 delannoyk

Hey @delannoyk

I have tested it with my setup and it works well. Currently, i haven't looked into canceling preloading. I'm not preloading the whole queue, it just preloads the next song only. So, if the next song is removed, the preloaded data gets removed along with it.

I know there is lot of work to be done on this, my initiative was to provide some thoughts or ideas :)

han9over avatar Mar 22 '16 18:03 han9over

by the way, not sure which city you are in, hope you are not near by or affected by those attacks? Be safe and take care.

han9over avatar Mar 22 '16 18:03 han9over

Ok, I'll look take a closer look in the next few days then. Thanks for you leads.

Yep those events suck. I live about an hour drive away from Brussels so I'm safe, thanks!

delannoyk avatar Mar 22 '16 18:03 delannoyk

good to hear.. thanks :)

han9over avatar Mar 22 '16 18:03 han9over

Any updates on this? Thinking it might be time for me to move my project away from HysteriaPlayer, but gapless is important to me.

JacobSyndeo avatar Aug 26 '18 22:08 JacobSyndeo