Jak zmniejszyć opóźnienie uruchamiania aplikacji AVPlayer dla systemu iOS

115

Uwaga na poniższe pytanie: Wszystkie zasoby są lokalne na urządzeniu - nie odbywa się strumieniowe przesyłanie strumieniowe w sieci. Filmy zawierają ścieżki dźwiękowe.

Pracuję nad aplikacją na iOS, która wymaga odtwarzania plików wideo z minimalnym opóźnieniem, aby rozpocząć dany klip wideo. Niestety nie wiemy, jaki konkretny klip wideo jest następny, dopóki nie musimy go faktycznie uruchomić. W szczególności: gdy odtwarzany jest jeden klip wideo, będziemy wiedzieć, jaki jest następny zestaw (w przybliżeniu) 10 klipów wideo, ale nie wiemy dokładnie, który z nich jest dokładnie, dopóki nie nadejdzie czas, aby „natychmiast” odtworzyć następny klip.

Aby spojrzeć na rzeczywiste opóźnienia rozpoczęcia, zadzwoniłem addBoundaryTimeObserverForTimesdo odtwarzacza wideo z okresem jednej milisekundy, aby zobaczyć, kiedy wideo faktycznie zaczęło się odtwarzać, i biorę różnicę tego znacznika czasu z pierwszym miejscem w kod wskazujący, który zasób rozpocząć odtwarzanie.

Z tego, co widziałem do tej pory, stwierdziłem, że użycie kombinacji AVAssetwczytywania, a następnie tworzenia AVPlayerItemz tego, gdy jest gotowy, a następnie czekania AVPlayerStatusReadyToPlayprzed wywołaniem gry, zwykle zajmuje od 1 do 3 sekund, aby rozpocząć spinacz.

Od tego czasu przeszedłem na to, co uważam za mniej więcej równoważne: dzwonienie [AVPlayerItem playerItemWithURL:]i czekanie na AVPlayerItemStatusReadyToPlaygrę. Mniej więcej taka sama wydajność.

Jedną z rzeczy, które obserwuję, jest to, że ładowanie pierwszego elementu AVPlayera jest wolniejsze niż pozostałe. Wydaje się, że jednym z pomysłów jest wykonanie przed lotem AVPlayera z krótkim / pustym zasobem przed próbą odtworzenia pierwszego wideo, co może być dobrą praktyką ogólną. [ Powolny start dla AVAudioPlayer przy pierwszym odtwarzaniu dźwięku

Bardzo chciałbym skrócić czas rozpoczęcia filmu tak bardzo, jak to możliwe i mam kilka pomysłów na eksperymenty, ale chciałbym uzyskać wskazówki od każdego, kto mógłby pomóc.

Aktualizacja: idea 7, poniżej, w stanie zaimplementowanym, daje czasy przełączania około 500 ms. To jest poprawa, ale byłoby miło zrobić to jeszcze szybciej.

Pomysł 1: Użyj N AVPlayers (nie zadziała)

Używając ~ 10 AVPPlayerobiektów i uruchamiaj i zatrzymuj wszystkie ~ 10 klipów, a kiedy już wiemy, który z nich naprawdę potrzebujemy, przełącz się na poprawny i wznów pauzę AVPlayeri zacznij wszystko od nowa w następnym cyklu.

Nie sądzę, żeby to działało, ponieważ przeczytałem, że AVPlayer'sw iOS jest mniej więcej limit 4 aktywnych . Ktoś pytał o to tutaj na StackOverflow i dowiedział się o limicie 4 AVPlayer: szybkie przełączanie między filmami przy użyciu avfoundation

Pomysł 2: Użyj AVQueuePlayer (nie zadziała)

Nie wierzę, że wrzucenie 10 AVPlayerItemsdo AVQueuePlayeri załadowałoby je wszystkie dla płynnego startu. AVQueuePlayerjest kolejką i myślę, że tak naprawdę tylko sprawia, że ​​następny film w kolejce jest gotowy do natychmiastowego odtwarzania. Nie wiem, który z ~ 10 filmów chcemy odtworzyć, dopóki nie nadejdzie czas, aby to rozpocząć. ios-avplayer-video-preloading

Pomysł 3: Załaduj, odtwarzaj i zachowaj AVPlayerItemsw tle (jeszcze nie w 100% - ale nie wygląda dobrze)

Sprawdzam, czy ładowanie i odtwarzanie pierwszej sekundy każdego klipu wideo w tle (wyłączanie wyjścia wideo i audio) przynosi jakiekolwiek korzyści i zachowuje odniesienie do każdego z nich AVPlayerItemoraz kiedy wiemy, który element należy odtworzyć real, zamień ten jeden i zamień działający w tle AVPlayer z aktywnym. Wypłukać i powtórzyć.

Teoria byłaby taka, że ​​ostatnio odtwarzane AVPlayer/AVPlayerItemutwory mogą nadal zawierać pewne przygotowane zasoby, które przyspieszyłyby późniejsze odtwarzanie. Jak dotąd nie widziałem korzyści z tego, ale być może nie mam AVPlayerLayerpoprawnej konfiguracji dla tła. Wątpię, czy to naprawdę poprawi sytuację w porównaniu z tym, co widziałem.

Pomysł 4: Użyj innego formatu pliku - może takiego, który jest szybszy do załadowania?

Obecnie używam formatu .m4v (video-MPEG4) H.264. H.264 ma wiele różnych opcji kodeków, więc możliwe jest, że niektóre opcje będą szybciej wyszukiwane niż inne. Zauważyłem, że użycie bardziej zaawansowanych ustawień, które zmniejszają rozmiar pliku, wydłuża czas wyszukiwania, ale nie znalazłem żadnych opcji, które idą w drugą stronę.

Pomysł 5: Połączenie bezstratnego formatu wideo + AVQueuePlayer

Jeśli istnieje format wideo, który szybko się ładuje, ale być może tam, gdzie rozmiar pliku jest szalony, jednym z pomysłów może być wstępne przygotowanie pierwszych 10 sekund każdego klipu wideo z wersją, która jest rozdęta, ale szybsza do załadowania, ale z powrotem to wszystko z zasobem zakodowanym w H.264. Użyj odtwarzacza AVQueuePlayer i dodaj pierwsze 10 sekund w nieskompresowanym formacie pliku, a następnie w formacie H.264, który uzyskuje do 10 sekund czasu przygotowania / wstępnego ładowania. Dostałbym więc „to, co najlepsze” z obu światów: szybki czas uruchamiania, ale także korzyści wynikające z bardziej kompaktowego formatu.

Pomysł 6: Użyj niestandardowego odtwarzacza AVPlayer / napisz własny / użyj cudzego

Biorąc pod uwagę moje potrzeby, być może nie mogę korzystać z AVPlayera, ale muszę uciekać się do AVAssetReader i dekodować pierwsze kilka sekund (ewentualnie zapisać plik raw na dysk), a jeśli chodzi o odtwarzanie, użyć formatu raw do jego odtworzenia z powrotem szybko. Wydaje mi się, że jest to ogromny projekt, a jeśli podejdę do tego naiwnie, to jest niejasne / mało prawdopodobne, żebym nawet lepiej działał. Każda zdekodowana i nieskompresowana klatka wideo zajmuje 2,25 MB. Mówiąc naiwnie - jeśli zdecydujemy się na ~ 30 fps dla wideo, skończymy z wymaganiem odczytu z dysku ~ 60 MB / s, co prawdopodobnie jest niemożliwe / pchanie go. Oczywiście musielibyśmy zastosować pewien poziom kompresji obrazu (być może natywne formaty kompresji openGL / es przez PVRTC) ... ale to trochę szalone. Może jest tam biblioteka, z której mogę skorzystać?

Pomysł 7: Połącz wszystko w jeden zasób filmu i seekToTime

Jednym z pomysłów, który może być łatwiejszy niż niektóre z powyższych, jest połączenie wszystkiego w jeden film i użycie seekToTime. Chodzi o to, że skakalibyśmy po całym miejscu. Zasadniczo swobodny dostęp do filmu. Myślę, że to może naprawdę działać dobrze: avplayer-movie-playing-lag-in-ios5

Jak myślisz, które podejście byłoby najlepsze? Jak dotąd nie poczyniłem tak dużych postępów w zakresie zmniejszania opóźnienia.

Bernt Habermeier
źródło
Jeśli chodzi o to, co jest warte, wybieram Idea 7. Jest nadal wolna, ale nie tak nieprzewidywalnie powolna, jak inne opcje. Następne pytanie, które mam, to - czy opcje kodeka, rozdzielczość i częstotliwość klatek kluczowych mają wpływ na taktowanie seekto?
Bernt Habermeier,
Spóźniony do gry, ale warto przełączać filmy tak szybko, jak to możliwe (tj. Natychmiast po rozpoczęciu odtwarzania nowego) i sprofilować aplikację, aby zobaczyć, gdzie spędza większość czasu procesora.
tc.
1
Prawie rok później, czego się o tym dowiedziałeś?
lnafziger
1
Poszedłem z opcją 7 i dotarłem do miejsca między 300 ms a 500 ms. Jedną z rzeczy, które odkryłem, jest to, że im bardziej wyszukane opcje kodeka mp4, tym wolniejsza funkcja seekTo. Istnieje kilka opcji kompresji wideo, które pozwalają na lepszą kompresję i utrzymują jakość wideo, ale zabijają czas dekodowania.
Bernt Habermeier
2
Jest to rozsądnie brzmiące żądanie, ale tak naprawdę, aby wdrożyć opcję 7, implementacja ma zbyt wiele aspektów, aby je opublikować. Rozważ: (a) Utwórz łańcuch narzędzi do scalania zasobów wideo, (b) upewnij się, że śledzisz przesunięcia segmentów wideo, (c) przesunięcia wyszukiwania, gdy przychodzi żądanie odtworzenia określonego klipu, (d) użyj addPeriodicTimeObserverForInterval, aby sprawdzić jeśli wypadniesz z klipu wideo i zareagujesz odpowiednio (użyj tej metody w porównaniu z pojedynczym addBoundaryTimeObserverForTimes, ponieważ zauważyłem, że ten ostatni czasami nie uruchamia się ... ogólnie rzecz biorąc, nie nadaje się to do wklejania kodu.
Bernt Habermeier

Odpowiedzi:

4

Dla iOS 10.xi nowszych, aby zmniejszyć opóźnienie startu AVPlayera, ustawiłem: avplayer.automaticallyWaitsToMinimizeStalling = false; i wydawało się, że to rozwiązało problem. Może to mieć inne konsekwencje, ale jeszcze ich nie uderzyłem.

Pomysł na to wziąłem z: https://stackoverflow.com/a/50598525/9620547

grizzb
źródło
Dostałem 6-7 sekund redukcji opóźnienia. Ale mam pytanie, czy wpłynie to na wydajność aplikacji?
kalpa
@kalpa Używamy tego kodu w aplikacji produkcyjnej od ponad roku bez żadnych zauważalnych negatywnych skutków. Przeważnie odtwarzamy pliki audio od 20 do 60 minut słuchaczom w USA, gdzie zasięg danych mobilnych jest ogólnie szybki. Twój przypadek użycia może być jednak inny.
grizzb
Dzięki za informację. @grizzb
kalpa
1

Zasób może nie być gotowy po utworzeniu, może wykonywać obliczenia, takie jak czas trwania filmu, upewnij się, że zawiera wszystkie metadane filmu w pliku.

nova
źródło
1

Powinieneś najpierw wypróbować opcję # 7, aby zobaczyć, czy możesz to zadziałać. Podejrzewam, że w rzeczywistości nie będzie to działać dla twoich potrzeb, ponieważ czas wyszukiwania prawdopodobnie nie będzie wystarczająco szybki, aby zapewnić płynne przełączanie między klipami. Jeśli spróbujesz tego i to się nie powiedzie, radziłbym wykonać opcję 4/6 i spojrzeć na moją bibliotekę iOS zaprojektowaną specjalnie do tego celu, po prostu zrób szybkie wyszukiwanie w Google na AVAnimatorze, aby dowiedzieć się więcej. Moja biblioteka umożliwia wdrażanie płynnych pętli i przełączanie się z jednego klipu na inny, jest to bardzo szybkie, ponieważ wideo musi zostać wcześniej zdekodowane do pliku. W twoim przypadku wszystkie 10 klipów wideo zostanie zdekodowanych do plików przed rozpoczęciem, ale przełączanie między nimi byłoby szybkie.

MoDJ
źródło
A co z dźwiękową częścią wideo? Potrzebuję synchronizacji obrazu i dźwięku.
Bernt Habermeier,
Tak, dźwięk jest już obsługiwany z bardzo ścisłą synchronizacją między ścieżką audio a klipem wideo. Zobacz przykładowe projekty xcode. Jest już zaimplementowany, wystarczy go pobrać i wypróbować.
MoDJ,
Czy istnieje możliwość odtwarzania wideo sieciowego za pomocą programu AVAnimator?
Richard Topchii,
Nie, działa na plikach lokalnych, strumieniowe przesyłanie wideo w sieci to zupełnie inna sprawa.
MoDJ,
0

Bez zrobienia czegoś takiego w przeszłości, w oparciu o twoje przemyślenia i doświadczenia, spróbuję kombinacji 7 i 1: Wstępnie załaduj jeden AVPlayer z pierwszymi kilkoma sekundami z 10 kolejnych filmów. W takim przypadku pomijanie będzie prawdopodobnie szybsze i bardziej niezawodne ze względu na mniej danych. Podczas odtwarzania wybranego utworu masz wystarczająco dużo czasu, aby przygotować AVPlayer do reszty wybranego wideo uzupełniającego w tle. Gdy początek się zakończy, przełączasz się na przygotowany AVPlayer. W sumie możesz w dowolnym momencie załadować maksymalnie 2 odtwarzacze AVPlayers.

Oczywiście nie wiem, czy przełączanie da się zrobić tak płynnie, żeby nie przeszkadzało w odtwarzaniu.

(Dodałbym to jako komentarz, gdybym mógł.)

Najlepsze, Peter

ilmiacs
źródło
Nie znalazłem żadnej korzyści z ładowania 10 zasobów w serii na jednym odtwarzaczu AVPlayer. Co więcej, nie rozumiem twojej sugestii, ponieważ widzę opcje (1) i (7) wzajemnie się wykluczające. Opcja 7 łączy wszystkie zasoby wideo w jeden zasób - w ten sposób jest tylko jeden zasób do załadowania. To właśnie robię dzisiaj i co jest tego warte, dostaję około 500 ms opóźnienia w rzeczywistych czasach rozpoczęcia / odtwarzania. Warto zauważyć, że SeekTo kończy się szybciej niż rzeczywista pierwsza klatka jest odtwarzana, więc dla prawdziwych czasów rozpoczęcia mierzę, kiedy pierwsza klatka jest faktycznie odtwarzana za pomocą wywołania zwrotnego w czasie.
Bernt Habermeier
Żeby wyjaśnić mój pomysł: moim pomysłem było podzielenie zasobu na dwie części: kilka pierwszych sekund i resztę. Teraz stworzyłeś dwa aktywa z jednego. 10 początków, do których dołączysz i skorzystasz z pomijania, a grając na początku, załadujesz resztę. To następnie zawiera sugestię 8, która dodaje do twojej listy.
ilmiacs
Ale jak rozumiem z twojego ostatniego komentarza, w międzyczasie popchnąłeś badania dalej, co jest dobre, a rozwiązanie 7 również nie działa. Wygląda na to, że AV zasadniczo stoi ci na drodze, a jedynym sposobem na to jest prawdopodobnie użycie technologii, która jest pod spodem, czyli Core Media, aby uzyskać większą kontrolę nad swoimi zasobami. Piotr.
ilmiacs
Och, teraz lepiej rozumiem twój pomysł. Dzięki. Byłoby interesujące zbadanie, czy można uzyskać szybkie czasy wyszukiwania krótkich klipów wideo. Jeszcze tego nie próbowałem, ale warto byłoby to rozważyć. Odnośnie korzystania z podstawowych mediów - nie znalazłem żadnego dobrego materiału referencyjnego na temat tego API. Czy masz dobre źródło informacji, na które możesz mi wskazać?
Bernt Habermeier
Nie, przykro mi. Jak powiedziałem, nie jestem ekspertem w dziedzinie AV ani Core Media. Po prostu przeczytaj swoje pytanie i mam kilka pomysłów, jak osobiście postąpię, i chciałem się nimi podzielić. Peter
ilmiacs
0

Jeśli dobrze zrozumiałem Twój problem, wydaje się, że masz jedno ciągłe wideo, do którego musisz w każdej chwili załadować ścieżkę dźwiękową.

Jeśli tak jest, proponuję przyjrzeć się basowi . BASS to biblioteka audio podobna do AVPlayer, która zapewnia (względnie) łatwy dostęp do niskopoziomowych interfejsów API struktury AudioUnits w systemie iOS. Co to oznacza dla Ciebie? Oznacza to, że przy odrobinie manipulacji buforem (możesz go nawet nie potrzebować, zależy od tego, jak małe chcesz opóźnienie), możesz natychmiast rozpocząć odtwarzanie muzyki.

Ograniczenia dotyczą jednak wideo, jak powiedziałem, jest to biblioteka audio, więc wszelkie manipulacje wideo będą nadal musiały być wykonywane za pomocą AVPlayera. Jednak użycie -seekToTime:toleranfeBefore:toleranceAfter:powinno być w stanie osiągnąć szybkie wyszukiwanie w filmie, o ile masz przed filmem wszystkie niezbędne opcje.

Jeśli synchronizujesz na wielu urządzeniach (co może sugerować Twoja aplikacja), po prostu zostaw komentarz, a ja z przyjemnością zmienię moją odpowiedź.

PS: BASS może na początku wyglądać zniechęcająco ze względu na format podobny do C, ale jest naprawdę łatwy w użyciu, jak na to, czym jest.

osioł
źródło
-2

Oto kilka właściwości i metod udostępnianych przez klasę AVAsset, które mogą pomóc:

- (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
źródło