Предварительная буферизация для AVQueuePlayer
Кто-нибудь знает, если AVQueuePlayer
начинает буферизацию следующего AVPlayerItem
, когда текущий элемент готов закончить игру?
Я ничего не знаю в документах, чтобы предложить это, я прошу, в основном, если кто-либо соблюдает такое поведение или нет.
Ответы
Ответ 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];
}
Выполните следующие действия. Если у вас есть сомнения, прокомментируйте пожалуйста.