Observação, para a pergunta abaixo: Todos os ativos são locais no dispositivo - nenhum streaming de rede está ocorrendo. Os vídeos contêm faixas de áudio.
Estou trabalhando em um aplicativo iOS que requer a reprodução de arquivos de vídeo com atraso mínimo para iniciar o videoclipe em questão. Infelizmente, não sabemos qual será o próximo videoclipe até que realmente precisemos iniciá-lo. Especificamente: Quando um videoclipe está sendo reproduzido, saberemos qual é o próximo conjunto de (aproximadamente) 10 videoclipes, mas não sabemos qual exatamente, até que chegue a hora de reproduzir "imediatamente" o próximo clipe.
O que fiz para ver os atrasos reais de início foi chamar addBoundaryTimeObserverForTimes
o player de vídeo, com um período de um milissegundo para ver quando o vídeo realmente começou a ser reproduzido, e considero a diferença desse carimbo de hora com o primeiro lugar em o código que indica qual ativo começar a jogar.
Pelo que vi até agora, descobri que usar a combinação de AVAsset
carregamento e, em seguida, criar um a AVPlayerItem
partir disso quando estiver pronto e, em seguida, esperar AVPlayerStatusReadyToPlay
antes de chamar o jogo, tende a levar entre 1 e 3 segundos para iniciar o grampo.
Desde então mudei para o que considero mais ou menos equivalente: ligar [AVPlayerItem playerItemWithURL:]
e esperar para AVPlayerItemStatusReadyToPlay
jogar. Praticamente o mesmo desempenho.
Uma coisa que estou observando é que o carregamento do primeiro item do AVPlayer é mais lento do que o resto. Parece que uma ideia é fazer um pré-voo do AVPlayer com um ativo curto / vazio antes de tentar reproduzir o primeiro vídeo. Isso pode ser uma boa prática geral. [ Início lento para AVAudioPlayer na primeira vez que um som é reproduzido
Eu adoraria diminuir o tempo de início do vídeo o máximo possível e ter algumas ideias de coisas para experimentar, mas gostaria de receber orientação de alguém que possa ajudar.
Atualização: a ideia 7, abaixo, conforme implementada, produz tempos de comutação de cerca de 500 ms. Isso é uma melhoria, mas seria bom fazer isso ainda mais rápido.
Ideia 1: Use N AVPlayers (não funciona)
Usando ~ 10 AVPPlayer
objetos, inicie e pause todos os ~ 10 clipes e, quando soubermos qual deles realmente precisamos, mude para e retome o correto AVPlayer
e comece tudo de novo para o próximo ciclo.
Não acho que isso funcione, porque li que há um limite de 4 ativos AVPlayer's
no iOS. Alguém perguntou sobre isso no StackOverflow aqui e descobriu sobre o limite de 4 AVPlayer: fast-switching-between-videos-using-avfoundation
Ideia 2: Use AVQueuePlayer (não funciona)
Não acredito que enfiar 10 AVPlayerItems
em um AVQueuePlayer
pré-carregue todos eles para um início perfeito. AVQueuePlayer
é uma fila e acho que realmente só deixa o próximo vídeo da fila pronto para reprodução imediata. Não sei qual dos cerca de 10 vídeos queremos reproduzir, até que seja hora de começar esse. ios-avplayer-video-preloading
Ideia 3: carregar, reproduzir e manter AVPlayerItems
em segundo plano (ainda não tenho 100% de certeza - mas não parece bom)
Estou verificando se há algum benefício em carregar e reproduzir o primeiro segundo de cada videoclipe em segundo plano (suprimir a saída de vídeo e áudio) e manter uma referência para cada um AVPlayerItem
e quando sabemos qual item precisa ser reproduzido real, troque aquele em, e troque o AVPlayer de fundo com o ativo. Enxague e repita.
A teoria seria que os reproduzidos recentemente AVPlayer/AVPlayerItem
ainda podem conter alguns recursos preparados que tornariam a reprodução subsequente mais rápida. Até agora, não vi benefícios disso, mas posso não ter a AVPlayerLayer
configuração correta para o plano de fundo. Duvido que isso realmente melhore as coisas pelo que tenho visto.
Ideia 4: Use um formato de arquivo diferente - talvez um que seja mais rápido de carregar?
No momento, estou usando o formato .m4v (vídeo-MPEG4) H.264. O H.264 tem várias opções de codec diferentes, portanto, é possível que algumas opções sejam mais rápidas de procurar do que outras. Descobri que o uso de configurações mais avançadas que tornam o tamanho do arquivo menor aumenta o tempo de busca, mas não encontrei nenhuma opção que vá de outra maneira.
Ideia 5: Combinação de formato de vídeo sem perdas + AVQueuePlayer
Se houver um formato de vídeo que carregue rapidamente, mas talvez o tamanho do arquivo seja insano, uma ideia pode ser preparar os primeiros 10 segundos de cada clipe de vídeo com uma versão que está inchada, mas mais rápida de carregar, mas de volta isso com um recurso codificado em H.264. Use um AVQueuePlayer e adicione os primeiros 10 segundos no formato de arquivo descompactado e, em seguida, use um que esteja em H.264, que obtém até 10 segundos de preparação / pré-carregamento. Portanto, eu obteria 'o melhor' dos dois mundos: tempos de início rápidos, mas também se beneficia de um formato mais compacto.
Ideia 6: Usar um AVPlayer não padrão / escrever meu / usar o de outra pessoa
Dadas as minhas necessidades, talvez eu não possa usar o AVPlayer, mas tenho que recorrer a AVAssetReader e decodificar os primeiros segundos (possivelmente gravar o arquivo bruto no disco) e, quando se trata de reprodução, fazer uso do formato bruto para reproduzi-lo de volta rápido. Parece um grande projeto para mim, e se eu fizer isso de uma forma ingênua, é pouco claro / improvável que funcione melhor. Cada quadro de vídeo decodificado e descompactado tem 2,25 MB. Falando ingenuamente - se usarmos ~ 30 fps para o vídeo, eu acabaria com o requisito de leitura do disco de ~ 60 MB / s, o que provavelmente é impossível / forçando-o. Obviamente, teríamos que fazer algum nível de compressão de imagem (talvez formatos de compressão openGL / es nativos via PVRTC) ... mas isso é meio louco. Talvez haja uma biblioteca que eu possa usar?
Ideia 7: Combine tudo em um único recurso de filme e procureToTime
Uma ideia que pode ser mais fácil do que algumas das anteriores, é combinar tudo em um único filme e usar seekToTime. O fato é que estaríamos pulando por todo o lugar. Acesso essencialmente aleatório ao filme. Acho que isso pode realmente funcionar bem: avplayer-movie-playing-lag-in-ios5
Qual abordagem você acha que seria a melhor? Até agora, não fiz muito progresso em termos de redução do atraso.
Respostas:
Para iOS 10.xe superior, para reduzir o atraso no início do AVPlayer, eu defini:
avplayer.automaticallyWaitsToMinimizeStalling = false;
e isso pareceu consertar para mim. Isso pode ter outras consequências, mas ainda não as atingi.Tive a ideia em: https://stackoverflow.com/a/50598525/9620547
fonte
O ativo pode não estar pronto depois de criá-lo, ele pode fazer cálculos como a duração do filme, certifique-se de conter todos os metadados do filme no arquivo.
fonte
Você deve tentar a opção nº 7 primeiro, apenas para ver se consegue fazê-la funcionar. Suspeito que não funcionará de fato para as suas necessidades, já que o tempo de busca provavelmente não será rápido o suficiente para permitir uma alternância perfeita entre os clipes. Se você tentar isso e falhar, eu aconselho que você escolha a opção 4/6 e dê uma olhada em minha biblioteca iOS projetada especificamente para esse propósito, basta fazer uma rápida pesquisa no Google sobre AVAnimator para saber mais. Minha biblioteca torna possível implementar loops contínuos e mudar de um clipe para outro, é muito rápido porque o vídeo tem que ser decodificado em um arquivo antes da mão. No seu caso, todos os 10 videoclipes seriam decodificados em arquivos antes de você começar, mas a troca entre eles seria rápida.
fonte
Sem ter feito nada parecido no passado, com base em seus pensamentos e experiências, eu tentaria uma combinação de 7 e 1: Pré-carregar um AVPlayer com os primeiros segundos dos 10 vídeos de acompanhamento. Então, pular provavelmente será mais rápido e confiável devido a menos dados. Enquanto reproduz a peça selecionada, você tem tempo suficiente para preparar o AVPlayer para o restante do vídeo de acompanhamento selecionado em segundo plano. Quando o início estiver concluído, você alterna para o AVPlayer preparado. Portanto, no total, a qualquer momento, você terá no máximo 2 AVPlayers carregados.
É claro que não sei se a mudança pode ser feita de forma tão suave que não perturbe a reprodução.
(Teria adicionado como comentário se pudesse.)
Atenciosamente, Peter
fonte
Se entendi seu problema corretamente, parece que você tem um vídeo contínuo para o qual precisa carregar a faixa de áudio imediatamente.
Se for esse o caso, sugiro olhar para o BASS . BASS é uma biblioteca de áudio muito parecida com o AVPlayer, que fornece acesso (relativamente) fácil às APIs de baixo nível da estrutura AudioUnits no iOS. O que isso significa para você? Isso significa que, com um pouquinho de manipulação do buffer (você pode nem precisar, depende de quão minúsculo você deseja o atraso), você pode começar a reproduzir música instantaneamente.
As limitações no entanto se estendem ao vídeo, como eu disse, é uma biblioteca de áudio então qualquer manipulação de vídeo ainda terá que ser feita com o AVPlayer. No entanto usando
-seekToTime:toleranfeBefore:toleranceAfter:
o você deve conseguir uma busca rápida no vídeo, contanto que faça a pré-rolagem com todas as opções necessárias.Se você estiver sincronizando em vários dispositivos (o que seu aplicativo pode sugerir), deixe um comentário e será um prazer editar minha resposta.
PS: BASS pode parecer assustador no início por causa de seu formato semelhante ao C, mas é realmente muito fácil de usar pelo que é.
fonte
Aqui estão várias propriedades e métodos fornecidos pela classe AVAsset que podem ajudar:
fonte