Transmisje na żywo

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.

Okres ważności

  • 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, gdzie MediaItem.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 formie Timeline. Bieżącą wartość Timeline.Window można pobrać z Timeline za pomocą funkcji Player.getCurrentMediaItemIndexTimeline.getWindow. W Window:
    • 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 w MediaItem.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 do MediaItem.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 i fallbackMaxPlaybackSpeed: minimalna i maksymalna prędkość odtwarzania, które można dostosować do regulacji, jeśli ani media, ani MediaItem 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
  }
}