Воспроизвести элементы в AVQueuePlayer после последнего

Мне нужно создать что-то вроде бесконечного цикла в моем AVQueuePlayer. В частности, я хочу воспроизвести все NSArray из AVPlayerItem, когда закончится воспроизведение последнего компонента.

Должен признать, что на самом деле я понятия не имею, как этого добиться, и надеюсь, что вы дадите мне несколько подсказок.


person Edelweiss    schedule 22.06.2012    source источник
comment
Вы застряли на этом этапе или вам просто нужно создать его с начальной точки?   -  person Dhruv    schedule 22.06.2012
comment
Я на самом деле теперь, как создать его и воспроизвести все AVQueuePlayers, теперь я ищу перезапуск проигрывателя, когда будет выполнен последний QVPlayerItem.   -  person Edelweiss    schedule 22.06.2012
comment
-(void) playVideoAtIndex:(NSInteger)index{ [self performSelector:@selector(setObservationInfo)]; currentIndex = index; AVPlayerItem *videoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:[arrVideoList objectAtIndex:index]]]; } где нужно проверить, if (currentIndex < [arrVideoList count]-1) { currentIndex++; } else { currentIndex = 0; } [self playVideoAtIndex:currentIndex];   -  person Dhruv    schedule 22.06.2012
comment
Итак, вы используете массив с видео? Этот массив содержит путь к видео, верно?   -  person Edelweiss    schedule 22.06.2012
comment
Да, именно так, вам просто нужно передать путь, и когда вы получите последний, вы можете вернуться к первому индексу.   -  person Dhruv    schedule 22.06.2012
comment
Я собираюсь изучить это подробнее, очень мило с вашей стороны. Спасибо.   -  person Edelweiss    schedule 22.06.2012
comment
Если вы столкнетесь с какой-либо проблемой, спросите, разрешит ли вам проблему. :)   -  person Dhruv    schedule 22.06.2012
comment
@Edelweiss, я тоже ищу. Вы нашли какое-нибудь решение для этого?   -  person user2526811    schedule 25.09.2016


Ответы (5)


лучший способ зациклить последовательность видео в AVQueuePlayer.

наблюдать за каждым элементом проигрывателя в AVQueuePlayer.

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

на каждом следующем видео снова вставьте текущий элемент, чтобы поставить его в очередь для воспроизведения. обязательно стремитесь к нулю для каждого элемента. после advanceToNextItem AVQueuePlayer удалит текущий элемент из очереди.

-(void) nextVideo:(NSNotification*)notif {
    AVPlayerItem *currItem = notif.userInfo[@"object"];
    [currItem seekToTime:kCMTimeZero];
    [queuePlayer advanceToNextItem];
    [queuePlayer insertItem:currItem afterItem:nil];
}
person darshansonde    schedule 18.02.2015
comment
У меня это сработало после изменения notif.userInfo[@"object"] на notif.object (iOS 9). - person Tomas Andrle; 06.07.2018

Это в значительной степени с нуля. Компоненты:

  1. Создайте очередь, которая является NSArray из AVPlayerItems.
  2. По мере добавления каждого элемента в очередь настройте наблюдателя NSNotificationCenter, чтобы он просыпался, когда видео достигает конца.
  3. В селекторе наблюдателя сообщите AVPlayerItem, что вы хотите выполнить цикл, чтобы вернуться к началу, а затем скажите игроку начать воспроизведение.

(ПРИМЕЧАНИЕ: AVPlayerDemoPlaybackView взят из образца Apple "AVPlayerDemo". Просто подкласс UIView с установщиком)

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

Вот и все! Сообщите мне, что кое-что требует дополнительных объяснений.

person mpemburn    schedule 16.01.2013
comment
Что делать, если я добавляю в плеер 3 видео, и после завершения всех 3 видео последнее видео будет воспроизводиться в бесконечном цикле - person ios developer; 12.03.2013
comment
Логика здесь не обеспечивает желаемое поведение OP. Он зацикливает последний элемент, а не весь массив элементов игрока. - person Jordan H; 03.04.2016

Я нашел способ перебрать все видео в очереди видео, а не только одно. Сначала я инициализировал свой 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];
}

Когда уведомление размещено

- (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]];
        }
    }
}
person Chris    schedule 13.02.2015

В Swift 4 вы можете использовать что-то вроде следующего:

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

Насколько я понимаю, AVQueuePlayer удалит последний воспроизведенный элемент, поэтому вам нужно продолжать добавлять только что воспроизведенное видео в очередь, иначе у него действительно закончится видео для воспроизведения! Вы не можете просто .seek на 0 и снова .play(), это не сработает.

person Pixel    schedule 20.05.2019

Работает на iOS 14 с марта 2021 года

Просто хотел опубликовать свое решение в качестве нового кодера, мне потребовалось время, чтобы понять. мое решение зацикливает avqueuplayer внутри UIView без использования AVLooper. Идея пришла от raywenderlich. Вы можете игнорировать оболочку UIView и т. Д. Идея состоит в том, чтобы использовать NSKeyValeObservation, а не центр уведомлений, как предлагают все остальные.

class QueuePlayerUIView: UIView {
private var playerLayer = AVPlayerLayer()
private var playerLooper: AVPlayerLooper?

@objc let queuePlayer = AVQueuePlayer()

var token: NSKeyValueObservation?

var clips = [URL]()

func addAllVideosToPlayer() {
    for video in clips {
        let asset = AVURLAsset(url: video)
        let item = AVPlayerItem(asset: asset)
        queuePlayer.insert(item, after: queuePlayer.items().last)
      }
}

init(videosArr: [URL]) {
    super.init(frame: .zero)
    
    self.clips = videosArr

    addAllVideosToPlayer()
    playerLayer.player = queuePlayer
    playerLayer.videoGravity = .resizeAspectFill
    layer.addSublayer(playerLayer)
    queuePlayer.play()

    token = queuePlayer.observe(\.currentItem) { [weak self] player, _ in
      if player.items().count == 1 {
        self?.addAllVideosToPlayer()
      }
    }
}

override func layoutSubviews() {
    super.layoutSubviews()
    playerLayer.frame = bounds
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

}

person tiktoker    schedule 14.03.2021