ExoPlayer obsługuje HLS w wielu formatach kontenera. Muszą być też obsługiwane formaty próbek audio i wideo (szczegóły znajdziesz w sekcji Formaty próbek). Gorąco zachęcamy twórców treści HLS do generowania transmisji HLS wysokiej jakości, jak opisano w tym poście na blogu.
Funkcja | Obsługiwane | Komentarze |
---|---|---|
Kontenery | ||
MPEG-TS | TAK | |
FMP4/CMAF | TAK | |
ADTS (AAC) | TAK | |
MP3 | TAK | |
Napisy | ||
CEA-608 | TAK | |
CEA-708 | TAK | |
WebVTT | TAK | |
Metadane | ||
ID3 | TAK | |
SCTE-35 | NIE | |
Ochrona treści | ||
AES-128 | TAK | |
Przykład AES-128 | NIE | |
Widevine | TAK | Interfejs API 19 i nowsze wersje (schemat „cenc”) oraz 25 i nowsze wersje (schemat „cbcs”) |
PlayReady SL2000 | TAK | Tylko Android TV |
Zarządzanie serwerem | ||
Aktualizacje delta | TAK | |
Blokowanie ponownego wczytywania playlisty | TAK | |
Blokowanie wczytywania wskazówek dotyczących wstępnego wczytywania | TAK | z wyjątkiem zakresów bajtów o nieokreślonej długości; |
Wstawianie reklam | ||
Wstawianie reklam sterowane przez serwer (reklamy pełnoekranowe) | Częściowo | Tylko VOD z X-ASSET-URI .
Transmisje na żywo i X-ASSET-LIST zostaną dodane później. |
Reklamy IMA po stronie serwera i klienta | TAK | Przewodnik dotyczący osadzania reklam |
Odtwarzanie na żywo | ||
Odtwarzanie na żywo | TAK | |
HLS o niskim opóźnieniu (Apple) | TAK | |
HLS o niskim opóźnieniu (wersja dla społeczności) | NIE | |
Common Media Client Data (CMCD) | TAK | Przewodnik po integracji z CMCD |
Korzystanie z MediaItem
Aby odtworzyć strumień HLS, musisz polegać na module HLS.
Kotlin
implementation("androidx.media3:media3-exoplayer-hls:1.6.0")
Groovy
implementation "androidx.media3:media3-exoplayer-hls:1.6.0"
Następnie możesz utworzyć MediaItem
dla URI playlisty HLS i przekazać go odtwarzaczowi.
Kotlin
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri))
// Prepare the player.
player.prepare()
Java
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri));
// Prepare the player.
player.prepare();
Jeśli adres URI nie kończy się na .m3u8
, możesz przekazać parametr MimeTypes.APPLICATION_M3U8
do metody setMimeType
obiektu MediaItem.Builder
, aby wyraźnie wskazać typ treści.
Identyfikator URI elementu multimedialnego może wskazywać playlistę multimediów lub playlistę wielowariantową. Jeśli adres URI wskazuje na playlistę wielowariantową, która deklaruje wiele tagów #EXT-X-STREAM-INF
, ExoPlayer automatycznie dostosowuje się do wariantów, biorąc pod uwagę zarówno dostępną przepustowość, jak i możliwości urządzenia.
Korzystanie z HlsMediaSource
Aby uzyskać więcej opcji dostosowywania, możesz utworzyć HlsMediaSource
i przekazać go bezpośrednio odtwarzaczowi zamiast MediaItem
.
Kotlin
// Create a data source factory.
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Create a HLS media source pointing to a playlist uri.
val hlsMediaSource =
HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri))
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource)
// Prepare the player.
player.prepare()
Java
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource);
// Prepare the player.
player.prepare();
Dostęp do pliku manifestu
Bieżący plik manifestu możesz pobrać, wywołując Player.getCurrentManifest
.
W przypadku HLS zwracany obiekt należy rzutować na HlsManifest
. Pobieranie pliku manifestu powoduje też wywołanie wywołania zwrotnego onTimelineChanged
w komponencie Player.Listener
. W przypadku treści dostępnych na żądanie będzie to miało miejsce raz, a w przypadku transmisji na żywo – wielokrotnie. Ten fragment kodu pokazuje, jak aplikacja może wykonać jakąś czynność po załadowaniu pliku manifestu.
Kotlin
player.addListener(
object : Player.Listener {
override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) {
val manifest = player.currentManifest
if (manifest is HlsManifest) {
// Do something with the manifest.
}
}
}
)
Java
player.addListener(
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
Object manifest = player.getCurrentManifest();
if (manifest != null) {
HlsManifest hlsManifest = (HlsManifest) manifest;
// Do something with the manifest.
}
}
});
Odtwarzanie strumieni HLS z reklamami pełnoekranowymi
Specyfikacja HLS definiuje reklamy HLS, które można wykorzystać do umieszczania informacji o reklamach w formie banerów wstawnych w playlistach multimedialnych. ExoPlayer domyślnie ignoruje te interstials. Pomoc można dodać za pomocą HlsInterstitialsAdsLoader
. Nie będziemy od razu obsługiwać wszystkich funkcji specyfikacji. Jeśli brakuje obsługi Twojej transmisji, poinformuj nas o tym, zgłaszając problem w GitHubie i przesyłając URI strumienia, abyśmy mogli dodać obsługę Twojej transmisji.
Używanie MediaItem
z interfejsem API playlisty
Najwygodniejszym sposobem odtwarzania strumieni HLS z reklamami pośrednimi jest utworzenie instancji ExoPlayera za pomocą HlsInterstitialsAdsLoader.AdsMediaSourceFactory
.
Umożliwia to odtwarzanie reklam pełnoekranowych HLS za pomocą interfejsu Player
opartego na MediaItem
interfejsie API playlist.
Wartość MediaSource.Factory
obiektu ExoPlayer
może zostać wstrzyknięta do konstruktora podczas tworzenia instancji gracza:
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader,
playerView,
DefaultMediaSourceFactory(context),
)
// Build player with interstitials media source factory
player =
ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build()
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player)
playerView.setPlayer(player)
Java
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader, playerView, new DefaultMediaSourceFactory(context));
// Build player with interstitials media source factory
player =
new ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build();
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);
W przypadku takiej konfiguracji odtwarzanie reklam HLS polega na ustawieniu elementu multimedialnego za pomocą elementu AdsConfiguration
w odtwarzaczu:
Kotlin
player.setMediaItem(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0") // must be unique within playlist
.build())
.build())
player.prepare();
player.play();
Java
player.setMediaItem(
new MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
new AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0") // must be unique within playlist
.build())
.build());
player.prepare();
player.play();
Korzystanie z interfejsu API na podstawie źródła multimediów
Możesz też tworzyć instancje ExoPlayera bez zastępowania domyślnej fabryki źródeł multimediów. Aby obsługiwać reklamy pośrednie, aplikacja może bezpośrednio używać interfejsu HlsInterstitialsAdsLoader.AdsMediaSourceFactory
do tworzenia MediaSource
i przekazywać go do ExoPlayera za pomocą interfejsu API playlisty opartego na źródle multimediów:
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
var hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(hlsInterstitialsAdsLoader, playerView, context)
// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();
// Create an media source from an HLS media item with ads configuration.
val mediaSource =
hlsMediaSourceFactory.createMediaSource(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0")
.build()
)
.build()
)
// Set the media source on the player.
player.setMediaSource(mediaSource)
player.prepare()
player.play()
Java
HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader, playerView, context);
// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();
// Create an media source from an HLS media item with ads configuration.
MediaSource mediaSource =
hlsMediaSourceFactory.createMediaSource(
new MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0")
.build())
.build());
// Set the media source on the player.
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.play();
Nasłuchiwanie zdarzeń reklamowych
Do HlsInterstitialsAdsLoader
można dodać Listener
, aby monitorować zdarzenia dotyczące zmian stanu odtwarzania reklam HLS. Umożliwia to aplikacji lub pakietowi SDK śledzenie wyświetlanych reklam, wczytywanie list komponentów i przygotowywanie źródeł mediów reklamowych lub wykrywanie błędów wczytywania list komponentów i przygotowywania reklam. Ponadto metadane emitowane przez źródła mediów reklamowych mogą być odbierane w celu szczegółowej weryfikacji odtwarzania reklam lub śledzenia postępów odtwarzania reklam.
Kotlin
class AdsLoaderListener : HlsInterstitialsAdsLoader.Listener {
override fun onStart(mediaItem: MediaItem, adsId: Any, adViewProvider: AdViewProvider) {
// Do something when HLS media item with interstitials is started.
}
override fun onMetadata(
mediaItem: MediaItem,
adsId: Any,
adGroupIndex: Int,
adIndexInAdGroup: Int,
metadata: Metadata,
) {
// Do something with metadata that is emitted by the ad media source of the given ad.
}
override fun onAdCompleted(
mediaItem: MediaItem,
adsId: Any,
adGroupIndex: Int,
adIndexInAdGroup: Int,
) {
// Do something when ad completed playback.
}
// ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener.
override fun onStop(mediaItem: MediaItem, adsId: Any, adPlaybackState: AdPlaybackState) {
// Do something with the resulting ad playback state when stopped.
}
}
Java
private class AdsLoaderListener
implements HlsInterstitialsAdsLoader.Listener {
// implement HlsInterstitialsAdsLoader.Listener
@Override
public void onStart(MediaItem mediaItem, Object adsId, AdViewProvider adViewProvider) {
// Do something when HLS media item with interstitials is started.
}
@Override
public void onMetadata(
MediaItem mediaItem,
Object adsId,
int adGroupIndex,
int adIndexInAdGroup,
Metadata metadata) {
// Do something with metadata that is emitted by the ad media source of the given ad.
}
@Override
public void onAdCompleted(
MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup) {
// Do something when ad completed playback.
}
// ... See JavaDoc for further callbacks
@Override
public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) {
// Do something with the resulting ad playback state when stopped.
}
}
Szczegółową dokumentację wszystkich dostępnych funkcji zwrotnych znajdziesz w JavaDoc HlsInterstitialsAdsLoader.Listener
.
Następnie możesz dodać detektor do ładowarki reklam:
Kotlin
var listener = AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);
Java
AdsLoaderListener listener = new AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);
HlsInterstitialsAdsLoader
cyklu życia
Wystąpienia HlsInterstitialsAdsLoader
lub HlsInterstitialsAdsLoader.AdsMediaSourceFactory
można ponownie wykorzystać w przypadku wielu wystąpień odtwarzacza, które tworzą wiele źródeł multimediów, do których muszą być ładowane reklamy.
Możesz utworzyć instancję na przykład w metodie onCreate
obiektu Activity
, a potem ponownie użyć jej w przypadku wielu instancji gracza. Działa to, o ile jest używane przez jedną instancję odtwarzacza w tym samym czasie. Jest to przydatne w typowym przypadku, gdy aplikacja jest przenoszona do tła, instancja odtwarzacza jest usuwana, a następnie tworzona jest nowa instancja, gdy aplikacja ponownie jest na pierwszym planie.
Kotlin
// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context);
// Build a player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Build another player and set it on the ads loader (for example onStart).
player = ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Release the ads loader when not used anymore (for example onDestroy).
hlsInterstitialsAdsLoader.release();
Java
// Create the ads loader instance (for example onCreate).
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Build a player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Build another player and set it on the ads loader (for example onStart).
player = new ExoPlayer.Builder(context).build();
hlsInterstitialsAdsLoader.setPlayer(player);
// Release the player and unset it on the ads loader (for example onStop).
player.release();
hlsInterstitialsAdsLoader.setPlayer(null);
// Release the ads loader when not used anymore (for example onDestroy).
hlsInterstitialsAdsLoader.release();
Zazwyczaj przed użyciem kolejnej instancji odtwarzacza należy zwolnić starą instancję. Po wydaniu samego ładowarki reklam nie można już jej używać.
Dostosowywanie odtwarzania
ExoPlayer udostępnia wiele sposobów dostosowywania odtwarzania do potrzeb aplikacji. Przykłady znajdziesz na stronie personalizacji.
Wyłączanie przygotowania bez podziału na części
Domyślnie ExoPlayer używa przygotowania bez fragmentów. Oznacza to, że ExoPlayer będzie używać tylko informacji z playlisty wielowariantowej do przygotowania strumienia. Działa to, jeśli tagi #EXT-X-STREAM-INF
zawierają atrybut CODECS
.
Jeśli segmenty multimediów zawierają zmiksowane ścieżki z napisami, które nie są zadeklarowane w multiwariantowej playliście za pomocą tagu #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS
, konieczne może być wyłączenie tej funkcji. W przeciwnym razie te ścieżki z napisami nie zostaną wykryte i odtworzone. Możesz wyłączyć przygotowanie bez segmentów w HlsMediaSource.Factory
, jak pokazano w tym fragmencie kodu. Pamiętaj, że zwiększy to czas uruchamiania, ponieważ ExoPlayer musi pobrać segment multimediów, aby wykryć te dodatkowe ścieżki. Lepiej jest zamiast tego zadeklarować ścieżki z napisami na liście odtwarzania z wieloma wariantami.
Kotlin
val hlsMediaSource =
HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(false)
.createMediaSource(MediaItem.fromUri(hlsUri))
Java
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(false)
.createMediaSource(MediaItem.fromUri(hlsUri));
Tworzenie wysokiej jakości treści HLS
Aby w pełni wykorzystać możliwości ExoPlayera, możesz przestrzegać określonych wytycznych, które pomogą Ci ulepszyć zawartość HLS. Aby uzyskać pełne wyjaśnienie, przeczytaj post na Medium na temat odtwarzania HLS w ExoPlayerze. Najważniejsze kwestie:
- Używaj dokładnych długości segmentów.
- Używaj ciągłego strumienia multimediów. Unikaj zmian w strukturze multimediów w różnych segmentach.
- Użyj tagu
#EXT-X-INDEPENDENT-SEGMENTS
. - Preferuj strumienie demuxed zamiast plików zawierających zarówno wideo, jak i dźwięk.
- Uwzględnij wszystkie możliwe informacje na playlistzie z wieloma wariantami.
W przypadku transmisji na żywo obowiązują te wytyczne:
- Użyj tagu
#EXT-X-PROGRAM-DATE-TIME
. - Użyj tagu
#EXT-X-DISCONTINUITY-SEQUENCE
. - Długi czas trwania transmisji na żywo. 1 minuta lub dłużej.