Подтвердить что ты не робот

Предварительная буферизация для AVQueuePlayer

Кто-нибудь знает, если AVQueuePlayer начинает буферизацию следующего AVPlayerItem, когда текущий элемент готов закончить игру?

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

4b9b3361

Ответ 1

Хорошо, я снова просмотрел эту проблему и написал код для проверки AVQueuePlayer.

jollyCocoa ответ указал мне в правильном направлении, предложив наблюдать за свойством status на AVPlayerItem. Однако в документации не указано, что это свойство (и это значение AVPlayerItemStatusReadyToPlay в частности) может быть связано с буферизацией.

Однако свойство AVPlayerItem's loadedTimeRanges больше похоже на буферизацию.

Выполнение KVO в этом массиве было немного сложнее - сам объект массива не изменяется, только его элементы - поэтому я прибегал к его распечатке каждый раз.

То, что я узнал, это то, что несколько секунд в первом элементе очереди, loadedTimeRanges для второго элемента показывает новый CMTimeRange со временем начала 0 и небольшой продолжительностью. Длительность может увеличиться до 60 секунд, пока предыдущий элемент продолжает играть.

Короткий ответ: AVQueuePlayer будет буферизовать следующий AVPlayerItem при воспроизведении текущего.

Ответ 2

Как и в iOS 5, похоже, что AVQueuePlayer больше не предварительно буферизует. Он сделал предварительную буферизацию следующего трека в iOS 4.

Я не уверен, почему произошли какие-то изменения, или если они даже обсуждают часть Apple или просто ошибку. В любом случае, это боль, и я буду сообщать им об этом.

Ответ 3

Найдена работа!!! После многих часов поиска и неудачных тестов я нашел решение, которое работает на iOS 5 и выше.

Это будет воспроизведение файлов без задержки между ними. Не так удобно, если вы хотите переключаться между файлами, но, конечно же, все вместе поставите перед собой. По-прежнему можно отслеживать, где начинается каждый файл, добавляя AVURLAsset, сохраняйте переменную CMTime, например:

NSValue *toJumpTo = [NSValue valueWithCMTime:composition.duration];
[array addObject:toJumpTo];

а затем просто просмотрите массив, проверив текущее значение времени и сравнив его с помощью CMTimeCompare.

Изменить: был найден на http://www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html, но, как отметил в комментариях Стефан Кендалл, он теперь является сайтом спама. Думал, что я оставлю ссылку для целей присвоения, но не посещать ее!

Ответ 4

Я экспериментировал с AVQueuePlayer, используя фильмы, предоставляемые сервером. В этом конкретном тесте я установил queuePlayer с AVPlayerItems, инициализированным URL-адресом, двумя различными видеоатрибутами разных типов. Один -.mp4 файл (прогрессивная загрузка), другой - файл http-streaming.m3u8. Должен сказать, он действует немного фанк.

В зависимости от порядка, в котором я ставил очередь в файлы, я получаю совсем другое поведение. Если я начинаю с .mp4 файла, AVPlayerLayer пуст и тикает, когда nextItem запускает плеер (файл .m3u8). Если я изменю порядок и начню с файла m3u8, AVPlayerLayer будет пустым, но звук из второго файла будет отлично воспроизводиться.

Мое впечатление от того, что я видел до сих пор, заключается в том, что AVQueuePlayer делает NOT начало загрузки следующего элемента AVPlayerItem до того, как текущий AVPlayerItem закончил игру. Но я могу ошибаться.

Продолжение следует, я думаю...

Изменить: Хорошо, поэтому я сделал еще несколько тестов, и кажется, что следующие предметы начинают загружаться до того, как последний элемент закончит игру. См. Сообщение ниже. Обведите "NOT"...

Edit2: добавьте сюда свое объяснение, так как комментарий оставил меня без параметров форматирования. (Лучшая практика для этого приветствуется, если это плохой способ обработки обновлений ответов)

Во всяком случае, я повторил через мои пунктыArray, добавляя наблюдение свойства статуса с помощью KVO...

[playerItem addObserver: self forKeyPath:@"status" options: 0 context: NULL];

Я также установил свой контроллер в качестве наблюдателя уведомления AVPlayerItem AVPlayerItemDidPlayToEndTimeNotification:

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(playerItemDidReachEnd:) 
                                             name: AVPlayerItemDidPlayToEndTimeNotification 
                                           object: nil];

Теперь я могу зарегистрировать изменение статуса AVPlayerItem, и, оказывается, следующий AVPlayerItem в очереди регистрируется со статусом AVPlayerItemStatusReadyToPlay до того, как текущий элемент закончит воспроизведение.

Мой вывод заключается в том, что следующий элемент в строке начинает загрузку до окончания текущего элемента.

Однако! Я создаю массив с элементами AVPlayerItems, которые я использую для создания своего плеера. Чтение документов AVFoundation на AVAssets, создается впечатление, что они загружают свои активы асинхронно, но я все еще не уверен, когда эти загрузки инициируются IAPlayerItem. У меня все еще есть какое-то причудливое поведение, когда я добавляю .m3u8 файл в очередь, что заставляет меня задаться вопросом, начнут ли эти ресурсы загружаться во время создания (кажется мне немного странным).

Ответ 5

Я сделал простую демонстрацию для вас и других, которые хотят сократить время буферизации каждого видео с URL-адреса.

ПРИМЕЧАНИЕ: Не знаю, что это правильно или нет, но он отлично работает для меня без каких-либо проблем. Если кто-то знает больше или хочет улучшить код для лучшего результата, тогда добро пожаловать на изменение моего ответа.

В приведенном ниже коде первое видео занимает обычное время буферизации. Следующее видео будет запускаться автоматически, когда текущее видео закончится, и вы можете прокручивать влево и вправо для перемещения следующего и предыдущего видео.

Выполните следующие шаги.

1) В файле videoPlayerVC.h.

#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>

@interface videoPlayerVC : UIViewController
{
    AVPlayerViewController *avPlyrViewController;
    AVPlayerItem *currentAVPlyrItem;

    int currentVideoNO;  // For current video track.
    NSMutableArray *arrOfAVPItems;
    NSMutableArray *arrOfPlyer;
}

2) В файле videoPlayerVC.m

Здесь вы можете начать воспроизведение видео, нажав кнопку. Ниже приведен метод действий с кнопками.

-(void)clickOnPlayVideosImage:(UIButton *)sender
{
   // In my App. all video URLs is up to 15 second.
    currentVideoNO = 0;
    NSMutableArray *arrForVideoURL = [[NSMutableArray alloc]initWithObjects:
                                      [NSURL URLWithString:@"http://videos/url1.mov"],
                                      [NSURL URLWithString:@"http://videos/url2.mov"],
                                      [NSURL URLWithString:@"http://videos/url3.mov"],
                                      [NSURL URLWithString:@"http://videos/url3.mov"],
                                      [NSURL URLWithString:@"http://videos/url4.mov"],
                                      [NSURL URLWithString:@"http://videos/url5.mov"], nil];

    AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:0]];
    AVPlayerItem *thePlayerItemB = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:1]];
    AVPlayerItem *thePlayerItemC = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:2]];
    AVPlayerItem *thePlayerItemD = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:3]];
    AVPlayerItem *thePlayerItemE = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:4]];
    AVPlayerItem *thePlayerItemF = [[AVPlayerItem alloc] initWithURL:[arrForVideoURL objectAtIndex:5]];

    if(arrOfAVPItems.count > 0)
       [arrOfAVPItems removeAllObjects];
    arrOfAVPItems = nil;

    if(arrOfPlyer.count > 0)
        [arrOfPlyer removeAllObjects];
    arrOfPlyer = nil;
    arrOfPlyer = [[NSMutableArray alloc] init];

    arrOfAVPItems = [NSMutableArray arrayWithObjects:thePlayerItemA, thePlayerItemB, thePlayerItemC, thePlayerItemD, thePlayerItemE, thePlayerItemF, nil]; // Add All items in the Array

    for(AVPlayerItem *myPlyrItem in arrOfAVPItems)
    {
        AVPlayer *videoPlayerNext = [AVPlayer playerWithPlayerItem:myPlyrItem]; /// Add item in the player
        [videoPlayerNext play]; 
        [videoPlayerNext pause];
        [arrOfPlyer addObject:videoPlayerNext]; /// Make Array of  "AVPlayer" just reduce buffering of each video. 
    }

    avPlyrViewController = [AVPlayerViewController new];
    avPlyrViewController.delegate = self;
    avPlyrViewController.player = (AVPlayer *)arrOfPlyer[0]; // Add first player from the Array.
    avPlyrViewController.showsPlaybackControls = YES;
    avPlyrViewController.allowsPictureInPicturePlayback = YES;
    avPlyrViewController.videoGravity = AVLayerVideoGravityResizeAspect;

    [self presentViewController:avPlyrViewController animated:YES completion:^{
        [self performSelector:@selector(playVideos) withObject:nil afterDelay:1];/// call method after one second for play video.

        UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToNextVideo:)];
        swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
        [avPlyrViewController.view addGestureRecognizer:swipeleft]; // Add left swipe for move on next video

        UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeleftToPerviousVideo:)];
        swiperight.direction=UISwipeGestureRecognizerDirectionRight;
        [avPlyrViewController.view addGestureRecognizer:swiperight]; // Add right swipe for move on previous video
    }];
}

Теперь напишите код метода playVideos.

#pragma mark - AVPlayer Methods -

-(void)playVideos
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // remove current "AVPlayerItem" from the notification observe.

    if(arrOfAVPItems.count > 0)
    {
        currentAVPlyrItem = (AVPlayerItem *)arrOfAVPItems[currentVideoNO]; // Add "AVPlayerItem" from the array.

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem]; // Add notification observer to indication current "AVPlayerItem" is finish.

        // pause and nil previous player if available.
        [avPlyrViewController.player pause];
        avPlyrViewController.player = nil;

        avPlyrViewController.player = (AVPlayer *)arrOfPlyer[currentVideoNO]; // add new player from the array
        [avPlyrViewController.player.currentItem seekToTime:kCMTimeZero]; // set for start video on initial position.
        [avPlyrViewController.player play]; // Play video

    }
}

Наблюдатель уведомлений, чтобы указать, что видео завершено.

- (void)playerItemDidReachEnd:(NSNotification *)notification
{
    NSLog(@"IT REACHED THE END");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:currentAVPlyrItem];  // remove current "AVPlayerItem" from the notification observe.

    [self swipeleftToNextVideo:nil]; // Call method for next video.
}

Способ воспроизведения следующего видео

-(void)swipeleftToNextVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
    currentVideoNO++;
    if(currentVideoNO > (arrOfAVPItems.count -1))
        currentVideoNO = 0;

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));

    [self playVideos];
}

Способ воспроизведения предыдущего видео.

-(void)swipeleftToPerviousVideo:(UISwipeGestureRecognizer*)gestureRecognizer
{
    currentVideoNO--;
    if(currentVideoNO < 0)
        currentVideoNO = (int)(arrOfAVPItems.count -1);

    NSLog(@"current - %d and last - %d", currentVideoNO, (int)(arrOfAVPItems.count -1));

    [self playVideos];
}

Выполните следующие действия. Если у вас есть сомнения, прокомментируйте пожалуйста.