问题
I am playing a Tv-show that has been sliced to different chapters on my project using an AVQueuePlayer. I also want to offer the possibility to skip to the previous/next chapter or to select a different chapter on the fly, while the AVQueuePlayer is already playing.
Skipping to next Item is no problem with the advanceToNextItem
provided by AVQueuePlayer, but there is nothing alike for skipping back or playing a certainitem from the queue.
So I am not quite sure what would be the best approach here:
- Using an AVPlayer instead of AVQueuePlayer, invoke
replaceCurrentItemWithPlayerItem:
atactionAtItemEnd
to play the nextItem and just use 'replaceCurrentItemWithPlayerItem' to let the User select a certain Chapter
or
- reorganise the queue or the current player by using 'insertItem:afterItem:' and 'removeAllItems'
Additional information: I store the Path to the different videos in the order they should appear in a NSArray The user is supposed to jump to certain chapters by pressing buttons that represent the chapter. The Buttons have tags, that are also the indexes of the corresponding videos in the array.
Hope I could make myself clear? Anyone having any experience with this situation? If anyone knows where to buy a good IOS VideoPlayerFramework which provides the functionality, I would also appreciate the link.
回答1:
If you want your program can play previous item and play the selected item from your playeritems(NSArray),you can do this.
- (void)playAtIndex:(NSInteger)index
{
[player removeAllItems];
for (int i = index; i <playerItems.count; i ++) {
AVPlayerItem* obj = [playerItems objectAtIndex:i];
if ([player canInsertItem:obj afterItem:nil]) {
[obj seekToTime:kCMTimeZero];
[player insertItem:obj afterItem:nil];
}
}
}
edit: playerItems is the NSMutableArray(NSArray) where you store your AVPlayerItems.
回答2:
The first answer removes all items from AVQueuePlayer, and repopulates queue starting with iteration passed as index arg. This would start the newly populated queue with previous item(assuming you passed correct index) as well the rest of the items in existing playerItems array from that point forward, BUT it does not allow for multiple reverses, e.g. you are on track 10 and want to go back and replay track 9, then replay track 5, with above you cannot accomplish. But here you can...
-(IBAction) prevSongTapped: (id) sender
{
if (globalSongCount>1){ //keep running tally of items already played
[self resetAVQueue]; //removes all items from AVQueuePlayer
for (int i = 1; i < globalSongCount-1; i++){
[audioQueuePlayer advanceToNextItem];
}
globalSongCount--;
[audioQueuePlayer play];
}
}
回答3:
The following code allows you to jump to any item in your. No playerhead advancing. Plain and simple. playerItemList is your NSArray with AVPlayerItem objects.
- (void)playAtIndex:(NSInteger)index
{
[audioPlayer removeAllItems];
AVPlayerItem* obj = [playerItemList objectAtIndex:index];
[obj seekToTime:kCMTimeZero];
[audioPlayer insertItem:obj afterItem:nil];
[audioPlayer play];
}
回答4:
djiovann created a subclass of AVQueuePlayer
that provides exactly this functionality.
You can find it on github.
I haven't tested it yet but from browsing through the code it seems to get the job done. Also the code is well documented, so it should at least serve as a good reference for a custom implementation of the functionality (I suggest using a category instead of subclassing though).
回答5:
@saiday's answer works for me, here is swift version of his answer
func play(at index: Int) {
queue.removeAllItems()
for i in index..<items.count {
let obj: AVPlayerItem? = items[i]
if queue.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
queue.insert(obj!, after: nil)
}
}
}
回答6:
This should be the responsability of the AVQueuePlayer object and not your view controller itself, thus you should make it reusable and expose other answers implementations through an extension and use it in a similar way of advanceToNextItem() :
extension AVQueuePlayer {
func advanceToPreviousItem(for currentItem: Int, with initialItems: [AVPlayerItem]) {
self.removeAllItems()
for i in currentItem..<initialItems.count {
let obj: AVPlayerItem? = initialItems[i]
if self.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
self.insert(obj!, after: nil)
}
}
}
}
Usage (you only have to store an index and a reference to initial queue player items) :
self.queuePlayer.advanceToPreviousItem(for: self.currentIndex, with: self.playerItems)
One way of maintaining an index is to observe the AVPlayerItemDidPlayToEndTime notification for each of your video items :
func addDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
func removeDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
@objc func playerDidFinishPlaying(note: NSNotification) {
if queuePlayer.currentItem == queuePlayer.items().last {
print("last item finished")
} else {
print("item \(currentIndex) finished")
currentIndex += 1
}
}
This observation can also be really useful for other use cases (progress bar, current video timer reset ...).
回答7:
If you want to play a song from any index using AVQueuePlayer.Then this below code can help to.
NSMutableArray *musicListarray (add song that you want to play in queue);
AVQueuePlayer *audioPlayer;
AVPlayerItem *item;
-(void) nextTapped
{
nowPlayingIndex = nowPlayingIndex + 1;
if (nowPlayingIndex > musicListarray.count)
{
}
else
{
[self playTrack];
}
}
-(void) playback
{
if (nowPlayingIndex < 0)
{
}
else
{
nowPlayingIndex = nowPlayingIndex - 1;
[self playTrack];
}
}
-(void) playTrack
{
@try
{
if (musicArray.count > 0)
{
item =[[AVPlayerItem alloc] initWithURL: [NSURL URLWithString:musicListarray
[nowPlayingIndex]]];
[audioPlayer replaceCurrentItemWithPlayerItem:item];
[audioPlayer play];
}
}
@catch (NSException *exception)
{
}
}
-(void) PlaySongAtIndex
{
//yore code...
nowPlayingIndex = i (stating index from queue)[audioPlayer play];
[self playTrack];
}
Here PlaySongAtIndex call when you want to play a song.
来源:https://stackoverflow.com/questions/12176699/skip-to-previous-avplayeritem-on-avqueueplayer-play-selected-item-from-queue