Pergunta

Alguém sabe se AVQueuePlayer começa a buffer o próximo AVPlayerItem Quando o item atual está prestes a terminar de jogar?

Sei que não há nada nos documentos para sugerir isso, estou perguntando principalmente se alguém observou esse tipo de comportamento ou não.

Foi útil?

Solução

Ok, eu olhei por esse problema novamente e escrevi algum código para verificar AVQueuePlayer.

A resposta de Jollycocoa me apontou na direção certa, sugerindo observar a propriedade de status em AVPlayerItem. No entanto, a documentação não parece apontar que esta propriedade (e é AVPlayerItemStatusReadyToPlay valor em particular) pode estar relacionado ao buffer.

No entanto, o AVPlayerItem's loadedTimeRanges A propriedade parece mais relacionada ao buffer.

Fazer KVO nessa matriz foi um pouco mais complicado - o próprio objeto da matriz não muda, apenas os itens são - então eu recorri a imprimir seu conteúdo a cada segundo.

O que eu descobri é que alguns segundos no primeiro item da fila, o loadedTimeRanges Para o segundo item, aparece um novo CMTimeRange Com o tempo de início 0 e uma pequena duração. A duração pode aumentar até 60 segundos, enquanto o item anterior continua jogando.

Resposta curta: AVQueuePlayer irá atingir o próximo AVPlayerItem enquanto joga o atual.

Outras dicas

Até o iOS 5, parece que o AvqueuePlayer não é mais pré-buffer. Ele pré-buffer a próxima faixa no iOS 4.

Não sei por que houve uma mudança ou se é deliberada na parte da Apple ou simplesmente um bug. De qualquer forma, é uma dor e enviarei um bug para eles sobre isso.

Encontrei uma vida por aí !!! Depois de muitas horas de pesquisa e testes fracassados, encontrei uma solução que funciona no iOS 5 e acima.

Isso reproduzirá arquivos sem nenhum atraso no meio. Não é tão conveniente se você deseja alternar entre arquivos, mas certamente juntará tudo para você. Ainda é possível acompanhar onde cada arquivo inicia, ao adicionar avurlasset, mantenha a variável CMtime, algo como:

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

E então passe pela matriz, verificando o valor do tempo atual e comparando -o usando o cmtimecompare.

Edit: foi encontrado em esta página Visível do arquivo da web (atualmente o link aponta para spam).

Eu tenho experimentado o AvqueuePlayer usando filmes fornecidos por um servidor. Neste teste em particular, configurei um QueuePlayer com o AvPlayeritems inicializado com URLs para dois ativos de vídeo diferentes de tipos diferentes. Um é um .mp4-File (download progressivo), o outro e um arquivo .m3u8 http streaming. Ele age um pouco descolado, devo dizer.

Dependendo da ordem em que eu filo os arquivos, recebo comportamentos bem diferentes. Se eu começar com o .mp4-File, o AvPlayerlayer fica em branco e silencioso quando o próximo jogador inicia (o .M3U8-File). Se eu alterar o pedido e começar com o m3u8-arquivo, o AvPlayerlayer ficará em branco, mas o áudio do segundo arquivo é bom.

Minha impressão, pelo que vi até agora, é que o AvqueuePlayer faz NÃO Comece a baixar o próximo AvPlayeritem antes que o AvPlayeritem atual termine de jogar. Mas eu posso estar errado.

Para continuar, eu acho ...

EDIT: OK, então eu fiz mais alguns testes e parece que os próximos itens começam a baixar antes que o último item termine de jogar. Veja a postagem abaixo. Acaricie o "não" ...

Edit2: Adicionando minha explicação aqui, já que o comentário me deixou sem opções de formatação. (As melhores práticas para isso são bem -vindas, se essa é uma maneira ruim de lidar com as atualizações de respostas)

De qualquer forma, eu itei através do meu itens -mar de itens adicionando observação da propriedade de status usando KVO ...

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

Eu também defino meu controlador como o observador da notificação AvPlayeritem AvPlayeritemDidPlayToEndTimenotification:

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

Agora eu poderia registrar a mudança de status do AvPlayeritem, e acontece que o próximo Avplayeritem na fila é registrado com uma mudança de status AvPlayeritemStatusReadyToPlay Antes que o item atual termine.

Minha conclusão é assim que o próximo item na fila começa a baixar antes do final do item atual.

No entanto! Eu crio uma matriz com o AvPlayeritems que eu uso para criar meu jogador. Lendo os documentos da Avfoundation em Avasts, tenho a impressão de que estes baixam seus ativos de maneira assíncrona, mas ainda não tenho certeza quando esses downloads estão sendo iniciados pelo Iaplayeritem. Ainda tenho algum comportamento descolado quando adiciono o .M3U8-File à fila, o que me faz pensar se esses ativos começam a baixar no momento da criação (parece um pouco estranho para mim).

Fiz uma demonstração simples para você e outros que desejam reduzir o tempo de buffer de cada vídeo da URL.

NOTA: Não sei isso é certo ou não, mas está funcionando perfeito para mim sem nenhum problema. Se alguém souber mais ou quiser melhorar o código para obter um melhor resultado, seja bem -vindo para alterar minha resposta.

No código abaixo, o primeiro vídeo leva o tempo normal de buffer. O próximo vídeo será iniciado automaticamente quando o vídeo atual estiver concluído e você poderá deslizar para a esquerda e para a direita para se mover em seguida e o vídeo anterior.

Siga as etapas abaixo.

1) no videoplayervc.h Arquivo.

#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) no videoplayervc.m Arquivo

Aqui você pode começar a reproduzir vídeo clicando no botão. Abaixo está o método de ação do botão.

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

Agora escreva playVideos Código do método.

#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

    }
}

O observador de notificação para indicar que o vídeo está acabando.

- (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.
}

Método para reproduzir o próximo vídeo

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

Método para reproduzir vídeo anterior.

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

Siga esses passos. Se alguma dúvida, comente, por favor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top