Rozwiązywanie problemów


Naprawianie błędów „Cleartext HTTP traffic not permitted”

Ten błąd wystąpi, jeśli aplikacja zażąda ruchu HTTP w formie nieszyfrowanej (czyli http:// zamiast https://), a jej konfiguracja bezpieczeństwa sieci na to nie zezwala. Jeśli Twoja aplikacja jest kierowana na Androida 9 (API na poziomie 28) lub nowszego, domyślna konfiguracja wyłącza ruch HTTP w postaci zwykłego tekstu.

Jeśli aplikacja musi obsługiwać ruch HTTP w postaci tekstu nieszyfrowanego, musisz użyć konfiguracji bezpieczeństwa sieci, która na to zezwala. Szczegółowe informacje znajdziesz w dokumentacji dotyczącej bezpieczeństwa sieci na Androidzie. Aby włączyć cały ruch HTTP w formie zwykłego tekstu, wystarczy dodać android:usesCleartextTraffic="true" do elementu application w pliku AndroidManifest.xml aplikacji.

Aplikacja demonstracyjna ExoPlayer korzysta z domyślnych ustawień bezpieczeństwa sieci, więc nie dopuszcza ruchu HTTP w formie zwykłego tekstu. Możesz ją włączyć, postępując zgodnie z instrukcjami powyżej.

Naprawianie błędów „SSLHandshakeException”, „CertPathValidatorException” i „ERR_CERT_AUTHORITY_INVALID”

Kody SSLHandshakeException, CertPathValidatorExceptionERR_CERT_AUTHORITY_INVALID wskazują na problem z certyfikatem SSL serwera. Te błędy nie są specyficzne dla ExoPlayera. Więcej informacji znajdziesz w dokumentacji SSL na Androidzie.

Dlaczego niektórych plików multimedialnych nie można przewijać?

Domyślnie ExoPlayer nie obsługuje przewijania w przypadku multimediów, w których jedyną metodą dokładnego przewijania jest skanowanie i indeksowanie całego pliku przez odtwarzacz. ExoPlayer uznaje takie pliki za nieprzewijalne. Większość nowoczesnych formatów kontenerów multimedialnych zawiera metadane do wyszukiwania (np. indeks próbek), ma dobrze zdefiniowany algorytm wyszukiwania (np. interpolacyjne wyszukiwanie metodą połowienia w przypadku formatu Ogg) lub wskazuje, że ich zawartość ma stałą szybkość transmisji. W tych przypadkach ExoPlayer obsługuje wydajne operacje wyszukiwania.

Jeśli chcesz korzystać z wyszukiwania, ale masz treści, w których nie można tego robić, zalecamy przekonwertowanie ich na format kontenera, który lepiej się do tego nadaje. W przypadku plików MP3, ADTS i AMR możesz też włączyć wyszukiwanie, zakładając, że pliki mają stałą szybkość transmisji, zgodnie z opisem tutaj.

Dlaczego w niektórych plikach MP3 wyszukiwanie jest niedokładne?

Pliki MP3 o zmiennej szybkości transmisji bitów (VBR) zasadniczo nie nadają się do przypadków użycia, które wymagają dokładnego wyszukiwania. Może to wynikać z 2 przyczyn:

  1. W przypadku precyzyjnego przewijania format kontenera powinien zawierać w nagłówku dokładne mapowanie czasu na bajty. Mapowanie umożliwia graczowi przypisanie żądanego czasu wyszukiwania do odpowiedniego przesunięcia bajtowego i rozpoczęcie żądania, analizowania i odtwarzania multimediów od tego przesunięcia. Nagłówki dostępne do określania tego mapowania w MP3 (np. nagłówki XING) są niestety często niedokładne.
  2. W przypadku formatów kontenerów, które nie zapewniają dokładnego mapowania czasu na bajty (lub nie zapewniają żadnego mapowania czasu na bajty), nadal można wykonać dokładne wyszukiwanie, jeśli kontener zawiera w strumieniu bezwzględne sygnatury czasowe próbek. W takim przypadku odtwarzacz może przypisać czas wyszukiwania do najbardziej prawdopodobnego przesunięcia bajtów, rozpocząć wysyłanie żądań dotyczących multimediów od tego przesunięcia, przeanalizować pierwszą bezwzględną sygnaturę czasową próbki i przeprowadzić wyszukiwanie binarne w multimediach, aż znajdzie odpowiednią próbkę. Niestety format MP3 nie zawiera w strumieniu bezwzględnych sygnatur czasowych próbek, więc to podejście nie jest możliwe.

Z tych powodów jedynym sposobem na dokładne wyszukanie w pliku MP3 o zmiennej szybkości transmisji jest przeskanowanie całego pliku i ręczne utworzenie w odtwarzaczu mapowania czasu na bajty. Tę strategię można włączyć za pomocą parametru FLAG_ENABLE_INDEX_SEEKING, który można ustawić w DefaultExtractorsFactory za pomocą parametru setMp3ExtractorFlags. Pamiętaj, że nie sprawdza się to w przypadku dużych plików MP3, zwłaszcza jeśli użytkownik próbuje przewinąć strumień do końca krótko po rozpoczęciu odtwarzania. Wymaga to od odtwarzacza poczekania, aż pobierze i zindeksuje cały strumień, zanim wykona przewijanie. W ExoPlayerze w tym przypadku zdecydowaliśmy się zoptymalizować szybkość kosztem dokładności, dlatego FLAG_ENABLE_INDEX_SEEKING jest domyślnie wyłączony.

Jeśli masz kontrolę nad odtwarzanymi multimediami, zdecydowanie zalecamy użycie bardziej odpowiedniego formatu kontenera, takiego jak MP4. Nie znamy żadnych przypadków użycia, w których MP3 byłby najlepszym wyborem formatu multimediów.

Dlaczego przewijanie filmu jest powolne?

Gdy odtwarzacz przechodzi do nowej pozycji odtwarzania w filmie, musi wykonać 2 czynności:

  1. Załaduj do bufora dane odpowiadające nowej pozycji odtwarzania (może to nie być konieczne, jeśli dane są już w buforze).
  2. Wyczyść dekoder wideo i zacznij dekodowanie od klatki I (klatki kluczowej) przed nową pozycją odtwarzania ze względu na kodowanie wewnątrzklatkowe używane przez większość formatów kompresji wideo. Aby zapewnić dokładność przewijania (tzn. odtwarzanie rozpoczyna się dokładnie w miejscu przewijania), wszystkie klatki między poprzednią klatką I a miejscem przewijania muszą zostać zdekodowane i natychmiast odrzucone (bez wyświetlania na ekranie).

Opóźnienie spowodowane przez (1) można zmniejszyć, zwiększając ilość danych buforowanych w pamięci przez odtwarzacz lub wstępnie buforując dane na dysku.

Opóźnienie wprowadzone przez (2) można zmniejszyć, obniżając dokładność wyszukiwania za pomocą ExoPlayer.setSeekParameters lub ponownie kodując film, aby częściej zawierał klatki I (co spowoduje większy plik wyjściowy).

Dlaczego niektórych plików MPEG-TS nie można odtworzyć?

Niektóre pliki MPEG-TS nie zawierają ograniczników jednostek dostępu (AUD). Domyślnie ExoPlayer korzysta z jednostek dostępu do dźwięku, aby tanio wykrywać granice klatek. Podobnie niektóre pliki MPEG-TS nie zawierają klatek kluczowych IDR. Domyślnie są to jedyne typy klatek kluczowych, które są brane pod uwagę przez ExoPlayer.

ExoPlayer będzie sprawiał wrażenie, że utknął w stanie buforowania, gdy zostanie poproszony o odtwarzanie pliku MPEG-TS, w którym brakuje jednostek dostępu do dźwięku lub kluczowych klatek IDR. Jeśli chcesz odtworzyć takie pliki, możesz to zrobić za pomocą FLAG_DETECT_ACCESS_UNITSFLAG_ALLOW_NON_IDR_KEYFRAMES. Te flagi można ustawić w DefaultExtractorsFactory za pomocą setTsExtractorFlags lub w DefaultHlsExtractorFactory za pomocą konstruktora. Użycie FLAG_DETECT_ACCESS_UNITS nie ma żadnych efektów ubocznych poza tym, że jest kosztowne obliczeniowo w porównaniu z wykrywaniem granic klatek na podstawie AUD. Użycie FLAG_ALLOW_NON_IDR_KEYFRAMES może spowodować tymczasowe uszkodzenie obrazu na początku odtwarzania i bezpośrednio po przewinięciu niektórych plików MPEG-TS.

Dlaczego w niektórych plikach MPEG-TS nie ma napisów?

Niektóre pliki MPEG-TS zawierają ścieżki CEA-608, ale nie są one zadeklarowane w metadanych kontenera, więc ExoPlayer nie może ich wykryć. Możesz ręcznie określić dowolne ścieżki napisów, podając listę oczekiwanych formatów napisów w DefaultExtractorsFactory, w tym kanały ułatwień dostępu, które można wykorzystać do ich identyfikacji w strumieniu MPEG-TS:

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory()
    .setTsSubtitleFormats(
      listOf(
        Format.Builder()
          .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
          .setAccessibilityChannel(accessibilityChannel)
          // Set other subtitle format info, such as language.
          .build()
      )
    )
val player: Player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, extractorsFactory)).build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory()
        .setTsSubtitleFormats(
            ImmutableList.of(
                new Format.Builder()
                    .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
                    .setAccessibilityChannel(accessibilityChannel)
                    // Set other subtitle format info, such as language.
                    .build()));
Player player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

Dlaczego niektóre pliki MP4/FMP4 są odtwarzane nieprawidłowo?

Niektóre pliki MP4/FMP4 zawierają listy edycji, które zmieniają oś czasu multimediów, pomijając, przenosząc lub powtarzając listy próbek. ExoPlayer częściowo obsługuje stosowanie list edycji. Może na przykład opóźniać lub powtarzać grupy próbek rozpoczynające się od próbki synchronizacji, ale nie obcina próbek audio ani materiałów przed odtworzeniem w przypadku edycji, które nie rozpoczynają się od próbki synchronizacji.

Jeśli widzisz, że część multimediów jest nieoczekiwanie pominięta lub powtórzona, spróbuj ustawić Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS lub FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS, co spowoduje, że ekstraktor całkowicie zignoruje listy edycji. Można je ustawić naDefaultExtractorsFactory za pomocą setMp4ExtractorFlags lub setFragmentedMp4ExtractorFlags.

Dlaczego niektóre strumienie kończą się niepowodzeniem z kodem odpowiedzi HTTP 301 lub 302?

Kody odpowiedzi HTTP 301 i 302 wskazują przekierowanie. Krótkie opisy znajdziesz na Wikipedii. Gdy ExoPlayer wysyła żądanie i otrzymuje odpowiedź z kodem stanu 301 lub 302, zwykle postępuje zgodnie z przekierowaniem i rozpoczyna odtwarzanie w normalny sposób. Jedynym przypadkiem, w którym nie dzieje się to domyślnie, są przekierowania między protokołami. Przekierowanie międzyprotokołowe to przekierowanie z HTTPS do HTTP lub odwrotnie (lub rzadziej między inną parą protokołów). Aby sprawdzić, czy adres URL powoduje przekierowanie między protokołami, użyj narzędzia wiersza poleceń wget w ten sposób:

wget "https://yourserver.example.com/test.mp3" 2>&1  | grep Location

Dane wyjściowe powinny wyglądać mniej więcej tak:

Location: https://secondserver.example.net/test.mp3 [following]
Location: http://thirdserver.example.org/test.mp3 [following]

W tym przykładzie występują 2 przekierowania. Pierwsze przekierowanie jest z https://yourserver.example.com/test.mp3 na https://secondserver.example.net/test.mp3. Oba adresy URL korzystają z protokołu HTTPS, więc nie jest to przekierowanie między protokołami. Drugie przekierowanie jest z https://secondserver.example.net/test.mp3 do http://thirdserver.example.org/test.mp3. Przekierowuje z HTTPS do HTTP, więc jest to przekierowanie międzyprotokołowe. W domyślnej konfiguracji ExoPlayer nie będzie podążać za tym przekierowaniem, co oznacza, że odtwarzanie się nie powiedzie.

W razie potrzeby możesz skonfigurować ExoPlayer tak, aby podczas tworzenia instancji DefaultHttpDataSource.Factory używanych w aplikacji śledził przekierowania między protokołami. Więcej informacji o wybieraniu i konfigurowaniu stosu sieciowego znajdziesz tutaj.

Dlaczego niektóre strumienie kończą się niepowodzeniem z błędem UnrecognizedInputFormatException?

To pytanie dotyczy błędów odtwarzania w przypadku następującego formularza:

UnrecognizedInputFormatException: None of the available extractors
(MatroskaExtractor, FragmentedMp4Extractor, ...) could read the stream.

Możliwe są 2 przyczyny tego błędu. Najczęstszą przyczyną jest próba odtworzenia treści DASH (mpd), HLS (m3u8) lub SmoothStreaming (ism, isml), ale odtwarzacz próbuje odtworzyć je jako strumień progresywny. Aby odtwarzać takie strumienie, musisz korzystać z odpowiedniego modułu ExoPlayera. Jeśli identyfikator URI strumienia nie kończy się standardowym rozszerzeniem pliku, możesz też przekazać MimeTypes.APPLICATION_MPD, MimeTypes.APPLICATION_M3U8 lub MimeTypes.APPLICATION_SS do setMimeTypeMediaItem.Builder, aby wyraźnie określić typ strumienia.

Drugą, rzadszą przyczyną jest to, że ExoPlayer nie obsługuje formatu kontenera multimediów, które próbujesz odtworzyć. W tym przypadku błąd działa zgodnie z założeniami, ale możesz przesłać prośbę o dodanie funkcji do naszego systemu śledzenia problemów, podając szczegóły formatu kontenera i strumień testowy. Zanim prześlesz nową prośbę, wyszukaj istniejące prośby o funkcje.

Dlaczego funkcja setPlaybackParameters nie działa prawidłowo na niektórych urządzeniach?

Podczas uruchamiania wersji debugowania aplikacji na Androidzie M lub starszym możesz zauważyć niestabilną wydajność, słyszalne artefakty i wysokie wykorzystanie procesora podczas korzystania z interfejsu setPlaybackParameters API. Dzieje się tak, ponieważ optymalizacja, która jest ważna dla tego interfejsu API, jest wyłączona w przypadku kompilacji debugowania działających na tych wersjach Androida.

Pamiętaj, że ten problem dotyczy tylko wersji debugowania. Nie ma to wpływu na wersje produkcyjne, w przypadku których optymalizacja jest zawsze włączona. Dlatego wydania udostępniane użytkownikom nie powinny być dotknięte tym problemem.

Co oznaczają błędy „Player is accessed on the wrong thread”?

Zapoznaj się z sekcją Uwaga na temat wątków na stronie dla początkujących.

Jak rozwiązać problem „Unexpected status line: ICY 200 OK”?

Ten problem może wystąpić, jeśli odpowiedź serwera zawiera wiersz stanu ICY zamiast wiersza zgodnego z protokołem HTTP. Linie stanu ICY są przestarzałe i nie należy ich używać. Jeśli masz kontrolę nad serwerem, zaktualizuj go, aby wysyłał odpowiedź zgodną z HTTP. Jeśli nie możesz tego zrobić, problem rozwiąże użycie biblioteki ExoPlayer OkHttp, ponieważ prawidłowo obsługuje ona wiersze stanu ICY.

Jak mogę sprawdzić, czy odtwarzany strumień jest transmisją na żywo?

Możesz wysłać zapytanie o metodę isCurrentWindowLive gracza. Możesz też sprawdzić isCurrentWindowDynamic, aby dowiedzieć się, czy okno jest dynamiczne (czyli nadal aktualizowane z upływem czasu).

Jak sprawić, aby dźwięk był odtwarzany, gdy aplikacja działa w tle?

Aby zapewnić ciągłe odtwarzanie dźwięku, gdy aplikacja działa w tle, wykonaj te czynności:

  1. Musisz mieć działającą usługę na pierwszym planie. Zapobiega to zamknięciu procesu przez system w celu zwolnienia zasobów.
  2. Musisz mieć WifiLockWakeLock. Dzięki temu system utrzymuje aktywność modułu Wi-Fi i procesora. Można to łatwo zrobić, używając ExoPlayer przez wywołanie setWakeMode, które automatycznie uzyska i zwolni wymagane blokady we właściwym czasie.

Ważne jest, aby zwolnić blokady (jeśli nie używasz setWakeMode) i zatrzymać usługę, gdy tylko odtwarzanie dźwięku zostanie zakończone.

Dlaczego ExoPlayer obsługuje moje treści, a biblioteka ExoPlayer Cast nie?

Możliwe, że treści, które próbujesz odtworzyć, nie są obsługiwane przez CORS. Platforma Cast wymaga, aby treści były obsługiwane przez CORS, aby można było je odtwarzać.

Dlaczego nie można odtworzyć treści, ale nie pojawia się żaden błąd?

Możliwe, że urządzenie, na którym odtwarzasz treści, nie obsługuje określonego formatu próbki multimediów. Możesz to łatwo sprawdzić, dodając EventLogger jako odbiorcę do odtwarzacza i szukając w Logcat wiersza podobnego do tego:

[ ] Track:x, id=x, mimeType=mime/type, ... , supported=NO_UNSUPPORTED_TYPE

NO_UNSUPPORTED_TYPE oznacza, że urządzenie nie może zdekodować formatu próbki multimediów określonego przez mimeType. Informacje o obsługiwanych formatach próbek znajdziesz w dokumentacji formatów multimediów na Androida. Może Ci się też przydać artykuł Jak załadować bibliotekę dekodowania i używać jej do odtwarzania?

Jak mogę załadować bibliotekę dekodowania i użyć jej do odtwarzania?

  • Większość bibliotek dekoderów wymaga ręcznego sprawdzenia i skompilowania zależności, więc upewnij się, że wykonano czynności opisane w pliku README odpowiedniej biblioteki. Na przykład w przypadku biblioteki ExoPlayer FFmpeg należy postępować zgodnie z instrukcjami w pliku libraries/decoder_ffmpeg/README.md, w tym przekazywać flagi konfiguracji, aby włączyć dekodery dla wszystkich formatów, które chcesz odtwarzać.
  • W przypadku bibliotek z kodem natywnym upewnij się, że używasz prawidłowej wersji Android NDK określonej w pliku README, i zwróć uwagę na błędy, które pojawiają się podczas konfiguracji i kompilacji. Po wykonaniu czynności opisanych w pliku README w podkatalogu libs ścieżki biblioteki powinny pojawić się pliki .so dla każdej obsługiwanej architektury.
  • Aby wypróbować odtwarzanie za pomocą biblioteki w aplikacji demonstracyjnej, zapoznaj się z informacjami o włączaniu dołączonych dekoderów. Instrukcje korzystania z biblioteki w aplikacji znajdziesz w pliku README.
  • Jeśli używasz DefaultRenderersFactory, po wczytaniu dekodera w Logcat powinien pojawić się wiersz dziennika na poziomie informacji, np. „Loaded FfmpegAudioRenderer”. Jeśli go brakuje, sprawdź, czy aplikacja jest zależna od biblioteki dekodującej.
  • Jeśli w Logcat zobaczysz logi na poziomie ostrzeżenia z LibraryLoader, oznacza to, że nie udało się wczytać natywnego komponentu biblioteki. Jeśli tak się stanie, sprawdź, czy instrukcje w pliku README biblioteki zostały wykonane prawidłowo i czy podczas ich wykonywania nie wystąpiły żadne błędy.

Jeśli nadal masz problemy z korzystaniem z bibliotek dekodowania, sprawdź tracker problemów Media3, aby znaleźć odpowiednie ostatnie problemy. Jeśli chcesz zgłosić nowy problem związany z tworzeniem natywnej części biblioteki, dołącz pełne dane wyjściowe wiersza poleceń po wykonaniu instrukcji z pliku README. Pomoże nam to zdiagnozować problem.

Czy mogę odtwarzać filmy z YouTube bezpośrednio w ExoPlayerze?

Nie, ExoPlayer nie może odtwarzać filmów z YouTube, np. adresów URL w formacie https://www.youtube.com/watch?v=.... Zamiast tego używaj interfejsu API YouTube iFrame Player, który jest oficjalnym sposobem odtwarzania filmów w YouTube na urządzeniach z Androidem.

Odtwarzanie filmu jest przerywane

Urządzenie może nie być w stanie wystarczająco szybko dekodować treści, jeśli np. szybkość transmisji bitów lub rozdzielczość treści przekracza możliwości urządzenia. Aby uzyskać dobrą wydajność na takich urządzeniach, może być konieczne używanie treści o niższej jakości.

Jeśli na urządzeniu z Androidem w wersji od 6.0 (poziom interfejsu API 23) do 11 (poziom interfejsu API 30) występuje zacinanie się filmów, zwłaszcza podczas odtwarzania treści chronionych DRM lub treści o wysokiej liczbie klatek na sekundę, możesz spróbować włączyć asynchroniczne kolejkowanie buforów.

Niestabilne błędy lint interfejsu API

Media3 gwarantuje zgodność binarną w przypadku części interfejsu API. Części, które nie gwarantują zgodności binarnej, są oznaczone symbolem @UnstableApi. Aby wyraźnie zaznaczyć to rozróżnienie, użycie niestabilnych symboli interfejsu API generuje błąd lint, chyba że są one oznaczone adnotacją @OptIn.

Adnotacja @UnstableApi nie oznacza niczego w kwestii jakości ani wydajności interfejsu API, a jedynie to, że nie jest on „zamrożony”.

Błędy lintowania niestabilnego interfejsu API możesz rozwiązać na 2 sposoby:

  • Przejdź na stabilny interfejs API, który daje ten sam wynik.
  • Dalej używaj niestabilnego interfejsu API i oznaczaj jego użycie symbolem @OptIn, jak pokazano poniżej.
Dodaj adnotację @OptIn

Android Studio może Ci pomóc w dodaniu adnotacji:

Zrzut ekranu: jak dodać adnotację Optin
Rysunek 2. Dodawanie adnotacji @androidx.annotations.OptIn w Android Studio.

Możesz też ręcznie dodawać adnotacje do konkretnych miejsc użycia w języku Kotlin:

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

@OptIn(UnstableApi::class)
fun functionUsingUnstableApi() { ... }

A także w języku Java:

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

@OptIn(markerClass = UnstableApi.class)
private void methodUsingUnstableApis() { ... }

Możesz włączyć całe pakiety, dodając plik package-info.java:

@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

Całe projekty można włączyć, pomijając konkretny błąd lint w lint.xml:

 <?xml version="1.0" encoding="utf-8"?>
 <lint>
   <issue id="UnsafeOptInUsageError">
     <option name="opt-in" value="androidx.media3.common.util.UnstableApi" />
   </issue>
 </lint>

Istnieje też adnotacja kotlin.OptIn, której nie należy używać. Ważne jest, aby używać adnotacji androidx.annotation.OptIn.