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 addBoundaryTimeObserverForTimes
do 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 AVAsset
wczytywania, a następnie tworzenia AVPlayerItem
z tego, gdy jest gotowy, a następnie czekania AVPlayerStatusReadyToPlay
przed 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 AVPlayerItemStatusReadyToPlay
grę. 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 AVPPlayer
obiektó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ę AVPlayer
i zacznij wszystko od nowa w następnym cyklu.
Nie sądzę, żeby to działało, ponieważ przeczytałem, że AVPlayer's
w 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 AVPlayerItems
do AVQueuePlayer
i załadowałoby je wszystkie dla płynnego startu. AVQueuePlayer
jest 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 AVPlayerItems
w 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 AVPlayerItem
oraz 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/AVPlayerItem
utwory 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 AVPlayerLayer
poprawnej 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.
Odpowiedzi:
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
źródło
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.
źródło
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.
źródło
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
źródło
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.
źródło
Oto kilka właściwości i metod udostępnianych przez klasę AVAsset, które mogą pomóc:
źródło