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 instancjiMediaSource
z elementuMediaItem
przezMediaSource.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
naMediaSource
. PoleMediaSource.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ępneRenderer
. Wstrzyknięto:TrackSelector
po utworzeniu odtwarzacza. LoadControl
, który określa, kiedyMediaSource
buforuje więcej multimediów, ile multimediów jest buforowanych. PoleLoadControl
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 PoleLivePlaybackSpeedControl
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
iForwardingPlayer.setPlayWhenReady
, ponieważ element wywołujący oczekuje, że działanie tych metod będzie identyczne, gdyplayWhenReady = 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 iForwardingPlayer.getSeekForwardIncrement
, aby zgłosić raport we właściwym przypadku. - Jeśli chcesz kontrolować, jakie
Player.Commands
reklamuje odtwarzacz musisz zastąpić zarównoPlayer.getAvailableCommands()
, jak iPlayer.isCommandAvailable()
, a także posłuchajPlayer.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 obiektRenderer
do obsługi typu mediów nieobsługiwanego przez domyślne implementacje udostępniane przez bibliotece.TrackSelector
– zastosowanie niestandardowego atrybutuTrackSelector
umożliwia aplikacji w celu zmiany sposobu, w jaki ścieżki udostępniane przezMediaSource
są wybrane do wykorzystania przez każde z dostępnych zasobówRenderer
.LoadControl
– zastosowanie niestandardowego atrybutuLoadControl
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 klasyExtractor
.MediaSource
– wdrożenie niestandardowej klasyMediaSource
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 komponowanieMediaSource
zachowanie użytkownika.MediaSource.Factory
– wdrażanie niestandardowego elementuMediaSource.Factory
pozwala aplikacji dostosować sposób tworzenia obiektuMediaSource
z:MediaItem
.DataSource
– pakiet nadrzędny ExoPlayer zawiera już pewną liczbę ImplementacjeDataSource
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ącHandler
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 metodziehandleMessage
. 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.