214 lines
9.8 KiB
Markdown
214 lines
9.8 KiB
Markdown
---
|
|
title: Live streaming
|
|
---
|
|
|
|
ExoPlayer plays most adaptive live streams out-of-the-box without any special
|
|
configuration. See the [Supported Formats page][] for more details.
|
|
|
|
Adaptive live streams offer a window of available media that is updated in
|
|
regular intervals to move with the current real-time. That means the playback
|
|
position will always be somewhere in this window, in most cases close to the
|
|
current real-time at which the stream is being produced. The difference between
|
|
the current real-time and the playback position is called the *live offset*.
|
|
|
|
Unlike adaptive live streams, progressive live streams do not have a live window
|
|
and can only be played at one position. The documentation on this page is only
|
|
relevant to adaptive live streams.
|
|
{:.info}
|
|
|
|
## Detecting and monitoring live playbacks ##
|
|
|
|
Every time a live window is updated, registered `Player.Listener` instances
|
|
will receive an `onTimelineChanged` event. You can retrieve details about the
|
|
current live playback by querying various `Player` and `Timeline.Window`
|
|
methods, as listed below and shown in the following figure.
|
|
|
|
{% include figure.html url="/images/live-window.png" index="1" caption="Live window" %}
|
|
|
|
* `Player.isCurrentWindowLive` indicates whether the currently playing media
|
|
item is a live stream. This value is still true even if the live stream has
|
|
ended.
|
|
* `Player.isCurrentWindowDynamic` indicates whether the currently playing media
|
|
item is still being updated. This is usually true for live streams that are
|
|
not yet ended. Note that this flag is also true for non-live streams in some
|
|
cases.
|
|
* `Player.getCurrentLiveOffset` returns the offset between the current real
|
|
time and the playback position (if available).
|
|
* `Player.getDuration` returns the length of the current live window.
|
|
* `Player.getCurrentPosition` returns the playback position relative to the
|
|
start of the live window.
|
|
* `Player.getCurrentMediaItem` returns the current media item, where
|
|
`MediaItem.liveConfiguration` contains app-provided overrides for the target
|
|
live offset and live offset adjustment parameters.
|
|
* `Player.getCurrentTimeline` returns the current media structure in a
|
|
`Timeline`. The current `Timeline.Window` can be retrieved from the `Timeline`
|
|
using `Player.getCurrentWindowIndex` and `Timeline.getWindow`. Within the
|
|
`Window`:
|
|
* `Window.liveConfiguration` contains the target live offset and live offset
|
|
adjustment parameters. These values are based on information in the media
|
|
and any app-provided overrides set in `MediaItem.liveConfiguration`.
|
|
* `Window.windowStartTimeMs` is the time since the Unix Epoch at which the
|
|
live window starts.
|
|
* `Window.getCurrentUnixTimeMs` is the time since the Unix Epoch of the
|
|
current real-time. This value may be corrected by a known clock difference
|
|
between the server and the client.
|
|
* `Window.getDefaultPositionMs` is the position in the live window at which
|
|
the player will start playback by default.
|
|
|
|
## Seeking in live streams ##
|
|
|
|
You can seek to anywhere within the live window using `Player.seekTo`. The seek
|
|
position passed is relative to the start of the live window. For example,
|
|
`seekTo(0)` will seek to the start of the live window. The player will try to
|
|
keep the same live offset as the seeked-to position after a seek.
|
|
|
|
The live window also has a default position at which playback is supposed to
|
|
start. This position is usually somewhere close to the live edge. You can seek
|
|
to the default position by calling `Player.seekToDefaultPosition`.
|
|
|
|
## Live playback UI ##
|
|
|
|
ExoPlayer's [default UI components][] show the duration of the live window and
|
|
the current playback position within it. This means the position will appear to
|
|
jump backwards each time the live window is updated. If you need different
|
|
behavior, for example showing the Unix time or the current live offset, you can
|
|
fork `StyledPlayerControlView` and modify it to suit your needs.
|
|
|
|
There is a [pending feature request (#2213)][] for ExoPlayer's default UI
|
|
components to support additional modes when playing live streams.
|
|
{:.info}
|
|
|
|
## Configuring live playback parameters ##
|
|
|
|
ExoPlayer uses some parameters to control the offset of the playback position
|
|
from the live edge, and the range of playback speeds that can be used to
|
|
adjust this offset.
|
|
|
|
ExoPlayer gets values for these parameters from three places, in descending
|
|
order of priority (the first value found is used):
|
|
|
|
* Per `MediaItem` values passed to `MediaItem.Builder.setLiveConfiguration`.
|
|
* Global default values set on `DefaultMediaSourceFactory`.
|
|
* Values read directly from the media.
|
|
|
|
~~~
|
|
// 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);
|
|
~~~
|
|
{: .language-java}
|
|
|
|
Available configuration values are:
|
|
|
|
* `targetOffsetMs`: The target live offset. The player will attempt to get
|
|
close to this live offset during playback if possible.
|
|
* `minOffsetMs`: The minimum allowed live offset. Even when adjusting the
|
|
offset to current network conditions, the player will not attempt to get below
|
|
this offset during playback.
|
|
* `maxOffsetMs`: The maximum allowed live offset. Even when adjusting the
|
|
offset to current network conditions, the player will not attempt to get above
|
|
this offset during playback.
|
|
* `minPlaybackSpeed`: The minimum playback speed the player can use to fall back
|
|
when trying to reach the target live offset.
|
|
* `maxPlaybackSpeed`: The maximum playback speed the player can use to catch up
|
|
when trying to reach the target live offset.
|
|
|
|
## Playback speed adjustment ##
|
|
|
|
When playing a low-latency live stream ExoPlayer adjusts the live offset by
|
|
slightly changing the playback speed. The player will try to match the target
|
|
live offset provided by the media or the app, but will also try to react to
|
|
changing network conditions. For example, if rebuffers occur during playback,
|
|
the player will slow down playback slightly to move further away from the live
|
|
edge. If the network then becomes stable enough to support playing closer to the
|
|
live edge again, the player will speed up playback to move back toward the
|
|
target live offset.
|
|
|
|
If automatic playback speed adjustment is not desired, it can be disabled by
|
|
setting `minPlaybackSpeed` and `maxPlaybackSpeed` properties to `1.0f`.
|
|
Similarly, it can be enabled for non-low-latency live streams by setting these
|
|
explicitly to values other than `1.0f`. See
|
|
[the configuration section above](#configuring-live-playback-parameters) for
|
|
more details on how these properties can be set.
|
|
|
|
### Customizing the playback speed adjustment algorithm ###
|
|
|
|
If speed adjustment is enabled, a `LivePlaybackSpeedControl` defines what
|
|
adjustments are made. It's possible to implement a custom
|
|
`LivePlaybackSpeedControl`, or to customize the default implementation, which is
|
|
`DefaultLivePlaybackSpeedControl`. In both cases an instance can be set when
|
|
building the player:
|
|
|
|
~~~
|
|
ExoPlayer player =
|
|
new ExoPlayer.Builder(context)
|
|
.setLivePlaybackSpeedControl(
|
|
new DefaultLivePlaybackSpeedControl.Builder()
|
|
.setFallbackMaxPlaybackSpeed(1.04f)
|
|
.build())
|
|
.build();
|
|
~~~
|
|
{: .language-java}
|
|
|
|
Relevant customization parameters of `DefaultLivePlaybackSpeedControl` are:
|
|
|
|
* `fallbackMinPlaybackSpeed` and `fallbackMaxPlaybackSpeed`: The minimum and
|
|
maximum playback speeds that can be used for adjustment if neither the media
|
|
nor the app-provided `MediaItem` define limits.
|
|
* `proportionalControlFactor`: Controls how smooth the speed adjustment is. A
|
|
high value makes adjustments more sudden and reactive, but also more likely to
|
|
be audible. A smaller value results in a smoother transition between speeds,
|
|
at the cost of being slower.
|
|
* `targetLiveOffsetIncrementOnRebufferMs`: This value is added to the target
|
|
live offset whenever a rebuffer occurs, in order to proceed more cautiously.
|
|
This feature can be disabled by setting the value to 0.
|
|
* `minPossibleLiveOffsetSmoothingFactor`: An exponential smoothing factor that
|
|
is used to track the minimum possible live offset based on the currently
|
|
buffered media. A value very close to 1 means that the estimation is more
|
|
cautious and may take longer to adjust to improved network conditions, whereas
|
|
a lower value means the estimation will adjust faster at a higher risk of
|
|
running into rebuffers.
|
|
|
|
## BehindLiveWindowException and ERROR_CODE_BEHIND_LIVE_WINDOW ##
|
|
|
|
The playback position may fall behind the live window, for example if the player
|
|
is paused or buffering for a long enough period of time. If this happens then
|
|
playback will fail and an exception with error code
|
|
`ERROR_CODE_BEHIND_LIVE_WINDOW` will be reported via
|
|
`Player.Listener.onPlayerError`. Application code may wish to handle such
|
|
errors by resuming playback at the default position. The [PlayerActivity][] of
|
|
the demo app exemplifies this approach.
|
|
|
|
~~~
|
|
@Override
|
|
public void onPlayerError(PlaybackException error) {
|
|
if (eror.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
|
|
// Re-initialize player at the current live window default position.
|
|
player.seekToDefaultPosition();
|
|
player.prepare();
|
|
} else {
|
|
// Handle other errors.
|
|
}
|
|
}
|
|
~~~
|
|
{: .language-java}
|
|
|
|
[Supported Formats page]: {{ site.baseurl }}/supported-formats.html
|
|
[default UI components]: {{ site.baseurl }}/ui-components.html
|
|
[pending feature request (#2213)]: https://github.com/google/ExoPlayer/issues/2213
|
|
[PlayerActivity]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
|