Предварительная буферизация для AVQueuePlayer - PullRequest
20 голосов
/ 18 ноября 2010

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

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

Ответы [ 5 ]

21 голосов
/ 07 января 2011

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

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

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

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

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

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

6 голосов
/ 08 ноября 2011

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

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

5 голосов
/ 05 июля 2012

Нашли обходной путь !!!После многих часов поиска и неудачных тестов я нашел решение, которое работает на 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, но, как упомянул Стефан Кендалл в комментариях, теперь это спам-сайт.Думаю, я бы оставил ссылку для целей атрибуции, но не посещаю ее!

2 голосов
/ 04 января 2011

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

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

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

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

Редактировать: Хорошо, я провел еще какое-то тестирование, и кажется, что следующие элементы начинают загружаться раньше, чем последний элемент.заканчивает игратьСмотрите пост ниже.Нажмите «НЕ» ...

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

Во всяком случае, я перебрал свой itemsArray, добавив наблюдение за свойством status с помощью 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-файла в очередь у меня все еще есть какое-то странное поведение, из-за чего я удивляюсь, начнут ли загружаться эти ресурсы во время создания (хотя мне это кажется немного странным).

0 голосов
/ 19 августа 2016

Я сделал простую демонстрацию для вас и других, которые хотят сократить время буферизации каждого видео по 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];
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...