ExoPlayer odtwarza większość dostosowanych transmisji na żywo bez specjalnej konfiguracji. Więcej informacji znajdziesz na stronie obsługiwanych formatów.
Adaptacyjne transmisje na żywo oferują okno dostępnych multimediów, które są aktualizowane w regularnych odstępach czasu, aby zmieniało się w czasie rzeczywistym. Oznacza to, że pozycja odtwarzania będzie zawsze znajdować się w tym oknie, w większości przypadków w pobliżu bieżącego czasu rzeczywistego, w którym generowany jest strumień. Różnica między bieżącą pozycją w czasie rzeczywistym a pozycją odtwarzania to opóźnienie na żywo.
Wykrywanie i monitorowanie odtwarzania na żywo
Za każdym razem, gdy okno transmisji na żywo zostanie zaktualizowane, zarejestrowane wystąpienia Player.Listener
otrzymają zdarzenie onTimelineChanged
. Szczegóły dotyczące bieżącego odtwarzania na żywo możesz pobrać, wysyłając zapytanie za pomocą różnych metod Player
i Timeline.Window
, jak pokazano na rysunku poniżej.
Player.isCurrentWindowLive
wskazuje, czy obecnie odtwarzany element multimedialny jest transmisją na żywo. Ta wartość będzie obowiązywać nawet po zakończeniu transmisji na żywo.Player.isCurrentWindowDynamic
wskazuje, czy obecnie odtwarzany element multimediów jest nadal aktualizowany. Zwykle dotyczy to transmisji na żywo, które jeszcze się nie zakończyły. Pamiętaj, że w niektórych przypadkach dotyczy to również transmisji, które nie są na żywo.Player.getCurrentLiveOffset
zwraca przesunięcie między bieżącym czasem rzeczywistym a miejscem odtwarzania (jeśli jest dostępne).Player.getDuration
zwraca długość bieżącego aktywnego okna.Player.getCurrentPosition
zwraca pozycję odtwarzania w stosunku do początku okna transmisji.Player.getCurrentMediaItem
zwraca bieżący element multimedialny, gdzieMediaItem.liveConfiguration
zawiera zastąpienia dostarczone przez aplikację dla docelowych parametrów przesunięcia na żywo i przesunięcia dostosowania na żywo.- Funkcja
Player.getCurrentTimeline
zwraca bieżącą strukturę mediów w formieTimeline
. Bieżącą wartośćTimeline.Window
można pobrać zTimeline
za pomocą funkcjiPlayer.getCurrentMediaItemIndex
iTimeline.getWindow
. WWindow
:Window.liveConfiguration
zawiera docelowy offset i parametry korekty offsetu. Te wartości są oparte na informacjach w multimediach oraz na wszelkich wartościach zastąpionych przez aplikację ustawionych wMediaItem.liveConfiguration
.Window.windowStartTimeMs
to czas od początku ery Unixa, w którym rozpoczyna się okno na żywo.Window.getCurrentUnixTimeMs
to czas od epoki Unix do bieżącego czasu rzeczywistego. Ta wartość może zostać skorygowana o znaną różnicę zegara między serwerem a klientem.Window.getDefaultPositionMs
to pozycja w oknie transmisji na żywo, od której odtwarzacz domyślnie rozpocznie odtwarzanie.
Przewijanie transmisji na żywo
Za pomocą przycisku Player.seekTo
możesz przewinąć do dowolnego miejsca w oknie transmisji na żywo. Podana pozycja przesunięcia jest względna do początku okna transmisji na żywo. Na przykład seekTo(0)
przewinie do początku aktywnego okna. Odtwarzacz spróbuje utrzymać ten sam przesunięcie w czasie, co pozycja po przesunięciu.
Okno transmisji na żywo ma też domyślną pozycję, w której ma się rozpocząć odtwarzanie. Ta pozycja znajduje się zwykle w pobliżu krawędzi. Możesz wrócić do pozycji domyślnej, naciskając Player.seekToDefaultPosition
.
Interfejs odtwarzania na żywo
Domyślne komponenty UI ExoPlayer pokazują czas trwania aktywnego okna i bieżącą pozycję odtwarzania. Oznacza to, że pozycja będzie się wydawać przeskakująca do tyłu za każdym razem, gdy okno na żywo zostanie zaktualizowane. Jeśli potrzebujesz innego zachowania, na przykład wyświetlania czasu Unix lub bieżącego przesunięcia czasu, możesz utworzyć fork PlayerControlView
i zmodyfikować go zgodnie ze swoimi potrzebami.
Konfigurowanie parametrów odtwarzania na żywo
ExoPlayer używa niektórych parametrów do sterowania przesunięciem pozycji odtwarzania od krawędzi transmisji na żywo oraz zakres prędkości odtwarzania, które można wykorzystać do dostosowania tego przesunięcia.
ExoPlayer pobiera wartości tych parametrów z 3 miejsc w malejącym porządku priorytetu (używana jest pierwsza znaleziona wartość):
- Wartości
MediaItem
przekazywane doMediaItem.Builder.setLiveConfiguration
. - Ustawione globalne wartości domyślne w
DefaultMediaSourceFactory
. - Wartości odczytywane bezpośrednio z multimediów.
Kotlin
// Global settings. val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build() // Per MediaItem settings. val mediaItem = MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build() ) .build() player.setMediaItem(mediaItem)
Java
// Global settings. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000)) .build(); // Per MediaItem settings. MediaItem mediaItem = new MediaItem.Builder() .setUri(mediaUri) .setLiveConfiguration( new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()) .build(); player.setMediaItem(mediaItem);
Dostępne wartości konfiguracji:
targetOffsetMs
: docelowa wartość przesunięcia w czasie rzeczywistym. Jeśli to możliwe, odtwarzacz będzie starał się zbliżyć do tego opóźnienia podczas odtwarzania.minOffsetMs
: minimalne dopuszczalne przesunięcie transmisji na żywo. Nawet jeśli przesuniesz offset, aby dopasować go do bieżących warunków sieciowych, odtwarzacz nie będzie próbował uzyskać wartości poniżej tego offsetu.maxOffsetMs
: maksymalny dopuszczalny przesunięcie transmisji na żywo. Nawet jeśli przesuniesz offset, aby dopasować go do bieżących warunków sieci, odtwarzacz nie będzie próbował przekroczyć tego offsetu.minPlaybackSpeed
: minimalna szybkość odtwarzania, przy której odtwarzacz może się przełączyć, gdy próbuje osiągnąć docelowe przesunięcie w czasie rzeczywistym.maxPlaybackSpeed
: maksymalna szybkość odtwarzania, przy której odtwarzacz może osiągnąć osiągnięcie docelowego przesunięcia w czasie rzeczywistym.
Regulacja szybkości odtwarzania
Podczas odtwarzania transmisji na żywo z małym opóźnieniem ExoPlayer dostosowuje opóźnienie odtwarzania, nieznacznie zmieniając szybkość odtwarzania. Odtwarzacz będzie się starał dopasować do docelowego opóźnienia na żywo podanego przez media lub aplikację, ale będzie też reagować na zmieniające się warunki sieci. Jeśli na przykład podczas odtwarzania wystąpi buforowanie, odtwarzacz spowolni odtwarzanie, aby oddalić się od krawędzi obrazu na żywo. Jeśli sieć stanie się na tyle stabilna, by znów umożliwić odtwarzanie bliżej krawędzi transmisji na żywo, odtwarzacz przyspieszy odtwarzanie, by zbliżyć się do docelowego przesunięcia w czasie rzeczywistym.
Jeśli nie chcesz automatycznie dostosowywać prędkości odtwarzania, możesz ją wyłączyć, ustawiając właściwości minPlaybackSpeed
i maxPlaybackSpeed
na 1.0f
.
Podobnie można go włączyć w przypadku transmisji na żywo z większym opóźnieniem, ustawiając te wartości bezpośrednio na wartości inne niż 1.0f
. Więcej informacji o sposobie ustawiania tych właściwości znajdziesz w sekcji konfiguracji powyżej.
Dostosowywanie algorytmu dostosowywania szybkości odtwarzania
Jeśli korekta prędkości jest włączona, LivePlaybackSpeedControl
określa, jakie korekty są wprowadzane. Możesz zaimplementować niestandardową implementację LivePlaybackSpeedControl
lub dostosować implementację domyślną, która jest dostępna pod adresem DefaultLivePlaybackSpeedControl
. W obu przypadkach podczas tworzenia odtwarzacza można ustawić instancję:
Kotlin
val player = ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build() ) .build()
Java
ExoPlayer player = new ExoPlayer.Builder(context) .setLivePlaybackSpeedControl( new DefaultLivePlaybackSpeedControl.Builder() .setFallbackMaxPlaybackSpeed(1.04f) .build()) .build();
Odpowiednie parametry dostosowywania w przypadku DefaultLivePlaybackSpeedControl
to:
fallbackMinPlaybackSpeed
ifallbackMaxPlaybackSpeed
: minimalna i maksymalna prędkość odtwarzania, które można dostosować do regulacji, jeśli ani media, aniMediaItem
określone przez aplikację nie określają limitów.proportionalControlFactor
: określa płynność regulacji prędkości. Wysoka wartość sprawia, że korekty są bardziej nagłe i reaktywne, ale przy tym bardziej słyszalne. Mniejsza wartość powoduje płynniejsze przejście między prędkościami, ale kosztem wolniejszego działania.targetLiveOffsetIncrementOnRebufferMs
: ta wartość jest dodawana do docelowego przesunięcia wersji opublikowanej po każdym ponownym buforowaniu, co pozwala zachować ostrożność. Funkcję tę można wyłączyć, ustawiając wartość na 0.minPossibleLiveOffsetSmoothingFactor
: wykładniczy współczynnik wygładzania, który służy do śledzenia minimalnego możliwego przesunięcia na żywo na podstawie obecnie buforowanych multimediów. Wartość bardzo zbliżona do 1 oznacza, że szacowanie jest bardziej ostrożne i może potrwać dłużej, aby dostosować się do lepszych warunków sieci. Niższa wartość oznacza, że szacowanie będzie się dostosowywać szybciej, ale z większym ryzykiem wystąpienia buforowania.
BehindLiveWindowException i ERROR_CODE_BEHIND_LIVE_WINDOW
Pozycja odtwarzania może być opóźniona w stosunku do okna transmisji na żywo, na przykład jeśli odtwarzacz był wstrzymany lub buforowany przez odpowiednio długi czas. W takim przypadku odtwarzanie się nie powiedzie, a wyjątek z kodem błędu ERROR_CODE_BEHIND_LIVE_WINDOW
zostanie zgłoszony przez Player.Listener.onPlayerError
. Kod aplikacji może obsługiwać takie błędy, wznawiając odtwarzanie w pozycji domyślnej. Przykładem tego podejścia jest PlayerActivity w aplikacji w wersji demonstracyjnej.
Kotlin
override fun onPlayerError(error: PlaybackException) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition() player.prepare() } else { // Handle other errors } }
Java
@Override public void onPlayerError(PlaybackException error) { if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the live edge. player.seekToDefaultPosition(); player.prepare(); } else { // Handle other errors } }