Dostosowanie

Podstawą biblioteki ExoPlayer jest interfejs Player. Player udostępnia tradycyjne funkcje odtwarzacza wysokiego poziomu, takie jak buforowania, odtwarzania, wstrzymywania i przewijania. Domyślna implementacja ExoPlayer to ma na celu wyciąganie nielicznych założeń (i z tego powodu niewiele ograniczeń) typu odtwarzanych multimediów, sposobu, miejsca ich przechowywania i sposobu ich przechowywania. wyrenderowano. Zamiast bezpośrednio wdrażać i renderować multimedia, Implementacje ExoPlayer przekazują tę pracę komponentom, które są wstrzykiwane po utworzeniu odtwarzacza lub przekazaniu do niego nowych źródeł multimediów. Komponenty wspólne we wszystkich implementacjach ExoPlayer to:

  • MediaSource instancji, które definiują multimedia, które mają być odtwarzane, ładują multimedia i z którego można odczytać załadowane multimedia. Utworzenie instancji MediaSource z elementu MediaItem przez MediaSource.Factory w odtwarzaczu. Mogą też mogą być przekazywane bezpośrednio do odtwarzacza za pomocą interfejsu API playlisty opartej na źródle multimediów.
  • Wystąpienia MediaSource.Factory, które konwertują MediaItem na MediaSource. Pole MediaSource.Factory jest wstrzykiwane podczas tworzenia odtwarzacza.
  • Renderer instancje, które renderują poszczególne komponenty mediów. Są to jest wstrzykiwany podczas tworzenia odtwarzacza.
  • Element TrackSelector, który wybiera ścieżki udostępnione przez usługę MediaSource jako zużywanych przez każde dostępne Renderer. Wstrzyknięto: TrackSelector po utworzeniu odtwarzacza.
  • LoadControl, który określa, kiedy MediaSource buforuje więcej multimediów, ile multimediów jest buforowanych. Pole LoadControl jest wstrzykiwane, gdy odtwarzacz jest Utworzono.
  • LivePlaybackSpeedControl, który steruje szybkością odtwarzania podczas transmisji na żywo odtworzenia, aby odtwarzacz mógł pozostać jak najbliżej skonfigurowanego przesunięcia w czasie rzeczywistym. O Pole LivePlaybackSpeedControl jest wstrzykiwane podczas tworzenia odtwarzacza.

Koncepcja wstrzykiwania komponentów, które implementują elementy odtwarzacza jest dostępna w całej bibliotece. Domyślne implementacje niektóre komponenty przekazują pracę kolejnym wstrzykiwanym komponentom. Dzięki temu wiele podkomponentów, które należy zastąpić pojedynczo implementacjami, które są niestandardowo skonfigurowane.

Personalizacja odtwarzacza

Oto kilka typowych przykładów dostosowywania odtwarzacza przez wstrzykiwanie komponentów: opisane poniżej.

Konfigurowanie stosu sieciowego

Mamy stronę na temat dostosowywania stosu sieciowego używanego przez ExoPlayer.

Dane z pamięci podręcznej wczytane z sieci

Zobacz przewodniki dotyczące usługi tymczasowe buforowanie na bieżąco oraz pobieranie multimediów.

Dostosowywanie interakcji z serwerem

Niektóre aplikacje mogą przechwytywać żądania i odpowiedzi HTTP. Możesz wstrzykiwać niestandardowe nagłówki żądania, odczytywać nagłówki odpowiedzi serwera, modyfikować Identyfikatory URI itp. Aplikacja może na przykład uwierzytelnić się przez wstrzykiwanie jako nagłówka żądań segmentów multimediów.

Poniższy przykład pokazuje, jak wdrożyć te zachowania przez wstrzyknięcie niestandardowego DataSource.Factory do DefaultMediaSourceFactory:

Kotlin

val dataSourceFactory =
  DataSource.Factory {
    val dataSource = httpDataSourceFactory.createDataSource()
    // Set a custom authentication request header.
    dataSource.setRequestProperty("Header", "Value")
    dataSource
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

We fragmencie kodu powyżej wstrzyknięty HttpDataSource zawiera nagłówek "Header: Value" w każdym żądaniu HTTP. To zachowanie zostało naprawione w przypadku: interakcji ze źródłem HTTP.

Aby uzyskać bardziej precyzyjne podejście, możesz wstrzykiwać zachowania w odpowiednim momencie za pomocą funkcji ResolvingDataSource Poniższy fragment kodu pokazuje, jak wstawić fragment nagłówki żądania tuż przed interakcją ze źródłem HTTP:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

    DataSource.Factory dataSourceFactory =
        new ResolvingDataSource.Factory(
            httpDataSourceFactory,
            // Provide just-in-time request headers.
            dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

Możesz też użyć narzędzia ResolvingDataSource, aby wykonać modyfikacje identyfikatora URI „just-in-time”, jak pokazano w tym fragmencie kodu:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

Dostosowywanie obsługi błędów

Wdrożenie niestandardowego elementu LoadErrorHandlingPolicy umożliwia aplikacjom dostosowywanie jak ExoPlayer reaguje na błędy wczytywania. Na przykład aplikacja może szybko ulegać awariom zamiast ponawiać próby wiele razy lub możesz dostosować logikę wycofywania, która określa, jak długo odtwarzacz oczekuje między kolejnymi próbami. Ten fragment kodu pokazuje, jak wdrożyć niestandardową logikę wycofywania:

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

Argument LoadErrorInfo zawiera więcej informacji o nieudanym wczytaniu do dostosować logikę do typu błędu lub nieudanego żądania.

Dostosowywanie flag modułu wyodrębniania

Flagi wyodrębniania pozwalają dostosować sposób wyodrębniania poszczególnych formatów z multimediów progresywnych. Można je ustawić na urządzeniu DefaultExtractorsFactory, które jest został przekazany do DefaultMediaSourceFactory. Ten przykład przekazuje flagę który umożliwia oparte na indeksie przewijanie strumieni MP3.

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

Włączanie przewijania o stałej szybkości transmisji bitów

W przypadku strumieni MP3, ADTS i AMR możesz włączyć przybliżone przewijanie za pomocą ze stałą szybkością transmisji bitów przy flagach FLAG_ENABLE_CONSTANT_BITRATE_SEEKING. Te flagi można ustawić dla poszczególnych modułów wyodrębniania za pomocą DefaultExtractorsFactory.setXyzExtractorFlags zgodnie z opisem powyżej. Do włącz przewijanie o stałej szybkości transmisji bitów dla wszystkich modułów wyodrębniania danych, które go obsługują, użyj DefaultExtractorsFactory.setConstantBitrateSeekingEnabled

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

Polecenie ExtractorsFactory można następnie wstrzykiwać za pomocą metody DefaultMediaSourceFactory jako opisane powyżej w celu dostosowania flag modułu wyodrębniania.

Włączanie asynchronicznego kolejkowania w buforze

Asynchroniczne kolejki buforowe to ulepszenie renderowania w ExoPlayer potok, który uruchamia MediaCodec instancji w trybie asynchronicznym oraz wykorzystuje dodatkowe wątki do planowania dekodowania i renderowania danych. Włączam może zmniejszyć liczbę utraconych klatek i niedostatecznie słyszalnych klatek.

Asynchroniczne kolejki buforowe jest domyślnie włączone na urządzeniach z Androidem 12 (poziom interfejsu API 31) i nowsze i można je włączyć ręcznie od Androida w wersji 6.0 (poziom interfejsu API 23). Rozważ włączenie tej funkcji na konkretnych urządzeniach, na których zaobserwowano spadek. zaniżania klatek lub dźwięku, szczególnie w przypadku odtwarzania z zabezpieczeniem DRM lub odtwarzania z dużą liczbą klatek. treści.

W najprostszym przypadku musisz wstrzyknąć DefaultRenderersFactory do w następujący sposób:

Kotlin

val renderersFactory = 
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

Jeśli bezpośrednio tworzysz instancje mechanizmów renderowania, AsynchronousMediaCodecAdapter.Factory do: MediaCodecVideoRenderer i MediaCodecAudioRenderer konstruktorów.

Przechwytywanie wywołań metod za pomocą funkcji ForwardingPlayer

Część działania instancji Player możesz dostosować, pakując ją w jeden podklasy ForwardingPlayer i metod zastępowania, które pozwalają wykonać dowolne z z następujące:

  • Dostęp do parametrów przed przekazaniem ich do przedstawiciela Player.
  • Uzyskaj dostęp do wartości zwracanej przez przedstawiciela Player przed jego zwróceniem.
  • Całkowicie ponownie wdróż metodę.

Przy zastępowaniu metod ForwardingPlayer należy upewnić się, jest samospójna i zgodna z dokumentem Player zwłaszcza w przypadku metod, które mają na celu identyczne lub powiązane zachowanie. Na przykład:

  • Jeśli chcesz zastąpić każde odtworzenie , musisz zastąpić obie wartości ForwardingPlayer.play i ForwardingPlayer.setPlayWhenReady, ponieważ element wywołujący oczekuje, że działanie tych metod będzie identyczne, gdy playWhenReady = true
  • Jeśli chcesz zmienić przyrost wartości przewijania do przodu, musisz zastąpić obie wartości ForwardingPlayer.seekForward, aby przeprowadzić wyszukiwanie przy użyciu dostosowanego i ForwardingPlayer.getSeekForwardIncrement, aby zgłosić raport we właściwym przypadku.
  • Jeśli chcesz kontrolować, jakie Player.Commands reklamuje odtwarzacz musisz zastąpić zarówno Player.getAvailableCommands(), jak i Player.isCommandAvailable(), a także posłuchaj Player.Listener.onAvailableCommandsChanged() oddzwonienie, o którym chcesz otrzymać powiadomienia zmiany pochodzące z odtwarzacza.

Dostosowanie MediaSource

W powyższych przykładach wstrzyknięto niestandardowe komponenty, które można wykorzystać podczas odtwarzania MediaItem obiektów przekazanych do odtwarzacza. Gdzie jest precyzyjne dostosowanie, niestandardowe, można również wstrzykiwać niestandardowe komponenty MediaSource instancji, które mogą być przekazywane bezpośrednio do odtwarzacza. Przykład poniżej pokazuje, jak dostosować ProgressiveMediaSource, aby używał niestandardowej DataSource.Factory, ExtractorsFactory i LoadErrorHandlingPolicy:

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

Tworzenie komponentów niestandardowych

Biblioteka zawiera domyślne implementacje komponentów wymienionych u góry znajdziesz na tej stronie. Element ExoPlayer może używać tych komponentów, ale można również tworzyć niestandardowe implementacje, jeśli niestandardowe Przykłady implementacji niestandardowych:

  • Renderer – możesz zaimplementować niestandardowy obiekt Renderer do obsługi typu mediów nieobsługiwanego przez domyślne implementacje udostępniane przez bibliotece.
  • TrackSelector – zastosowanie niestandardowego atrybutu TrackSelector umożliwia aplikacji w celu zmiany sposobu, w jaki ścieżki udostępniane przez MediaSource są wybrane do wykorzystania przez każde z dostępnych zasobów Renderer.
  • LoadControl – zastosowanie niestandardowego atrybutu LoadControl umożliwia aplikacji w celu zmiany zasad buforowania w odtwarzaczu.
  • Extractor – jeśli musisz obsługiwać format kontenera, który nie jest obecnie dostępny obsługiwanych przez bibliotekę, rozważ wdrożenie niestandardowej klasy Extractor.
  • MediaSource – wdrożenie niestandardowej klasy MediaSource może być odpowiedni, jeśli chcesz pobrać próbki multimediów do przesłania do mechanizmów renderowania lub jeśli chcesz wdrożyć niestandardowe komponowanie MediaSource zachowanie użytkownika.
  • MediaSource.Factory – wdrażanie niestandardowego elementu MediaSource.Factory pozwala aplikacji dostosować sposób tworzenia obiektu MediaSource z: MediaItem.
  • DataSource – pakiet nadrzędny ExoPlayer zawiera już pewną liczbę Implementacje DataSource w różnych przypadkach użycia. Możesz zaimplementuj własną klasę DataSource, aby wczytywać dane w inny sposób, na przykład: za pomocą niestandardowego protokołu, za pomocą niestandardowego stosu HTTP lub pamięci podręcznej.

Podczas tworzenia komponentów niestandardowych zalecamy te działania:

  • Jeśli komponent niestandardowy musi raportować zdarzenia z powrotem do aplikacji, zalecamy używając tego samego modelu, co istniejące komponenty ExoPlayer, używając klas EventDispatcher lub przekazując Handler razem z detektor konstruktora komponentu.
  • Zalecamy, aby komponenty niestandardowe używały tego samego modelu, co istniejący odtwarzacz ExoPlayer. które pozwalają na ponowną konfigurację aplikacji podczas odtwarzania. Aby to zrobić: komponenty niestandardowe powinny implementować PlayerMessage.Target i otrzymywać zmian konfiguracji w metodzie handleMessage. Kod aplikacji powinien przekazać zmiany konfiguracji, wywołując metodę createMessage ExoPlayer, skonfigurować wiadomość i wysłać ją do komponentu za pomocą PlayerMessage.send Wysyłam wiadomości, które mają zostać dostarczone w wątku odtwarzania zapewnia, że są one wykonywane w tej kolejności wraz z innymi operacjami wykonywane na odtwarzaczu.