Replay items in AVQueuePlayer after last

后端 未结 4 2040
独厮守ぢ
独厮守ぢ 2021-01-05 04:29

I need to create something like an infinite loop in my AVQueuePlayer. Especially, I want to replay the whole NSArray of AVPlayerItems

相关标签:
4条回答
  • 2021-01-05 04:37

    This is it pretty much from scratch. The components are:

    1. Create a queue that is an NSArray of AVPlayerItems.
    2. As each item is added to the queue, set up a NSNotificationCenter observer to wake up when the video reaches the end.
    3. In the observer's selector, tell the AVPlayerItem that you want to loop to go back the the beginning, then tell the player to play.

    (NOTE: The AVPlayerDemoPlaybackView comes from the Apple "AVPlayerDemo" sample. Simply a subclass of UIView with a setter)

    BOOL videoShouldLoop = YES;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSMutableArray *videoQueue = [[NSMutableArray alloc] init];
    AVQueuePlayer *mPlayer;
    AVPlayerDemoPlaybackView *mPlaybackView;
    
    // You'll need to get an array of the files you want to queue as NSARrray *fileList:
    for (NSString *videoPath in fileList) {
        // Add all files to the queue as AVPlayerItems
        if ([fileManager fileExistsAtPath: videoPath]) {
            NSURL *videoURL = [NSURL fileURLWithPath: videoPath];
            AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL: videoURL];
            // Setup the observer
            [[NSNotificationCenter defaultCenter] addObserver: self
                                                     selector: @selector(playerItemDidReachEnd:)
                                                         name: AVPlayerItemDidPlayToEndTimeNotification
                                                       object: playerItem];
            // Add the playerItem to the queue
            [videoQueue addObject: playerItem];
        }
    }
    // Add the queue array to the AVQueuePlayer
    mPlayer = [AVQueuePlayer queuePlayerWithItems: videoQueue];
    // Add the player to the view
    [mPlaybackView setPlayer: mPlayer];
    // If you should only have one video, this allows it to stop at the end instead of blanking the display
    if ([[mPlayer items] count] == 1) {
        mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    }
    // Start playing
    [mPlayer play];
    
    
    - (void) playerItemDidReachEnd: (NSNotification *)notification
    {
        // Loop the video
        if (videoShouldLoop) {
            // Get the current item
            AVPlayerItem *playerItem = [mPlayer currentItem];
            // Set it back to the beginning
            [playerItem seekToTime: kCMTimeZero];
            // Tell the player to do nothing when it reaches the end of the video
            //  -- It will come back to this method when it's done
            mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
            // Play it again, Sam
            [mPlayer play];
        } else {
            mPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
        }
    }
    

    That's it! Let me know of something needs further explanation.

    0 讨论(0)
  • 2021-01-05 04:53

    I figured out a solution to loop over all the videos in my video queue, not just one. First I initialized my AVQueuePlayer:

    - (void)viewDidLoad
    {
        NSMutableArray *vidItems = [[NSMutableArray alloc] init];
        for (int i = 0; i < 5; i++)
        {
            // create file name and make path
            NSString *fileName = [NSString stringWithFormat:@"intro%i", i];
            NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"mov"];
            NSURL *movieUrl = [NSURL fileURLWithPath:path];
            // load url as player item
            AVPlayerItem *item = [AVPlayerItem playerItemWithURL:movieUrl];
            // observe when this item ends
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(playerItemDidReachEnd:)
                                                         name:AVPlayerItemDidPlayToEndTimeNotification
                                                       object:item];
            // add to array
            [vidItems addObject:item];
    
    
        }
        // initialize avqueueplayer
        _moviePlayer = [AVQueuePlayer queuePlayerWithItems:vidItems];
        _moviePlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    
        // create layer for viewing
        AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:_moviePlayer];
    
        layer.frame = self.view.bounds;
        layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        // add layer to uiview container
        [_movieViewContainer.layer addSublayer:layer];
    }
    

    When the notification is posted

    - (void)playerItemDidReachEnd:(NSNotification *)notification {
        AVPlayerItem *p = [notification object];
    
        // keep playing the queue
        [_moviePlayer advanceToNextItem];
        // if this is the last item in the queue, add the videos back in
        if (_moviePlayer.items.count == 1)
        {
            // it'd be more efficient to make this a method being we're using it a second time
            for (int i = 0; i < 5; i++)
            {
                NSString *fileName = [NSString stringWithFormat:@"intro%i", i];
                NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"mov"];
                NSURL *movieUrl = [NSURL fileURLWithPath:path];
    
                AVPlayerItem *item = [AVPlayerItem playerItemWithURL:movieUrl];
    
                [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(playerItemDidReachEnd:)
                                                             name:AVPlayerItemDidPlayToEndTimeNotification
                                                           object:item];
    
                 // the difference from last time, we're adding the new item after the last item in our player to maintain the order
                [_moviePlayer insertItem:item afterItem:[[_moviePlayer items] lastObject]];
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-05 04:54

    In Swift 4, you can use something like the following:

            NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) {
    
                notification in
    
                print("A PlayerItem just finished playing !")
    
                currentVideo += 1
    
                if(currentVideo >= videoPaths.count) {
    
                   currentVideo = 0
                }
    
                let url = URL(fileURLWithPath:videoPaths[currentVideo])
                let playerItem = AVPlayerItem.init(url: url)
    
                player.insert(playerItem, after: nil)
            }
    

    From what I understand the AVQueuePlayer will remove the last item played, so you need to keep adding the video just played to the queue, otherwise it will actually run out of videos to play! You can't just .seek to 0 and .play() again, that doesn't work.

    0 讨论(0)
  • 2021-01-05 05:00

    best way to loop a sequence of videos in AVQueuePlayer.

    observe for each playeritem in AVQueuePlayer.

    queuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    for(AVPlayerItem *item in items) {
        [[NSNotificationCenter defaultCenter] addObserver:self 
                selector:@selector(nextVideo:) 
                name:AVPlayerItemDidPlayToEndTimeNotification 
                object:item ];
    }
    

    on each nextVideo insert the currentItem again to queue it for playback. make sure to seek to zero for each item. after advanceToNextItem the AVQueuePlayer will remove the currentItem from queue.

    -(void) nextVideo:(NSNotification*)notif {
        AVPlayerItem *currItem = notif.userInfo[@"object"];
        [currItem seekToTime:kCMTimeZero];
        [queuePlayer advanceToNextItem];
        [queuePlayer insertItem:currItem afterItem:nil];
    }
    
    0 讨论(0)
提交回复
热议问题