Comment réduire le délai de démarrage d'iOS AVPlayer

115

Remarque, pour la question ci-dessous: Tous les actifs sont locaux sur l'appareil - aucune diffusion réseau n'a lieu. Les vidéos contiennent des pistes audio.

Je travaille sur une application iOS qui nécessite la lecture de fichiers vidéo avec un délai minimum pour démarrer le clip vidéo en question. Malheureusement, nous ne savons pas quel clip vidéo spécifique est le prochain jusqu'à ce que nous ayons réellement besoin de le démarrer. Plus précisément: lorsqu'un clip vidéo est en cours de lecture, nous saurons ce que sera le prochain ensemble de (environ) 10 clips vidéo, mais nous ne savons pas lequel exactement, jusqu'à ce qu'il soit temps de lire «immédiatement» le clip suivant.

Ce que j'ai fait pour examiner les retards de démarrage réels est d'appeler addBoundaryTimeObserverForTimesle lecteur vidéo, avec une période d'une milliseconde pour voir quand la vidéo a réellement commencé à jouer, et je prends la différence de cet horodatage avec la première place dans le code qui indique quel actif commencer à jouer.

D'après ce que j'ai vu jusqu'à présent, j'ai trouvé que l'utilisation de la combinaison de AVAssetchargement, puis la création d'un à AVPlayerItempartir de celui-ci une fois qu'il est prêt, puis attendre AVPlayerStatusReadyToPlayavant d'appeler play, a tendance à prendre entre 1 et 3 secondes pour démarrer le agrafe.

Depuis, je suis passé à ce que je pense être à peu près équivalent: appeler [AVPlayerItem playerItemWithURL:]et attendre pour AVPlayerItemStatusReadyToPlayjouer. À peu près les mêmes performances.

Une chose que j'observe est que le premier chargement d'élément AVPlayer est plus lent que le reste. Il semble qu'une idée soit de pré-piloter l'AVPlayer avec un élément court / vide avant d'essayer de lire la première vidéo pourrait être une bonne pratique générale. [ Démarrage lent pour AVAudioPlayer la première fois qu'un son est lu

J'adorerais réduire autant que possible les heures de début de la vidéo et avoir des idées de choses à expérimenter, mais j'aimerais recevoir des conseils de toute personne qui pourrait être en mesure de vous aider.

Mise à jour: l'idée 7, ci-dessous, telle qu'implémentée, donne des temps de commutation d'environ 500 ms. C'est une amélioration, mais ce serait bien de l'obtenir encore plus rapidement.

Idée 1: Utilisez N AVPlayers (ne fonctionnera pas)

En utilisant ~ 10 AVPPlayerobjets et en démarrant et en mettant en pause les ~ 10 clips, et une fois que nous savons lequel nous avons vraiment besoin, basculez vers et réactivez la pause AVPlayer, et recommencez pour le cycle suivant.

Je ne pense pas que cela fonctionne, car j'ai lu qu'il y a environ une limite de 4 actifs AVPlayer'sdans iOS. Quelqu'un a posé une question à ce sujet sur StackOverflow ici, et a découvert la limite de 4 AVPlayer: commutation rapide entre les vidéos en utilisant avfoundation

Idée 2: utilisez AVQueuePlayer (ne fonctionnera pas)

Je ne crois pas que pousser 10 AVPlayerItemsdans un AVQueuePlayerles pré-chargerait tous pour un démarrage transparent. AVQueuePlayerest une file d'attente, et je pense que cela ne fait que préparer la prochaine vidéo de la file d'attente pour une lecture immédiate. Je ne sais pas laquelle des ~ 10 vidéos que nous voulons lire, jusqu'à ce qu'il soit temps de commencer celle-là. ios-avplayer-vidéo-préchargement

Idée 3: charger, lire et conserver AVPlayerItemsen arrière-plan (pas encore sûr à 100% - mais pas beau)

Je regarde s'il y a un avantage à charger et à lire la première seconde de chaque clip vidéo en arrière-plan (supprimer la sortie vidéo et audio), et garder une référence à chacun AVPlayerItem, et quand nous savons pour quel élément doit être lu réel, échangez celui-là et échangez l'AVPlayer d'arrière-plan avec celui actif. Rincez et répétez.

La théorie serait que les jeux récemment joués AVPlayer/AVPlayerItempeuvent encore contenir des ressources préparées qui rendraient la lecture ultérieure plus rapide. Jusqu'à présent, je n'en ai pas vu les avantages, mais je n'ai peut-être pas la AVPlayerLayerconfiguration correctement pour l'arrière-plan. Je doute que cela améliorera vraiment les choses par rapport à ce que j'ai vu.

Idée 4: Utilisez un format de fichier différent - peut-être un format de fichier plus rapide à charger?

J'utilise actuellement le format H.264 .m4v (vidéo-MPEG4). H.264 a beaucoup d'options de codec différentes, il est donc possible que certaines options soient plus rapides à rechercher que d'autres. J'ai trouvé que l'utilisation de paramètres plus avancés qui réduisent la taille du fichier augmente le temps de recherche, mais je n'ai trouvé aucune option qui va dans l'autre sens.

Idée 5: combinaison de format vidéo sans perte + AVQueuePlayer

S'il existe un format vidéo rapide à charger, mais peut-être où la taille du fichier est insensée, une idée pourrait être de pré-préparer les 10 premières secondes de chaque clip vidéo avec une version gonflée mais plus rapide à charger, mais en arrière cela avec un actif encodé en H.264. Utilisez un AVQueuePlayer et ajoutez les 10 premières secondes au format de fichier non compressé, puis faites-en un qui est en H.264 qui obtient jusqu'à 10 secondes de temps de préparation / préchargement. J'obtiendrais donc «le meilleur» des deux mondes: des temps de démarrage rapides, mais aussi des avantages d'un format plus compact.

Idée 6: utiliser un AVPlayer non standard / écrire le mien / utiliser celui de quelqu'un d'autre

Compte tenu de mes besoins, je ne peux peut-être pas utiliser AVPlayer, mais je dois recourir à AVAssetReader et décoder les premières secondes (éventuellement écrire un fichier brut sur le disque), et quand il s'agit de la lecture, utilisez le format brut pour le lire retour rapide. Cela me semble être un énorme projet, et si je le fais de manière naïve, il n'est pas clair / il est peu probable qu'il fonctionne mieux. Chaque image vidéo décodée et non compressée est de 2,25 Mo. Naïvement - si nous optons pour ~ 30 fps pour la vidéo, je me retrouverais avec ~ 60 Mo / s de lecture à partir du disque, ce qui est probablement impossible / pousser. Évidemment, nous devrons faire un certain niveau de compression d'image (peut-être des formats de compression openGL / es natifs via PVRTC) ... mais c'est un peu fou. Peut-être qu'il existe une bibliothèque que je peux utiliser?

Idée 7: combinez tout dans un seul élément de film et seekToTime

Une idée qui pourrait être plus facile que certaines des solutions ci-dessus est de tout combiner en un seul film et d'utiliser seekToTime. Le truc, c'est que nous sauterions partout. Accès essentiellement aléatoire au film. Je pense que cela peut fonctionner correctement: avplayer-movie-playing-lag-in-ios5

Quelle approche pensez-vous être la meilleure? Jusqu'à présent, je n'ai pas fait autant de progrès en termes de réduction du décalage.

Bernt Habermeier
la source
Pour ce que ça vaut, je vais avec Idea 7. C'est encore lent, mais pas aussi imprévisible que les autres options. La prochaine question que je me pose est la suivante: les options du codec, la résolution et la fréquence des images clés ont-elles un impact sur la synchronisation de la recherche?
Bernt Habermeier le
En retard dans le jeu, mais il peut être utile de changer de vidéo le plus rapidement possible (c'est-à-dire immédiatement après le début de la lecture de la nouvelle) et de profiler l'application pour voir où elle passe le plus de temps CPU.
tc.
1
Presque un an plus tard maintenant, qu'avez-vous découvert avec ça?
lnafziger
1
Je suis allé avec l'option 7, et je suis arrivé quelque part entre 300 ms et 500 ms. Une chose que j'ai trouvée est que plus les options du codec mp4 sont sophistiquées, plus le seekTo est lent. Certaines options de compression vidéo permettent une meilleure compression et maintiennent la qualité vidéo, mais éliminent le temps de décodage.
Bernt Habermeier
2
C'est une demande qui semble raisonnable, mais vraiment pour implémenter l'option 7, il y a trop de facettes dans l'implémentation à publier. Considérez: (a) Créez une chaîne d'outils pour fusionner les actifs vidéo, (b) assurez-vous de garder une trace des décalages de segment vidéo, (c) recherchez les décalages lorsqu'une demande arrive pour lire un clip spécifique, (d) utilisez addPeriodicTimeObserverForInterval pour vérifier si vous courez un clip vidéo et réagissez en conséquence (utilisez cette méthode par rapport à un addBoundaryTimeObserverForTimes à un seul coup parce que j'ai trouvé que ce dernier ne se déclenche parfois pas ... dans l'ensemble, cela ne se prête pas au collage de code.
Bernt Habermeier

Réponses:

4

Pour iOS 10.x et supérieur pour réduire le délai de démarrage d'AVPlayer que j'ai défini: avplayer.automaticallyWaitsToMinimizeStalling = false; et cela a semblé résoudre le problème pour moi. Cela pourrait avoir d'autres conséquences, mais je ne les ai pas encore atteintes.

J'ai eu l'idée de: https://stackoverflow.com/a/50598525/9620547

grizzb
la source
J'ai eu 6-7 secondes de réduction du délai. Mais j'ai une question ici, cela aura-t-il un impact sur les performances de l'application?
kalpa
@kalpa Nous utilisons ce code dans une application de production depuis plus d'un an sans aucun effet négatif perceptible. Nous lisons principalement des fichiers audio de 20 à 60 minutes aux auditeurs aux États-Unis où la couverture des données mobiles est généralement rapide. Votre cas d'utilisation pourrait cependant être différent.
grizzb
Merci pour l'info. @grizzb
kalpa
1

L'élément peut ne pas être prêt une fois que vous l'avez créé, il peut effectuer des calculs tels que la durée du film, assurez-vous de contenir toutes les métadonnées du film dans le fichier.

nova
la source
1

Vous devriez d'abord essayer l'option 7, juste pour voir si vous pouvez faire fonctionner cela. Je soupçonne que cela ne fonctionnera pas réellement pour vos besoins car le temps de recherche ne sera probablement pas assez rapide pour vous permettre de basculer de manière transparente entre les clips. Si vous essayez cela et que cela échoue, alors je vous conseillerais de faire l'option 4/6 et de jeter un œil à ma bibliothèque iOS conçue spécifiquement à cet effet, faites simplement une recherche rapide sur Google sur AVAnimator pour en savoir plus. Ma bibliothèque permet de mettre en place des boucles fluides et de passer d'un clip à un autre, c'est très rapide car la vidéo doit être décodée dans un fichier au préalable. Dans votre cas, les 10 clips vidéo seraient décodés en fichiers avant de commencer, mais basculer entre eux serait rapide.

MoDJ
la source
Qu'en est-il de la partie audio de la vidéo? J'ai besoin que la vidéo et l'audio soient synchronisés.
Bernt Habermeier
Oui, l'audio est déjà géré avec une synchronisation très étroite entre la piste audio et le clip vidéo. Voir les exemples de projets xcode. Tout est déjà implémenté, il vous suffit de le télécharger et de l'essayer.
MoDJ
Existe-t-il une possibilité de lire la vidéo en réseau avec AVAnimator?
Richard Topchii
Non, cela fonctionne sur des fichiers locaux, le streaming vidéo en réseau est une chose complètement différente.
MoDJ
0

Sans avoir rien fait de tel dans le passé, en fonction de vos pensées et de vos expériences, j'essaierais une combinaison de 7 et 1: préchargez un AVPlayer avec les premières secondes des 10 vidéos de suivi. Ensuite, le saut sera très probablement plus rapide et plus fiable en raison de moins de données. Pendant que vous jouez le morceau sélectionné, vous avez suffisamment de temps pour préparer l'AVPlayer pour le reste de la vidéo de suivi sélectionnée en arrière-plan. Lorsque le début est terminé, vous passez à l'AVPlayer préparé. Donc, au total, vous avez à tout moment un maximum de 2 AVPlayers chargés.

Bien sûr, je ne sais pas si la commutation peut être effectuée si facilement qu'elle ne perturbe pas la lecture.

(J'aurais ajouté ceci comme commentaire si je pouvais.)

Meilleur, Peter

ilmiacs
la source
Je n'ai trouvé aucun avantage à charger 10 actifs en série sur un AVPlayer. De plus, je ne comprends pas votre suggestion, car je vois les options (1) et (7) qui s'excluent mutuellement. L'option 7 fusionne tous les éléments vidéo en un seul élément - il n'y a donc qu'un seul élément à charger pendant la période. C'est ce que je fais aujourd'hui, et pour ce que ça vaut, j'obtiens un délai d'environ 500 ms sur les temps de démarrage / lecture réels. Il convient de noter que SeekTo se termine plus rapidement que la première image réelle est lue, donc pour les véritables heures de début, je mesure quand la première image est réellement lue via un rappel chronométré.
Bernt Habermeier
Juste pour clarifier mon idée: mon idée était de diviser un actif en deux parties: les premières secondes et le reste. Vous avez maintenant créé deux actifs sur un seul. Les 10 débuts que vous rejoignez et utilisez skip et en jouant le début, vous chargez le reste. Cela comprend alors la suggestion 8 qui s'ajoute à votre liste.
ilmiacs
Mais comme je l'ai compris d'après votre dernier commentaire, vous avez poussé la recherche plus loin, ce qui est bien, et la solution 7 ne fonctionne pas non plus. Il semble que l'AV reste fondamentalement sur votre chemin et la seule façon pour vous d'y aller est probablement d'utiliser la technologie en dessous, à savoir Core Media, pour obtenir plus de contrôle sur vos actifs. Peter.
ilmiacs
Oh, je comprends mieux votre idée maintenant. Merci. Il serait intéressant d'étudier si vous obtenez des temps de recherche rapides pour de courts clips vidéo. Je n'ai pas encore essayé cela, mais cela vaudrait la peine d'y réfléchir. En ce qui concerne l'utilisation des supports de base, je n'ai trouvé aucun bon matériel de référence sur cette API. Avez-vous une bonne ressource vers laquelle vous pouvez m'indiquer?
Bernt Habermeier le
Non désolé. Comme je l'ai dit, je ne suis pas un expert en AV ou Core Media. Lisez simplement votre question et j'ai eu quelques idées sur la façon dont je procéderais personnellement et je voulais les partager. Peter
ilmiacs
0

Si j'ai bien compris votre problème, il semble que vous ayez une vidéo continue sur laquelle vous devez charger la piste audio à tout moment.

Si tel est le cas, je suggère de regarder dans BASS . BASS est une bibliothèque audio similaire à AVPlayer qui vous donne un accès (relativement) facile aux API de bas niveau du framework AudioUnits sous iOS. Qu'est-ce que cela signifie pour vous? Cela signifie qu'avec un tout petit peu de manipulation du tampon (vous n'en aurez peut-être même pas besoin, cela dépend de la taille du délai souhaité), vous pouvez commencer à jouer de la musique instantanément.

Les limitations s'étendent cependant à la vidéo, comme je l'ai dit, il s'agit d'une bibliothèque audio , donc toute manipulation vidéo devra toujours être effectuée avec AVPlayer. Cependant, en utilisant, -seekToTime:toleranfeBefore:toleranceAfter:vous devriez être en mesure d'obtenir une recherche rapide dans la vidéo tant que vous pré-enroulez avec toutes les options nécessaires.

Si vous synchronisez sur plusieurs appareils (ce que votre application pourrait suggérer), laissez simplement un commentaire et je serais heureux de modifier ma réponse.

PS: BASS peut sembler intimidant au début à cause de son format de type C, mais il est vraiment très facile à utiliser pour ce qu'il est.

âne
la source
-2

Voici plusieurs propriétés et méthodes fournies par la classe AVAsset qui peuvent aider:

- (void)_pu_setCachedDuration:(id)arg1;
- (id)pu_cachedDuration; 
- (struct
 { 
   long long x1; 
   int x2;
   unsigned int x3; 
   long long x4; 
})pu_duration;
- (void)pu_loadDurationWithCompletionHandler:(id /* block */)arg1;
James Bush
la source