From ee6ba28d74cc75c5f872f8d24d85722c3b1133ab Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 3 Apr 2024 14:02:36 +0100 Subject: [PATCH] Redirect exoplayer.dev pages to media3 on d.android.com Two pages don't have media3 equivalents, so are redirected to https://developer.android.com/media/media3/exoplayer instead: * https://exoplayer.dev/design-documents.html * https://exoplayer.dev/pros-and-cons.html PiperOrigin-RevId: 621497706 (cherry picked from commit e6e7240df758b9ed6d757c77ca8855edd6aa7976) --- docs/ad-insertion.md | 338 +---------------------- docs/analytics.md | 270 +----------------- docs/battery-consumption.md | 82 +----- docs/customization.md | 317 +--------------------- docs/dash.md | 92 +------ docs/debug-logging.md | 125 +-------- docs/demo-application.md | 265 +----------------- docs/design-documents.md | 37 +-- docs/downloading-media.md | 417 +--------------------------- docs/drm.md | 117 +------- docs/glossary.md | 315 +-------------------- docs/hello-world.md | 221 +-------------- docs/hls.md | 140 +--------- docs/index.md | 34 +-- docs/listening-to-player-events.md | 246 +---------------- docs/live-streaming.md | 219 +-------------- docs/media-items.md | 165 +---------- docs/media-sources.md | 89 +----- docs/network-stacks.md | 185 +------------ docs/oems.md | 111 +------- docs/playlists.md | 214 +-------------- docs/progressive.md | 53 +--- docs/pros-and-cons.md | 53 +--- docs/retrieving-metadata.md | 96 +------ docs/rtsp.md | 104 +------ docs/shrinking.md | 140 +--------- docs/smoothstreaming.md | 94 +------ docs/supported-devices.md | 58 +--- docs/supported-formats.md | 128 +-------- docs/track-selection.md | 220 +-------------- docs/transforming-media.md | 144 +--------- docs/troubleshooting.md | 422 +---------------------------- docs/ui-components.md | 151 +---------- 33 files changed, 99 insertions(+), 5563 deletions(-) diff --git a/docs/ad-insertion.md b/docs/ad-insertion.md index 9ccdba968d2..f76fdc99337 100644 --- a/docs/ad-insertion.md +++ b/docs/ad-insertion.md @@ -1,337 +1,5 @@ --- -title: Ad insertion +permalink: /ad-insertion.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/ad-insertion --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer can be used for both client-side and server-side ad insertion. - -## Client-side ad insertion ## - -In client-side ad insertion, the player switches between loading media from -different URLs as it transitions between playing content and ads. Information -about ads is loaded separately from the media, such as from an XML [VAST][] or -[VMAP][] ad tag. This can include ad cue positions relative to the start of the -content, the actual ad media URIs and metadata such as whether a given ad is -skippable. - -When using ExoPlayer's `AdsMediaSource` for client-side ad insertion, the player -has information about the ads to be played. This has several benefits: - -* The player can expose metadata and functionality relating to ads via its API. -* [ExoPlayer UI components][] can show markers for ad positions automatically, - and change their behavior depending on whether ad is playing. -* Internally, the player can keep a consistent buffer across transitions between - ads and content. - -In this setup, the player takes care of switching between ads and content, which -means that apps don't need to take care of controlling multiple separate -background/foreground players for ads and content. - -When preparing content videos and ad tags for use with client-side ad insertion, -ads should ideally be positioned at synchronization samples (keyframes) in the -content video so that the player can resume content playback seamlessly. - -### Declarative ad support ### - -An ad tag URI can be specified when building a `MediaItem`: - -~~~ -MediaItem mediaItem = - new MediaItem.Builder() - .setUri(videoUri) - .setAdsConfiguration( - new MediaItem.AdsConfiguration.Builder(adTagUri).build()) - .build(); -~~~ -{: .language-java} - -To enable player support for media items that specify ad tags, it's necessary to -build and inject a `DefaultMediaSourceFactory` configured with an -`AdsLoader.Provider` and an `AdViewProvider` when creating the player: - -~~~ -MediaSource.Factory mediaSourceFactory = - new DefaultMediaSourceFactory(context) - .setLocalAdInsertionComponents( - adsLoaderProvider, /* adViewProvider= */ playerView); -ExoPlayer player = new ExoPlayer.Builder(context) - .setMediaSourceFactory(mediaSourceFactory) - .build(); -~~~ -{: .language-java} - -Internally, `DefaultMediaSourceFactory` will wrap the content media source in an -`AdsMediaSource`. The `AdsMediaSource` will obtain an `AdsLoader` from the -`AdsLoader.Provider` and use it to insert ads as defined by the media item's ad -tag. - -ExoPlayer's `StyledPlayerView` implements `AdViewProvider`. The IMA extension -provides an easy to use `AdsLoader`, as described below. - -### Playlists with ads ### - -When playing a [playlist][] with multiple media items, the default behavior is -to request the ad tag and store ad playback state once for each media ID, -content URI and ad tag URI combination. This means that users will see ads for -every media item with ads that has a distinct media ID or content URI, even if -the ad tag URIs match. If a media item is repeated, the user will see the -corresponding ads only once (the ad playback state stores whether ads have been -played, so they are skipped after their first occurrence). - -It's possible to customize this behavior by passing an opaque ads identifier -with which ad playback state for a given media item is linked, based on object -equality. Here is an example where ad playback state is linked to the ad tag -URI only, rather than the combination of the media ID and ad tag URI, by -passing the ad tag URI as the ads identifier. The effect is that ads will load -only once and the user will not see ads on the second item when playing the -playlist from start to finish. - -~~~ -// Build the media items, passing the same ads identifier for both items, -// which means they share ad playback state so ads play only once. -MediaItem firstItem = - new MediaItem.Builder() - .setUri(firstVideoUri) - .setAdsConfiguration( - new MediaItem.AdsConfiguration.Builder(adTagUri) - .setAdsId(adTagUri) - .build()) - .build(); -MediaItem secondItem = - new MediaItem.Builder() - .setUri(secondVideoUri) - .setAdsConfiguration( - new MediaItem.AdsConfiguration.Builder(adTagUri) - .setAdsId(adTagUri) - .build()) - .build(); -player.addMediaItem(firstItem); -player.addMediaItem(secondItem); -~~~ -{: .language-java} - -### IMA extension ### - -The [ExoPlayer IMA extension][] provides `ImaAdsLoader`, making it easy to -integrate client-side ad insertion into your app. It wraps the functionality of -the [client-side IMA SDK][] to support insertion of VAST/VMAP ads. For -instructions on how to use the extension, including how to handle backgrounding -and resuming playback, please see the [README][]. - -The [demo application][] uses the IMA extension, and includes several sample -VAST/VMAP ad tags in the sample list. - -#### UI considerations #### - -`StyledPlayerView` hides its transport controls during playback of ads by -default, but apps can toggle this behavior by calling -`setControllerHideDuringAds`. The IMA SDK will show additional views on top of -the player while an ad is playing (e.g., a 'more info' link and a skip button, -if applicable). - -Since advertisers expect a consistent experience across apps, the IMA SDK does -not allow customization of the views that it shows while an ad is playing. It is -therefore not possible to remove or reposition the skip button, change the -fonts, or make other customizations to the visual appearance of these views. -{:.info} - -The IMA SDK may report whether ads are obscured by application provided views -rendered on top of the player. Apps that need to overlay views that are -essential for controlling playback must register them with the IMA SDK so that -they can be omitted from viewability calculations. When using `StyledPlayerView` -as the `AdViewProvider`, it will automatically register its control overlays. -Apps that use a custom player UI must register overlay views by returning them -from `AdViewProvider.getAdOverlayInfos`. - -For more information about overlay views, see -[Open Measurement in the IMA SDK][]. - -#### Companion ads #### - -Some ad tags contain additional companion ads that can be shown in 'slots' in an -app UI. These slots can be passed via -`ImaAdsLoader.Builder.setCompanionAdSlots(slots)`. For more information see -[Adding Companion Ads][]. - -#### Standalone ads #### - -The IMA SDK is designed for inserting ads into media content, not for playing -standalone ads by themselves. Hence playback of standalone ads is not supported -by the IMA extension. We recommend using the [Google Mobile Ads SDK][] instead -for this use case. - -### Using a third-party ads SDK ### - -If you need to load ads via a third-party ads SDK, it's worth checking whether -it already provides an ExoPlayer integration. If not, implementing a custom -`AdsLoader` that wraps the third-party ads SDK is the recommended approach, -since it provides the benefits of `AdsMediaSource` described above. -`ImaAdsLoader` acts as an example implementation. - -Alternatively, you can use ExoPlayer's [playlist support][] to build a sequence -of ads and content clips: - -~~~ -// A pre-roll ad. -MediaItem preRollAd = MediaItem.fromUri(preRollAdUri); -// The start of the content. -MediaItem contentStart = - new MediaItem.Builder() - .setUri(contentUri) - .setClippingConfiguration( - new ClippingConfiguration.Builder() - .setEndPositionMs(120_000) - .build()) - .build(); -// A mid-roll ad. -MediaItem midRollAd = MediaItem.fromUri(midRollAdUri); -// The rest of the content -MediaItem contentEnd = - new MediaItem.Builder() - .setUri(contentUri) - .setClippingConfiguration( - new ClippingConfiguration.Builder() - .setStartPositionMs(120_000) - .build()) - .build(); - -// Build the playlist. -player.addMediaItem(preRollAd); -player.addMediaItem(contentStart); -player.addMediaItem(midRollAd); -player.addMediaItem(contentEnd); -~~~ -{: .language-java} - -## Server-side ad insertion ## - -In server-side ad insertion (also called dynamic ad insertion, or DAI), the -media stream contains both ads and content. A DASH manifest may point to both -content and ad segments, possibly in separate periods. For HLS, see the Apple -documentation on [incorporating ads into a playlist][]. - -When using server-side ad insertion, the client may need to resolve the media -URL dynamically to get the stitched stream, it may need to display ads overlays -in the UI or it may need to report events to an ads SDK or ad server. - -ExoPlayer's `DefaultMediaSourceFactory` can delegate all these tasks to a -server-side ad insertion `MediaSource` for URIs using the `ssai://` scheme: - -``` -Player player = - new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context) - .setServerSideAdInsertionMediaSourceFactory(ssaiFactory)) - .build(); -``` - -### IMA extension ### - -The [ExoPlayer IMA extension][] provides `ImaServerSideAdInsertionMediaSource`, -making it easy to integrate with IMA's server-side inserted ad streams in your -app. It wraps the functionality of the [IMA DAI SDK for Android][] and fully -integrates the provided ad metadata into the player. For example, this allows -you to use methods like `Player.isPlayingAd()`, listen to content-ad transitions -and let the player handle ad playback logic like skipping already played ads. - -In order to use this class, you need to set up the -`ImaServerSideAdInsertionMediaSource.AdsLoader` and the -`ImaServerSideAdInsertionMediaSource.Factory` and connect them to the player: - -``` -// MediaSource.Factory to load the actual media stream. -DefaultMediaSourceFactory defaultMediaSourceFactory = - new DefaultMediaSourceFactory(context); -// AdsLoader that can be reused for multiple playbacks. -ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader = - new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider) - .build(); -// MediaSource.Factory to create the ad sources for the current player. -ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory = - new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory); -// Configure DefaultMediaSourceFactory to create both IMA DAI sources and -// regular media sources. If you just play IMA DAI streams, you can also use -// adsMediaSourceFactory directly. -defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory); -// Set the MediaSource.Factory on the Player. -Player player = - new ExoPlayer.Builder(context) - .setMediaSourceFactory(defaultMediaSourceFactory) - .build(); -// Set the player on the AdsLoader -adsLoader.setPlayer(player); -``` - -Load your IMA asset key, or content source id and video id, by building an URL -with `ImaServerSideAdInsertionUriBuilder`: - -``` -Uri ssaiUri = - new ImaServerSideAdInsertionUriBuilder() - .setAssetKey(assetKey) - .setFormat(C.TYPE_HLS) - .build(); -player.setMediaItem(MediaItem.fromUri(ssaiUri)); -``` - -Finally, release your ads loader once it's no longer used: -``` -adsLoader.release(); -``` - -Currently only a single IMA server-side ad insertion stream is supported in the -same playlist. You can combine the stream with other media but not with another -IMA server-side ad insertion stream. -{:.info} - -#### UI considerations #### - -The same [UI considerations as for client-side ad insertion][] apply to -server-side ad insertion too. - -#### Companion ads #### - -Some ad tags contain additional companion ads that can be shown in 'slots' in an -app UI. These slots can be passed via -`ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots)`. -For more information see [Adding Companion Ads][]. - -### Using a third-party ads SDK ### - -If you need to load ads via a third-party ads SDK, it’s worth checking whether -it already provides an ExoPlayer integration. If not, it's recommended to -provide a custom `MediaSource` that accepts URIs with the `ssai://` scheme -similar to `ImaServerSideAdInsertionMediaSource`. - -The actual logic of creating the ad structure can be delegated to the general -purpose `ServerSideAdInsertionMediaSource`, which wraps a stream `MediaSource` -and allows the user to set and update the `AdPlaybackState` representing the ad -metadata. - -Often, server-side inserted ad streams contain timed events to notify the player -about ad metadata. Please see [supported formats][] for information on what -timed metadata formats are supported by ExoPlayer. Custom ads SDK `MediaSource` -implementations can listen for timed metadata events from the player via -`Player.Listener.onMetadata`. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/ad-insertion -[VAST]: https://www.iab.com/wp-content/uploads/2015/06/VASTv3_0.pdf -[VMAP]: https://www.iab.com/guidelines/digital-video-multiple-ad-playlist-vmap-1-0-1/ -[ExoPlayer UI components]: {{ site.baseurl }}/ui-components.html -[ExoPlayer IMA extension]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima -[client-side IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android -[README]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima -[demo application]: {{ site.baseurl }}/demo-application.html -[Open Measurement in the IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android/omsdk -[Adding Companion Ads]: https://developers.google.com/interactive-media-ads/docs/sdks/android/companions -[playlist]: {{ site.baseurl }}/playlists.html -[playlist support]: {{ site.baseurl }}/playlists.html -[incorporating ads into a playlist]: https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/incorporating_ads_into_a_playlist -[supported formats]: {{ site.baseurl }}/supported-formats.html -[Google Mobile Ads SDK]: https://developers.google.com/admob/android/quick-start -[IMA DAI SDK for Android]: https://developers.google.com/interactive-media-ads/docs/sdks/android/dai -[UI considerations as for client-side ad insertion]: #ui-considerations diff --git a/docs/analytics.md b/docs/analytics.md index 9622aee23be..35dc283325f 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -1,269 +1,5 @@ --- -title: Analytics +permalink: /analytics.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/analytics --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer supports a wide range of playback analytics needs. Ultimately, -analytics is about collecting, interpreting, aggregating and summarizing data -from playbacks. This data can be used either on the device, for example for -logging, debugging, or to inform future playback decisions, or reported to a -server to monitor playbacks across all devices. - -An analytics system usually needs to collect events first, and then process them -further to make them meaningful: - -* **Event collection**: - This can be done by registering an `AnalyticsListener` on an `ExoPlayer` - instance. Registered analytics listeners receive events as they occur during - usage of the player. Each event is associated with the corresponding media - item in the playlist, as well as playback position and timestamp metadata. -* **Event processing**: - Some analytics systems upload raw events to a server, with all event - processing performed server-side. It's also possible to process events on the - device, and doing so may be simpler or reduce the amount of information that - needs to be uploaded. ExoPlayer provides `PlaybackStatsListener`, which - allows you to perform the following processing steps: - 1. **Event interpretation**: To be useful for analytics purposes, events need - to be interpreted in the context of a single playback. For example the raw - event of a player state change to `STATE_BUFFERING` may correspond to - initial buffering, a rebuffer, or buffering that happens after a seek. - 1. **State tracking**: This step converts events to counters. For example, - state change events can be converted to counters tracking how much time is - spent in each playback state. The result is a basic set of analytics data - values for a single playback. - 1. **Aggregation**: This step combines the analytics data across multiple - playbacks, typically by adding up counters. - 1. **Calculation of summary metrics**: Many of the most useful metrics are - those that compute averages or combine the basic analytics data values in - other ways. Summary metrics can be calculated for single or multiple - playbacks. - -## Event collection with AnalyticsListener ## - -Raw playback events from the player are reported to `AnalyticsListener` -implementations. You can easily add your own listener and override only the -methods you are interested in: - -~~~ -exoPlayer.addAnalyticsListener(new AnalyticsListener() { - @Override - public void onPlaybackStateChanged( - EventTime eventTime, @Player.State int state) { - } - - @Override - public void onDroppedVideoFrames( - EventTime eventTime, int droppedFrames, long elapsedMs) { - } -}); -~~~ -{: .language-java} - -The `EventTime` that's passed to each callback associates the event to a media -item in the playlist, as well as playback position and timestamp metadata: - -* `realtimeMs`: The wall clock time of the event. -* `timeline`, `windowIndex` and `mediaPeriodId`: Defines the playlist and the - item within the playlist to which the event belongs. The `mediaPeriodId` - contains optional additional information, for example indicating whether the - event belongs to an ad within the item. -* `eventPlaybackPositionMs`: The playback position in the item when the event - occurred. -* `currentTimeline`, `currentWindowIndex`, `currentMediaPeriodId` and - `currentPlaybackPositionMs`: As above but for the currently playing item. The - currently playing item may be different from the item to which the event - belongs, for example if the event corresponds to pre-buffering of the next - item to be played. - -## Event processing with PlaybackStatsListener ## - -`PlaybackStatsListener` is an `AnalyticsListener` that implements on device -event processing. It calculates `PlaybackStats`, with counters and derived -metrics including: - -* Summary metrics, for example the total playback time. -* Adaptive playback quality metrics, for example the average video resolution. -* Rendering quality metrics, for example the rate of dropped frames. -* Resource usage metrics, for example the number of bytes read over the network. - -You will find a complete list of the available counts and derived metrics in the -[`PlaybackStats` Javadoc][]. - -`PlaybackStatsListener` calculates separate `PlaybackStats` for each media item -in the playlist, and also each client-side ad inserted within these items. You -can provide a callback to `PlaybackStatsListener` to be informed about finished -playbacks, and use the `EventTime` passed to the callback to identify which -playback finished. It's possible to [aggregate the analytics data][] for -multiple playbacks. It's also possible to query the `PlaybackStats` for the -current playback session at any time using -`PlaybackStatsListener.getPlaybackStats()`. - -~~~ -exoPlayer.addAnalyticsListener( - new PlaybackStatsListener( - /* keepHistory= */ true, (eventTime, playbackStats) -> { - // Analytics data for the session started at `eventTime` is ready. - })); -~~~ -{: .language-java} - -The constructor of `PlaybackStatsListener` gives the option to keep the full -history of processed events. Note that this may incur an unknown memory overhead -depending on the length of the playback and the number of events. Therefore you -should only turn it on if you need access to the full history of processed -events, rather than just to the final analytics data. - -Note that `PlaybackStats` uses an extended set of states to indicate not only -the state of the media, but also the user intention to play and more detailed -information such as why playback was interrupted or ended: - -| Playback state | User intention to play | No intention to play | -|:---|:---|:---| -| Before playback | `JOINING_FOREGROUND` | `NOT_STARTED`, `JOINING_BACKGROUND` | -| Active playback | `PLAYING` | | -| Interrupted playback | `BUFFERING`, `SEEKING` | `PAUSED`, `PAUSED_BUFFERING`, `SUPPRESSED`, `SUPPRESSED_BUFFERING`, `INTERRUPTED_BY_AD` | -| End states | | `ENDED`, `STOPPED`, `FAILED`, `ABANDONED` | - -The user intention to play is important to distinguish times when the user was -actively waiting for playback to continue from passive wait times. For example, -`PlaybackStats.getTotalWaitTimeMs` returns the total time spent in the -`JOINING_FOREGROUND`, `BUFFERING` and `SEEKING` states, but not the time when -playback was paused. Similarly, `PlaybackStats.getTotalPlayAndWaitTimeMs` will -return the total time with a user intention to play, that is the total active -wait time and the total time spent in the `PLAYING` state. - -### Processed and interpreted events ### - -You can record processed and interpreted events by using `PlaybackStatsListener` -with `keepHistory=true`. The resulting `PlaybackStats` will contain the -following event lists: - -* `playbackStateHistory`: An ordered list of extended playback states with - the `EventTime` at which they started to apply. You can also use - `PlaybackStats.getPlaybackStateAtTime` to look up the state at a given wall - clock time. -* `mediaTimeHistory`: A history of wall clock time and media time pairs allowing - you to reconstruct which parts of the media were played at which time. You can - also use `PlaybackStats.getMediaTimeMsAtRealtimeMs` to look up the playback - position at a given wall clock time. -* `videoFormatHistory` and `audioFormatHistory`: Ordered lists of video and - audio formats used during playback with the `EventTime` at which they started - to be used. -* `fatalErrorHistory` and `nonFatalErrorHistory`: Ordered lists of fatal and - non-fatal errors with the `EventTime` at which they occurred. Fatal errors are - those that ended playback, whereas non-fatal errors may have been recoverable. - -### Single-playback analytics data ### - -This data is automatically collected if you use `PlaybackStatsListener`, even -with `keepHistory=false`. The final values are the public fields that you can -find in the [`PlaybackStats` Javadoc][] and the playback state durations -returned by `getPlaybackStateDurationMs`. For convenience, you'll also find -methods like `getTotalPlayTimeMs` and `getTotalWaitTimeMs` that return the -duration of specific playback state combinations. - -~~~ -Log.d("DEBUG", "Playback summary: " - + "play time = " + playbackStats.getTotalPlayTimeMs() - + ", rebuffers = " + playbackStats.totalRebufferCount); -~~~ -{: .language-java} - -Some values like `totalVideoFormatHeightTimeProduct` are only useful when -calculating derived summary metrics like the average video height, but are -required to correctly combine multiple `PlaybackStats` together. -{:.info} - -### Aggregate analytics data of multiple playbacks ### - -You can combine multiple `PlaybackStats` together by calling -`PlaybackStats.merge`. The resulting `PlaybackStats` will contain the aggregated -data of all merged playbacks. Note that it won't contain the history of -individual playback events, since these cannot be aggregated. - -`PlaybackStatsListener.getCombinedPlaybackStats` can be used to get an -aggregated view of all analytics data collected in the lifetime of a -`PlaybackStatsListener`. - -### Calculated summary metrics ### - -In addition to the basic analytics data, `PlaybackStats` provides many methods -to calculate summary metrics. - -~~~ -Log.d("DEBUG", "Additional calculated summary metrics: " - + "average video bitrate = " + playbackStats.getMeanVideoFormatBitrate() - + ", mean time between rebuffers = " - + playbackStats.getMeanTimeBetweenRebuffers()); -~~~ -{: .language-java} - -## Advanced topics ## - -### Associating analytics data with playback metadata ### - -When collecting analytics data for individual playbacks, you may wish to -associate the playback analytics data with metadata about the media being -played. - -It's advisable to set media-specific metadata with `MediaItem.Builder.setTag`. -The media tag is part of the `EventTime` reported for raw events and when -`PlaybackStats` are finished, so it can be easily retrieved when handling the -corresponding analytics data: - -~~~ -new PlaybackStatsListener( - /* keepHistory= */ false, (eventTime, playbackStats) -> { - Object mediaTag = - eventTime.timeline.getWindow(eventTime.windowIndex, new Window()) - .mediaItem.localConfiguration.tag; - // Report playbackStats with mediaTag metadata. - }); -~~~ -{: .language-java} - -### Reporting custom analytics events ### - -In case you need to add custom events to the analytics data, you need to save -these events in your own data structure and combine them with the reported -`PlaybackStats` later. If it helps, you can extend `DefaultAnalyticsCollector` -to be able to generate `EventTime` instances for your custom events and send -them to the already registered listeners as shown in the following example. - -~~~ -interface ExtendedListener extends AnalyticsListener { - void onCustomEvent(EventTime eventTime); -} - -class ExtendedCollector extends DefaultAnalyticsCollector { - public void customEvent() { - EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); - sendEvent(eventTime, CUSTOM_EVENT_ID, listener -> { - if (listener instanceof ExtendedListener) { - ((ExtendedListener) listener).onCustomEvent(eventTime); - } - }); - } -} - -// Usage - Setup and listener registration. -ExoPlayer player = new ExoPlayer.Builder(context) - .setAnalyticsCollector(new ExtendedCollector()) - .build(); -player.addAnalyticsListener(new ExtendedListener() { - @Override - public void onCustomEvent(EventTime eventTime) { - // Save custom event for analytics data. - } -}); -// Usage - Triggering the custom event. -((ExtendedCollector) player.getAnalyticsCollector()).customEvent(); -~~~ -{: .language-java} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/analytics -[`PlaybackStats` Javadoc]: {{ site.exo_sdk }}/analytics/PlaybackStats.html -[aggregate the analytics data]: {{ site.baseurl }}/analytics.html#aggregate-analytics-data-of-multiple-playbacks diff --git a/docs/battery-consumption.md b/docs/battery-consumption.md index 5295040d5a1..dc7d90086e8 100644 --- a/docs/battery-consumption.md +++ b/docs/battery-consumption.md @@ -1,81 +1,5 @@ --- -title: Battery consumption +permalink: /battery-consumption.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/battery-consumption --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -## How important is battery consumption due to media playback? ## - -Avoiding unnecessary battery consumption is an important aspect of developing a -good Android application. Media playback can be a major cause of battery drain, -however its importance for a particular app heavily depends on its usage -patterns. If an app is only used to play small amounts of media each day, then -the corresponding battery consumption will only be a small percentage of the -total consumption of the device. In such it makes sense to prioritize feature -set and reliability over optimizing for battery when selecting which player to -use. On the other hand, if an app is often used to play large amounts of media -each day, then optimizing for battery consumption should be weighted more -heavily when choosing between a number of viable options. - -## How power efficient is ExoPlayer? ## - -The diverse nature of the Android device and media content ecosystems means that -it’s difficult to make widely applicable statements about ExoPlayer’s battery -consumption, and in particular how it compares with Android’s MediaPlayer API. -Both absolute and relative performance vary by hardware, Android version and the -media being played. Hence the information provided below should be treated as -guidance only. - -### Video playback ### - -For video playback, our measurements show that ExoPlayer and MediaPlayer draw -similar amounts of power. The power required for the display and decoding the -video stream are the same in both cases, and these account for most of the power -consumed during playback. - -Regardless of which media player is used, choosing between `SurfaceView` and -`TextureView` for output can have a significant impact on power consumption. -`SurfaceView` is more power efficient, with `TextureView` increasing total power -draw during video playback by as much as 30% on some devices. `SurfaceView` -should therefore be preferred where possible. Read more about choosing between -`SurfaceView` and `TextureView` -[here]({{ site.baseurl }}/ui-components.html#choosing-a-surface-type). - -Below are some power consumption measurements for playing 1080p and 480p video -on Pixel 2, measured using a [Monsoon power monitor][]. As mentioned above, -these numbers should not be used to draw general conclusions about power -consumption across the Android device and media content ecosystems. - -| | MediaPlayer | ExoPlayer | -|-------------------|:-----------:|:----------| -| SurfaceView 1080p | 202 mAh | 214 mAh | -| TextureView 1080p | 219 mAh | 221 mAh | -| SurfaceView 480p | 194 mAh | 207 mAh | -| TextureView 480p | 212 mAh | 215 mAh | - -### Audio playback ### - -For short audio playbacks or playbacks when the screen is on, using ExoPlayer -does not have a significant impact on power compared to using MediaPlayer. - -For long playbacks with the screen off, ExoPlayer's audio offload mode needs to -be used or ExoPlayer may consume significantly more power than MediaPlayer. -Audio offload allows audio processing to be offloaded from the CPU to a -dedicated signal processor. It is used by default by MediaPlayer but not -ExoPlayer. ExoPlayer introduced support for audio offload in 2.12 as an -experimental feature. See `DefaultRenderersFactory.setEnableAudioOffload` and -`ExoPlayer.experimentalSetOffloadSchedulingEnabled` for more details on how -to enable it. - -Due to SDK API limitations, ExoPlayer's audio offload mode is only available on -devices running Android 10 and above. MediaPlayer can use audio offload on -devices running earlier versions of Android. Whether the increased robustness, -flexibility and feature set that ExoPlayer provides over MediaPlayer is worth -the increased power consumption for audio only use cases on older devices is -something an app developer must decide, taking their requirements and app usage -patterns into account. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/battery-consumption -[Monsoon power monitor]: https://www.msoon.com/battery-configuration diff --git a/docs/customization.md b/docs/customization.md index e28111e510c..98a1c4641d4 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -1,316 +1,5 @@ --- -title: Customization +permalink: /customization.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/customization --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -At the core of the ExoPlayer library is the `Player` interface. A `Player` -exposes traditional high-level media player functionality such as the ability to -buffer media, play, pause and seek. The default implementation `ExoPlayer` is -designed to make few assumptions about (and hence impose few restrictions on) -the type of media being played, how and where it is stored, and how it is -rendered. Rather than implementing the loading and rendering of media directly, -`ExoPlayer` implementations delegate this work to components that are injected -when a player is created or when new media sources are passed to the player. -Components common to all `ExoPlayer` implementations are: - -* `MediaSource` instances that define media to be played, load the media, and - from which the loaded media can be read. `MediaSource` instances are created - from `MediaItem`s by a `MediaSource.Factory` inside the player. They can also - be passed directly to the player using the [media source based playlist API]. -* A `MediaSource.Factory` that converts `MediaItem`s to `MediaSource`s. The - `MediaSource.Factory` is injected when the player is created. -* `Renderer`s that render individual components of the media. `Renderer`s are - injected when the player is created. -* A `TrackSelector` that selects tracks provided by the `MediaSource` to be - consumed by each of the available `Renderer`s. A `TrackSelector` is injected - when the player is created. -* A `LoadControl` that controls when the `MediaSource` buffers more media, and - how much media is buffered. A `LoadControl` is injected when the player is - created. -* A `LivePlaybackSpeedControl` that controls the playback speed during live - playbacks to allow the player to stay close to a configured live offset. A - `LivePlaybackSpeedControl` is injected when the player is created. - -The concept of injecting components that implement pieces of player -functionality is present throughout the library. The default implementations of -some components delegate work to further injected components. This allows many -sub-components to be individually replaced with implementations that are -configured in a custom way. - -## Player customization ## - -Some common examples of customizing the player by injecting components are -described below. - -### Configuring the network stack ### - -We have a page about [customizing the network stack used by ExoPlayer]. - -### Caching data loaded from the network ### - -To temporarily cache media, or for -[playing downloaded media]({{ site.baseurl }}/downloading-media.html#playing-downloaded-content), -you can inject a `CacheDataSource.Factory` into the `DefaultMediaSourceFactory`: - -~~~ -DataSource.Factory cacheDataSourceFactory = - new CacheDataSource.Factory() - .setCache(simpleCache) - .setUpstreamDataSourceFactory(httpDataSourceFactory); - -ExoPlayer player = new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context) - .setDataSourceFactory(cacheDataSourceFactory)) - .build(); -~~~ -{: .language-java} - -### Customizing server interactions ### - -Some apps may want to intercept HTTP requests and responses. You may want to -inject custom request headers, read the server's response headers, modify the -requests' URIs, etc. For example, your app may authenticate itself by injecting -a token as a header when requesting the media segments. - -The following example demonstrates how to implement these behaviors by -injecting a custom `DataSource.Factory` into the `DefaultMediaSourceFactory`: - -~~~ -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(); -~~~ -{: .language-java} - -In the code snippet above, the injected `HttpDataSource` includes the header -`"Header: Value"` in every HTTP request. This behavior is *fixed* for every -interaction with an HTTP source. - -For a more granular approach, you can inject just-in-time behavior using a -`ResolvingDataSource`. The following code snippet shows how to inject -request headers just before interacting with an HTTP source: - -~~~ -DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( - httpDataSourceFactory, - // Provide just-in-time request headers. - dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))); -~~~ -{: .language-java} - -You may also use a `ResolvingDataSource` to perform -just-in-time modifications of the URI, as shown in the following snippet: - -~~~ -DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( - httpDataSourceFactory, - // Provide just-in-time URI resolution logic. - dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri))); -~~~ -{: .language-java} - -### Customizing error handling ### - -Implementing a custom [LoadErrorHandlingPolicy][] allows apps to customize the -way ExoPlayer reacts to load errors. For example, an app may want to fail fast -instead of retrying many times, or may want to customize the back-off logic that -controls how long the player waits between each retry. The following snippet -shows how to implement custom back-off logic: - -~~~ -LoadErrorHandlingPolicy loadErrorHandlingPolicy = - new DefaultLoadErrorHandlingPolicy() { - @Override - public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { - // Implement custom back-off logic here. - } - }; - -ExoPlayer player = - new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context) - .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) - .build(); -~~~ -{: .language-java} - -The `LoadErrorInfo` argument contains more information about the failed load to -customize the logic based on the error type or the failed request. - -### Customizing extractor flags ### - -Extractor flags can be used to customize how individual formats are extracted -from progressive media. They can be set on the `DefaultExtractorsFactory` that's -provided to the `DefaultMediaSourceFactory`. The following example passes a flag -that enables index-based seeking for MP3 streams. - -~~~ -DefaultExtractorsFactory extractorsFactory = - new DefaultExtractorsFactory() - .setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); - -ExoPlayer player = new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context, extractorsFactory)) - .build(); -~~~ -{: .language-java} - -### Enabling constant bitrate seeking ### - -For MP3, ADTS and AMR streams, you can enable approximate seeking using a -constant bitrate assumption with `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING` flags. -These flags can be set for individual extractors using the individual -`DefaultExtractorsFactory.setXyzExtractorFlags` methods as described above. To -enable constant bitrate seeking for all extractors that support it, use -`DefaultExtractorsFactory.setConstantBitrateSeekingEnabled`. - -~~~ -DefaultExtractorsFactory extractorsFactory = - new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true); -~~~ -{: .language-java} - -The `ExtractorsFactory` can then be injected via `DefaultMediaSourceFactory` as -described for customizing extractor flags above. - - -### Enabling asynchronous buffer queueing ### - -Asynchronous buffer queueing is an enhancement in ExoPlayer's rendering -pipeline, which operates `MediaCodec` instances in [asynchronous mode][] and -uses additional threads to schedule decoding and rendering of data. Enabling it -can reduce dropped frames and audio underruns. - -Asynchronous buffer queueing is enabled by default on devices running Android 12 -and above, and can be enabled manually from Android 6. Consider enabling the -feature for specific devices on which you observe dropped frames or audio -underruns, particularly when playing DRM protected or high frame rate content. - -In the simplest case, you need to inject a `DefaultRenderersFactory` to the -player as follows: - -~~~ -DefaultRenderersFactory renderersFactory = - new DefaultRenderersFactory(context) - .forceEnableMediaCodecAsynchronousQueueing(); -ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build(); -~~~ -{: .language-java} - -If you're instantiating renderers directly, pass a -`AsynchronousMediaCodecAdapter.Factory` to the `MediaCodecVideoRenderer` and -`MediaCodecAudioRenderer` constructors. - -### Intercepting method calls with `ForwardingPlayer` ### - -You can customize some of the behavior of a `Player` instance by wrapping it in -a subclass of `ForwardingPlayer` and overriding methods in order to do any of -the following: - -* Access parameters before passing them to the delegate `Player`. -* Access the return value from the delegate `Player` before returning it. -* Re-implement the method completely. - -When overriding `ForwardingPlayer` methods it's important to ensure the -implementation remains self-consistent and compliant with the `Player` -interface, especially when dealing with methods that are intended to have -identical or related behavior. For example: -* If you want to override every 'play' operation, you need to override both - `ForwardingPlayer.play` and `ForwardingPlayer.setPlayWhenReady`, because a - caller will expect the behavior of these methods to be identical when - `playWhenReady = true`. -* If you want to change the seek-forward increment you need to override both - `ForwardingPlayer.seekForward` to perform a seek with your customized - increment, and `ForwardingPlayer.getSeekForwardIncrement` in order to report - the correct customized increment back to the caller. -* If you want to control what `Player.Commands` are advertised by a player - instance, you must override `Player.getAvailableCommands()`, - `Player.isCommandAvailable()` and also listen to the - `Player.Listener.onAvailableCommandsChanged()` callback to get notified of -changes coming from the underlying player. - -## MediaSource customization ## - -The examples above inject customized components for use during playback of all -`MediaItem`s that are passed to the player. Where fine-grained customization is -required, it's also possible to inject customized components into individual -`MediaSource` instances, which can be passed directly to the player. The example -below shows how to customize a `ProgressiveMediaSource` to use a custom -`DataSource.Factory`, `ExtractorsFactory` and `LoadErrorHandlingPolicy`: - -~~~ -ProgressiveMediaSource mediaSource = - new ProgressiveMediaSource.Factory( - customDataSourceFactory, customExtractorsFactory) - .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) - .createMediaSource(MediaItem.fromUri(streamUri)); -~~~ -{: .language-java} - -## Creating custom components ## - -The library provides default implementations of the components listed at the top -of this page for common use cases. An `ExoPlayer` can use these components, but -may also be built to use custom implementations if non-standard behaviors are -required. Some use cases for custom implementations are: - -* `Renderer` – You may want to implement a custom `Renderer` to handle a - media type not supported by the default implementations provided by the - library. -* `TrackSelector` – Implementing a custom `TrackSelector` allows an app - developer to change the way in which tracks exposed by a `MediaSource` are - selected for consumption by each of the available `Renderer`s. -* `LoadControl` – Implementing a custom `LoadControl` allows an app - developer to change the player's buffering policy. -* `Extractor` – If you need to support a container format not currently - supported by the library, consider implementing a custom `Extractor` class. -* `MediaSource` – Implementing a custom `MediaSource` class may be - appropriate if you wish to obtain media samples to feed to renderers in a - custom way, or if you wish to implement custom `MediaSource` compositing - behavior. -* `MediaSource.Factory` – Implementing a custom `MediaSource.Factory` - allows an application to customize the way in which `MediaSource`s are created - from `MediaItem`s. -* `DataSource` – ExoPlayer’s upstream package already contains a number of - `DataSource` implementations for different use cases. You may want to - implement you own `DataSource` class to load data in another way, such as over - a custom protocol, using a custom HTTP stack, or from a custom persistent - cache. - -When building custom components, we recommend the following: - -* If a custom component needs to report events back to the app, we recommend - that you do so using the same model as existing ExoPlayer components, for - example using `EventDispatcher` classes or passing a `Handler` together with - a listener to the constructor of the component. -* We recommended that custom components use the same model as existing ExoPlayer - components to allow reconfiguration by the app during playback. To do this, - custom components should implement `PlayerMessage.Target` and receive - configuration changes in the `handleMessage` method. Application code should - pass configuration changes by calling ExoPlayer’s `createMessage` method, - configuring the message, and sending it to the component using - `PlayerMessage.send`. Sending messages to be delivered on the playback thread - ensures that they are executed in order with any other operations being - performed on the player. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/customization -[customizing the network stack used by ExoPlayer]: {{ site.baseurl }}/network-stacks.html -[LoadErrorHandlingPolicy]: {{ site.exo_sdk }}/upstream/LoadErrorHandlingPolicy.html -[media source based playlist API]: {{ site.baseurl }}/media-sources.html#media-source-based-playlist-api -[asynchronous mode]: https://developer.android.com/reference/android/media/MediaCodec#asynchronous-processing-using-buffers - diff --git a/docs/dash.md b/docs/dash.md index 6f0e5a9d706..7518cce6eee 100644 --- a/docs/dash.md +++ b/docs/dash.md @@ -1,91 +1,5 @@ --- -title: DASH +permalink: /dash.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/dash --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -{% include_relative _page_fragments/supported-formats-dash.md %} - -## Using MediaItem ## - -To play a DASH stream, you need to depend on the DASH module. - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X' -~~~ -{: .language-gradle} - -You can then create a `MediaItem` for a DASH MPD URI and pass it to the player. - -~~~ -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media item to be played. -player.setMediaItem(MediaItem.fromUri(dashUri)); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -If your URI doesn't end with `.mpd`, you can pass `MimeTypes.APPLICATION_MPD` -to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the -content. - -ExoPlayer will automatically adapt between representations defined in the -manifest, taking into account both available bandwidth and device capabilities. - -## Using DashMediaSource ## - -For more customization options, you can create a `DashMediaSource` and pass it -directly to the player instead of a `MediaItem`. - -~~~ -// Create a data source factory. -DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory(); -// Create a DASH media source pointing to a DASH manifest uri. -MediaSource mediaSource = - new DashMediaSource.Factory(dataSourceFactory) - .createMediaSource(MediaItem.fromUri(dashUri)); -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media source to be played. -player.setMediaSource(mediaSource); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Accessing the manifest ## - -You can retrieve the current manifest by calling `Player.getCurrentManifest`. -For DASH you should cast the returned object to `DashManifest`. The -`onTimelineChanged` callback of `Player.Listener` is also called whenever -the manifest is loaded. This will happen once for a on-demand content, and -possibly many times for live content. The code snippet below shows how an app -can do something whenever the manifest is loaded. - -~~~ -player.addListener( - new Player.Listener() { - @Override - public void onTimelineChanged( - Timeline timeline, @Player.TimelineChangeReason int reason) { - Object manifest = player.getCurrentManifest(); - if (manifest != null) { - DashManifest dashManifest = (DashManifest) manifest; - // Do something with the manifest. - } - } - }); -~~~ -{: .language-java} - -## Customizing playback ## - -ExoPlayer provides multiple ways for you to tailor playback experience to your -app's needs. See the [Customization page][] for examples. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/dash -[Customization page]: {{ site.baseurl }}/customization.html diff --git a/docs/debug-logging.md b/docs/debug-logging.md index 4154920d636..f9466e16a91 100644 --- a/docs/debug-logging.md +++ b/docs/debug-logging.md @@ -1,124 +1,5 @@ --- -title: Debug logging +permalink: /debug-logging.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/debug-logging --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -By default ExoPlayer only logs errors. To log player events, the `EventLogger` -class can be used. The additional logging it provides can be helpful for -understanding what the player is doing, as well as for debugging playback -issues. `EventLogger` implements `AnalyticsListener`, so registering an instance -with an `ExoPlayer` is easy: - -``` -player.addAnalyticsListener(new EventLogger()); -``` -{: .language-java} - -The easiest way to observe the log is using Android Studio's [logcat tab][]. You -can select your app as debuggable process by the package name ( -`com.google.android.exoplayer2.demo` if using the demo app) and tell the logcat -tab to log only for that app by selecting 'show only selected application'. It's -possible to further filter the logging with the expression -`EventLogger|ExoPlayerImpl`, to get only logging from `EventLogger` and the -player itself. - -An alternative to using Android Studio's logcat tab is to use the console. For -example: - -~~~ -adb logcat EventLogger:* ExoPlayerImpl:* *:s -~~~ -{: .language-shell} - -### Player information ### - -The `ExoPlayerImpl` class delivers two important lines about the player version, -the device and OS the app is running on and the modules of ExoPlayer that have -been loaded: - -``` -ExoPlayerImpl: Release 2cd6e65 [ExoPlayerLib/2.12.0] [marlin, Pixel XL, Google, 26] [goog.exo.core, goog.exo.ui, goog.exo.dash] -ExoPlayerImpl: Init 2e5194c [ExoPlayerLib/2.12.0] [marlin, Pixel XL, Google, 26] -``` - -### Playback state ### - -Player state changes are logged in lines like the ones below: - -``` -EventLogger: playWhenReady [eventTime=0.00, mediaPos=0.00, window=0, true, USER_REQUEST] -EventLogger: state [eventTime=0.01, mediaPos=0.00, window=0, BUFFERING] -EventLogger: state [eventTime=0.93, mediaPos=0.00, window=0, period=0, READY] -EventLogger: isPlaying [eventTime=0.93, mediaPos=0.00, window=0, period=0, true] -EventLogger: playWhenReady [eventTime=9.40, mediaPos=8.40, window=0, period=0, false, USER_REQUEST] -EventLogger: isPlaying [eventTime=9.40, mediaPos=8.40, window=0, period=0, false] -EventLogger: playWhenReady [eventTime=10.40, mediaPos=8.40, window=0, period=0, true, USER_REQUEST] -EventLogger: isPlaying [eventTime=10.40, mediaPos=8.40, window=0, period=0, true] -EventLogger: state [eventTime=20.40, mediaPos=18.40, window=0, period=0, ENDED] -EventLogger: isPlaying [eventTime=20.40, mediaPos=18.40, window=0, period=0, false] -``` - -In this example playback starts 0.93 seconds after the player is prepared. The -user pauses playback after 9.4 seconds, and resumes playback one second later at -10.4 seconds. Playback ends ten seconds later at 20.4 seconds. The common -elements within the square brackets are: - -* `[eventTime=float]`: The wall clock time since player creation. -* `[mediaPos=float]`: The current playback position. -* `[window=int]`: The current window index. -* `[period=int]`: The current period in that window. - -The final elements in each line indicate the value of the state being reported. - -### Media tracks ### - -Track information is logged when the available or selected tracks change. This -happens at least once at the start of playback. The example below shows track -logging for an adaptive stream: - -``` -EventLogger: tracks [eventTime=0.30, mediaPos=0.00, window=0, period=0, -EventLogger: group [ -EventLogger: [X] Track:0, id=133, mimeType=video/avc, bitrate=261112, codecs=avc1.4d4015, res=426x240, fps=30.0, supported=YES -EventLogger: [X] Track:1, id=134, mimeType=video/avc, bitrate=671331, codecs=avc1.4d401e, res=640x360, fps=30.0, supported=YES -EventLogger: [X] Track:2, id=135, mimeType=video/avc, bitrate=1204535, codecs=avc1.4d401f, res=854x480, fps=30.0, supported=YES -EventLogger: [X] Track:3, id=160, mimeType=video/avc, bitrate=112329, codecs=avc1.4d400c, res=256x144, fps=30.0, supported=YES -EventLogger: [ ] Track:4, id=136, mimeType=video/avc, bitrate=2400538, codecs=avc1.4d401f, res=1280x720, fps=30.0, supported=NO_EXCEEDS_CAPABILITIES -EventLogger: ] -EventLogger: group [ -EventLogger: [ ] Track:0, id=139, mimeType=audio/mp4a-latm, bitrate=48582, codecs=mp4a.40.5, channels=2, sample_rate=22050, supported=YES -EventLogger: [X] Track:1, id=140, mimeType=audio/mp4a-latm, bitrate=127868, codecs=mp4a.40.2, channels=2, sample_rate=44100, supported=YES -EventLogger: ] -EventLogger: ] -``` - -In this example, the player has selected four of the five available video -tracks. The fifth video track is not selected because it exceeds the -capabilities of the device, as indicated by `supported=NO_EXCEEDS_CAPABILITIES`. -The player will adapt between the selected video tracks during playback. When -the player adapts from one track to another, it's logged in a line like the one -below: - -``` -EventLogger: downstreamFormat [eventTime=3.64, mediaPos=3.00, window=0, period=0, id=134, mimeType=video/avc, bitrate=671331, codecs=avc1.4d401e, res=640x360, fps=30.0] -``` - -This log line indicates that the player switched to the 640x360 resolution video -track three seconds into the media. - -### Decoder selection ### - -In most cases ExoPlayer renders media using a `MediaCodec` acquired from the -underlying platform. When a decoder is initialized, this is logged in lines like -the ones below: - -``` -EventLogger: videoDecoderInitialized [0.77, 0.00, window=0, period=0, video, OMX.qcom.video.decoder.avc] -EventLogger: audioDecoderInitialized [0.79, 0.00, window=0, period=0, audio, OMX.google.aac.decoder] -``` - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/debug-logging -[logcat tab]: https://developer.android.com/studio/debug/am-logcat diff --git a/docs/demo-application.md b/docs/demo-application.md index 1a4a5ca8b7a..b7cb9de7d2f 100644 --- a/docs/demo-application.md +++ b/docs/demo-application.md @@ -1,264 +1,5 @@ --- -title: Demo application +permalink: /demo-application.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/demo-application --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer's main demo app serves two primary purposes: - -1. To provide a relatively simple yet fully featured example of ExoPlayer usage. - The demo app can be used as a convenient starting point from which to develop - your own application. -1. To make it easy to try ExoPlayer. The demo app can be used to test playback - of your own content in addition to the included samples. - -This page describes how to get, compile and run the demo app. It also describes -how to use it to play your own media. - -## Getting the code ## - -The source code for the main demo app can be found in the `demos/main` folder of -our [GitHub project][]. If you haven't already done so, clone the project into a -local directory: - -~~~ -git clone https://github.com/google/ExoPlayer.git -~~~ -{: .language-shell} - -Next, open the project in Android Studio. You should see the following in the -Android Project view (the relevant folders of the demo app have been expanded): - -{% include figure.html url="/images/demo-app-project.png" index="1" caption="The project in Android Studio" %} - -## Compiling and running ## - -To compile and run the demo app, select and run the `demo` configuration in -Android Studio. The demo app will install and run on a connected Android device. -We recommend using a physical device if possible. If you wish to use an emulator -instead, please read the emulators section of [Supported devices][] and ensure -that your Virtual Device uses a system image with an API level of at least 23. - -{% include figure.html url="/images/demo-app-screenshots.png" index="2" caption="SampleChooserActivity and PlayerActivity" %} - -The demo app presents of a list of samples (`SampleChooserActivity`). Selecting -a sample will open a second activity (`PlayerActivity`) for playback. The demo -features playback controls and track selection functionality. It also uses -ExoPlayer's `EventLogger` utility class to output useful debug information to -the system log. This logging can be viewed (along with error level logging for -other tags) with the command: - -~~~ -adb logcat EventLogger:V *:E -~~~ -{: .language-shell} - -### Enabling extension decoders ### - -ExoPlayer has a number of extensions that allow use of bundled software -decoders, including AV1, VP9, Opus, FLAC and FFmpeg (audio only). The demo app -can be built to include and use these extensions as follows: - -1. Build each of the extensions that you want to include. Note that this is a - manual process. Refer to the `README.md` file in each extension for - instructions. -1. In Android Studio's Build Variants view, set the build variant for the demo - module to `withDecoderExtensionsDebug` or `withDecoderExtensionsRelease` as - shown below. -1. Compile, install and run the `demo` configuration as normal. - -{% include figure.html url="/images/demo-app-build-variants.png" index="3" caption="Selecting the demo withDecoderExtensionsDebug build variant" %} - -By default an extension decoder will be used only if a suitable platform decoder -does not exist. It is possible to specify that extension decoders should be -preferred, as described in the sections below. - -## Playing your own content ## - -There are multiple ways to play your own content in the demo app. - -### 1. Editing assets/media.exolist.json ### - -The samples listed in the demo app are loaded from `assets/media.exolist.json`. -By editing this JSON file it's possible to add and remove samples from the demo -app. The schema is as follows, where [O] indicates an optional attribute. - -~~~ -[ - { - "name": "Name of heading", - "samples": [ - { - "name": "Name of sample", - "uri": "The URI of the sample", - "extension": "[O] Sample type hint. Values: mpd, ism, m3u8", - "clip_start_position_ms": "[O] A start point to which the sample should be clipped, in milliseconds" - "clip_end_position_ms": "[O] An end point from which the sample should be clipped, in milliseconds" - "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready, clearkey", - "drm_license_uri": "[O] URI of the license server if protected", - "drm_force_default_license_uri": "[O] Whether to force use of "drm_license_uri" for key requests that include their own license URI", - "drm_key_request_properties": "[O] Key request headers if protected", - "drm_session_for_clear_content": "[O] Whether to attach a DRM session to clear video and audio tracks" - "drm_multi_session": "[O] Enables key rotation if protected", - "subtitle_uri": "[O] The URI of a subtitle sidecar file", - "subtitle_mime_type": "[O] The MIME type of subtitle_uri (required if subtitle_uri is set)", - "subtitle_language": "[O] The BCP47 language code of the subtitle file (ignored if subtitle_uri is not set)", - "ad_tag_uri": "[O] The URI of an ad tag to load via the IMA extension" - }, - ...etc - ] - }, - ...etc -] -~~~ -{: .language-json} - -Playlists of samples can be specified using the schema: - -~~~ -[ - { - "name": "Name of heading", - "samples": [ - { - "name": "Name of playlist sample", - "playlist": [ - { - "uri": "The URI of the first sample in the playlist", - "extension": "[O] Sample type hint. Values: mpd, ism, m3u8" - "clip_start_position_ms": "[O] A start point to which the sample should be clipped, in milliseconds" - "clip_end_position_ms": "[O] An end point from which the sample should be clipped, in milliseconds" - "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready, clearkey", - "drm_license_uri": "[O] URI of the license server if protected", - "drm_force_default_license_uri": "[O] Whether to force use of "drm_license_uri" for key requests that include their own license URI", - "drm_key_request_properties": "[O] Key request headers if protected", - "drm_session_for_clear_content": "[O] Whether to attach a DRM session to clear video and audio tracks", - "drm_multi_session": "[O] Enables key rotation if protected", - "subtitle_uri": "[O] The URI of a subtitle sidecar file", - "subtitle_mime_type": "[O] The MIME type of subtitle_uri (required if subtitle_uri is set)", - "subtitle_language": "[O] The BCP47 language code of the subtitle file (ignored if subtitle_uri is not set)" - }, - { - "uri": "The URI of the second sample in the playlist", - ...etc - }, - ...etc - ] - }, - ...etc - ] - }, - ...etc -] -~~~ -{: .language-json} - -If required, key request headers are specified as an object containing a string -attribute for each header: - -~~~ -"drm_key_request_properties": { - "name1": "value1", - "name2": "value2", - ...etc -} -~~~ -{: .language-json} - -In the sample chooser activity, the overflow menu contains options for -specifying whether to prefer extension decoders. - -### 2. Loading an external exolist.json file ### - -The demo app can load external JSON files using the schema above and named -according to the `*.exolist.json` convention. For example if you host such a -file at `https://yourdomain.com/samples.exolist.json`, you can open it in the -demo app using: - -~~~ -adb shell am start -a android.intent.action.VIEW \ - -d https://yourdomain.com/samples.exolist.json -~~~ -{: .language-shell} - -Clicking a `*.exolist.json` link (e.g., in the browser or an email client) on a -device with the demo app installed will also open it in the demo app. Hence -hosting a `*.exolist.json` JSON file provides a simple way of distributing -content for others to try in the demo app. - -### 3. Firing an intent ### - -Intents can be used to bypass the list of samples and launch directly into -playback. To play a single sample set the intent's action to -`com.google.android.exoplayer.demo.action.VIEW` and its data URI to that of the -sample to play. Such an intent can be fired from the terminal using: - -~~~ -adb shell am start -a com.google.android.exoplayer.demo.action.VIEW \ - -d https://yourdomain.com/sample.mp4 -~~~ -{: .language-shell} - -Supported optional extras for a single sample intent are: - -* Sample configuration extras: - * `mime_type` [String] Sample MIME type hint. For example - `application/dash+xml` for DASH content. - * `clip_start_position_ms` [Long] A start point to which the sample should be - clipped, in milliseconds. - * `clip_end_position_ms` [Long] An end point from which the sample should be - clipped, in milliseconds. - * `drm_scheme` [String] DRM scheme if protected. Valid values are `widevine`, - `playready` and `clearkey`. DRM scheme UUIDs are also accepted. - * `drm_license_uri` [String] URI of the license server if protected. - * `drm_force_default_license_uri` [Boolean] Whether to force use of - `drm_license_uri` for key requests that include their own license URI. - * `drm_key_request_properties` [String array] Key request headers packed as - name1, value1, name2, value2 etc. if protected. - * `drm_session_for_clear_content` [Boolean] Whether to attach a DRM session - to clear video and audio tracks. - * `drm_multi_session` [Boolean] Enables key rotation if protected. - * `subtitle_uri` [String] The URI of a subtitle sidecar file. - * `subtitle_mime_type` [String] The MIME type of subtitle_uri (required if - subtitle_uri is set). - * `subtitle_language` [String] The BCP47 language code of the subtitle file - (ignored if subtitle_uri is not set). - * `ad_tag_uri` [String] The URI of an ad tag to load using the - [IMA extension][]. - * `prefer_extension_decoders` [Boolean] Whether extension decoders are - preferred to platform ones. - -When using `adb shell am start` to fire an intent, an optional string extra can -be set with `--es` (e.g., `--es extension mpd`). An optional boolean extra can -be set with `--ez` (e.g., `--ez prefer_extension_decoders TRUE`). An optional -long extra can be set with `--el` (e.g., `--el clip_start_position_ms 5000`). An -optional string array extra can be set with `--esa` (e.g., -`--esa drm_key_request_properties name1,value1`). - -To play a playlist of samples, set the intent's action to -`com.google.android.exoplayer.demo.action.VIEW_LIST`. The sample configuration -extras remain the same as for `com.google.android.exoplayer.demo.action.VIEW`, -except for two differences: - -* The extras' keys should have an underscore and the 0-based index of the sample - as suffix. For example, `extension_0` would hint the sample type for the first - sample. `drm_scheme_1` would set the DRM scheme for the second sample. -* The uri of the sample is passed as an extra with key `uri_`. - -Other extras, which are not sample dependant, do not change. For example, you -can run the following command in the terminal to play a playlist with two items, -overriding the extension of the second item: -~~~ -adb shell am start -a com.google.android.exoplayer.demo.action.VIEW_LIST \ - --es uri_0 https://a.com/sample1.mp4 \ - --es uri_1 https://b.com/sample2.fake_mpd \ - --es extension_1 mpd -~~~ -{: .language-shell} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/demo-application -[IMA extension]: {{ site.release_v2 }}/extensions/ima -[GitHub project]: https://github.com/google/ExoPlayer -[Supported devices]: {{ site.baseurl }}/supported-devices.html diff --git a/docs/design-documents.md b/docs/design-documents.md index 26fe01098d2..0344b121594 100644 --- a/docs/design-documents.md +++ b/docs/design-documents.md @@ -1,36 +1,5 @@ --- -title: Design documents +permalink: /design-documents.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer --- - -To get early feedback from developers, we publish design documents for larger -changes on this page. Feel free to comment on the documents that are still in -"request for comments" (RFC) status. Note that we do not typically update -documents once changes are implemented. - -### RFC status ### - -* There are no documents in RFC status at this time - -### Archive ### - -* [Add support for partially fragmented MP4s][] (July 2020) -* [Audio offload][] (April 2020) -* [Sniffing order optimization][] (April 2020) -* [Masking seek state changes][] (March 2020) -* [Frame-accurate pausing][] (January 2020) -* [Index seeking in MP3 streams][] (January 2020) -* [Low-latency live playback][] (September 2019) -* [Unwrapping Nested Metadata][] (September 2019) -* [Playlist API][] (July 2019) -* [Bandwidth estimation analysis][] (July 2019) - -[Add support for partially fragmented MP4s]: https://docs.google.com/document/d/1NUheADYlqIVVPT8Ch5UbV8DJHLDoxMoOD_L8mvU8tTM -[Audio offload]: https://docs.google.com/document/d/1r6wi6OtJUaI1QU8QLrLJTZieQBFTN1fyBK4U_PoPp3g -[Sniffing order optimization]: https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ -[Masking seek state changes]: https://docs.google.com/document/d/1XeOduvYus9HfwXtOtoRC185T4PK-L4u7JRmNM46Ee4w -[Frame-accurate pausing]: https://docs.google.com/document/d/1xXGvIMAYDWN4BGUNqAplNN-T7rjrW_1EAVXpyCcAqUI -[Index seeking in MP3 streams]: https://docs.google.com/document/d/1ZtQsCFvi_LiwFqhHWy20dJ1XwHLOXE4BJ5SzXWJ9a9E -[Low-latency live playback]: https://docs.google.com/document/d/1z9qwuP7ff9sf3DZboXnhEF9hzW3Ng5rfJVqlGn8N38k -[Unwrapping Nested Metadata]: https://docs.google.com/document/d/1TS13CVmexaLG1C4TdD-4NkX-BCSr_76FaHVOPo6XP1E -[Playlist API]: https://docs.google.com/document/d/11h0S91KI5TB3NNZUtsCzg0S7r6nyTnF_tDZZAtmY93g -[Bandwidth estimation analysis]: https://docs.google.com/document/d/1e3jVkZ6nxNWgCqTNibqV8uJcKo8d597XVl3nJkY7P8c diff --git a/docs/downloading-media.md b/docs/downloading-media.md index 361bd88bcb6..ee82ab9fe4e 100644 --- a/docs/downloading-media.md +++ b/docs/downloading-media.md @@ -1,416 +1,5 @@ --- -title: Downloading media +permalink: /downloading-media.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/downloading-media --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer provides functionality to download media for offline playback. In most -use cases it's desirable for downloads to continue even when your app is in the -background. For these use cases your app should subclass `DownloadService`, and -send commands to the service to add, remove and control the downloads. The -diagram below shows the main classes that are involved. - -{% include figure.html url="/images/downloading.svg" index="1" caption="Classes -for downloading media. The arrow directions indicate the flow of data." -width="85%" %} - -* `DownloadService`: Wraps a `DownloadManager` and forwards commands to it. The - service allows the `DownloadManager` to keep running even when the app is in - the background. -* `DownloadManager`: Manages multiple downloads, loading (and storing) their - states from (and to) a `DownloadIndex`, starting and stopping downloads based - on requirements such as network connectivity, and so on. To download the - content, the manager will typically read the data being downloaded from a - `HttpDataSource`, and write it into a `Cache`. -* `DownloadIndex`: Persists the states of the downloads. - -## Creating a DownloadService ## - -To create a `DownloadService`, you need to subclass it and implement its -abstract methods: - -* `getDownloadManager()`: Returns the `DownloadManager` to be used. -* `getScheduler()`: Returns an optional `Scheduler`, which can restart the - service when requirements needed for pending downloads to progress are met. - ExoPlayer provides these implementations: - * `PlatformScheduler`, which uses [JobScheduler][] (Minimum API is 21). See - the [PlatformScheduler][] javadocs for app permission requirements. - * `WorkManagerScheduler`, which uses [WorkManager][]. -* `getForegroundNotification()`: Returns a notification to be displayed when the - service is running in the foreground. You can use - `DownloadNotificationHelper.buildProgressNotification` to create a - notification in default style. - -Finally, you need to define the service in your `AndroidManifest.xml` file: - -~~~ - - - - - - - -~~~ -{: .language-xml} - -See [`DemoDownloadService`][] and [`AndroidManifest.xml`][] in the ExoPlayer -demo app for a concrete example. - -## Creating a DownloadManager ## - -The following code snippet demonstrates how to instantiate a `DownloadManager`, -which can be returned by `getDownloadManager()` in your `DownloadService`: - -~~~ -// Note: This should be a singleton in your app. -databaseProvider = new StandaloneDatabaseProvider(context); - -// A download cache should not evict media, so should use a NoopCacheEvictor. -downloadCache = new SimpleCache( - downloadDirectory, - new NoOpCacheEvictor(), - databaseProvider); - -// Create a factory for reading the data from the network. -dataSourceFactory = new DefaultHttpDataSource.Factory(); - -// Choose an executor for downloading data. Using Runnable::run will cause each download task to -// download data on its own thread. Passing an executor that uses multiple threads will speed up -// download tasks that can be split into smaller parts for parallel execution. Applications that -// already have an executor for background downloads may wish to reuse their existing executor. -Executor downloadExecutor = Runnable::run; - -// Create the download manager. -downloadManager = new DownloadManager( - context, - databaseProvider, - downloadCache, - dataSourceFactory, - downloadExecutor); - -// Optionally, setters can be called to configure the download manager. -downloadManager.setRequirements(requirements); -downloadManager.setMaxParallelDownloads(3); -~~~ -{: .language-java} - -See [`DemoUtil`][] in the demo app for a concrete example. - -## Adding a download ## - -To add a download you need to create a `DownloadRequest` and send it to your -`DownloadService`. For adaptive streams `DownloadHelper` can be used to help -build a `DownloadRequest`, as described [further down this page][]. The example -below shows how to create a download request: - -~~~ -DownloadRequest downloadRequest = - new DownloadRequest.Builder(contentId, contentUri).build(); -~~~ -{: .language-java} - -where `contentId` is a unique identifier for the content. In simple cases, the -`contentUri` can often be used as the `contentId`, however apps are free to use -whatever ID scheme best suits their use case. `DownloadRequest.Builder` also has -some optional setters. For example, `setKeySetId` and `setData` can be used to -set DRM and custom data that the app wishes to associate with the download, -respectively. The content's MIME type can also be specified using `setMimeType`, -as a hint for cases where the content type cannot be inferred from `contentUri`. - -Once created, the request can be sent to the `DownloadService` to add the -download: - -~~~ -DownloadService.sendAddDownload( - context, - MyDownloadService.class, - downloadRequest, - /* foreground= */ false) -~~~ -{: .language-java} - -where `MyDownloadService` is the app's `DownloadService` subclass, and the -`foreground` parameter controls whether the service will be started in the -foreground. If your app is already in the foreground then the `foreground` -parameter should normally be set to `false`, since the `DownloadService` will -put itself in the foreground if it determines that it has work to do. - -## Removing downloads ## - -A download can be removed by sending a remove command to the `DownloadService`, -where `contentId` identifies the download to be removed: - -~~~ -DownloadService.sendRemoveDownload( - context, - MyDownloadService.class, - contentId, - /* foreground= */ false) -~~~ -{: .language-java} - -You can also remove all downloaded data with -`DownloadService.sendRemoveAllDownloads`. - -## Starting and stopping downloads ## - -A download will only progress if four conditions are met: - -* The download doesn't have a stop reason. -* Downloads aren't paused. -* The requirements for downloads to progress are met. Requirements can specify - constraints on the allowed network types, as well as whether the device should - be idle or connected to a charger. -* The maximum number of parallel downloads is not exceeded. - -All of these conditions can be controlled by sending commands to your -`DownloadService`. - -#### Setting and clearing download stop reasons #### - -It's possible to set a reason for one or all downloads being stopped: - -~~~ -// Set the stop reason for a single download. -DownloadService.sendSetStopReason( - context, - MyDownloadService.class, - contentId, - stopReason, - /* foreground= */ false); - -// Clear the stop reason for a single download. -DownloadService.sendSetStopReason( - context, - MyDownloadService.class, - contentId, - Download.STOP_REASON_NONE, - /* foreground= */ false); -~~~ -{: .language-java} - -where `stopReason` can be any non-zero value (`Download.STOP_REASON_NONE = 0` is -a special value meaning that the download is not stopped). Apps that have -multiple reasons for stopping downloads can use different values to keep track -of why each download is stopped. Setting and clearing the stop reason for all -downloads works the same way as setting and clearing the stop reason for a -single download, except that `contentId` should be set to `null`. - -Setting a stop reason does not remove a download. The partial download will be -retained, and clearing the stop reason will cause the download to continue. -{:.info} - -When a download has a non-zero stop reason, it will be in the -`Download.STATE_STOPPED` state. Stop reasons are persisted in the -`DownloadIndex`, and so are retained if the application process is killed and -later restarted. - -#### Pausing and resuming all downloads #### - -All downloads can be paused and resumed as follows: - -~~~ -// Pause all downloads. -DownloadService.sendPauseDownloads( - context, - MyDownloadService.class, - /* foreground= */ false); - -// Resume all downloads. -DownloadService.sendResumeDownloads( - context, - MyDownloadService.class, - /* foreground= */ false); -~~~ -{: .language-java} - -When downloads are paused, they will be in the `Download.STATE_QUEUED` state. -Unlike [setting stop reasons][], this approach does not persist any state -changes. It only affects the runtime state of the `DownloadManager`. - -#### Setting the requirements for downloads to progress #### - -[`Requirements`][] can be used to specify constraints that must be met for -downloads to proceed. The requirements can be set by calling -`DownloadManager.setRequirements()` when creating the `DownloadManager`, as in -the example [above][]. They can also be changed dynamically by sending a command -to the `DownloadService`: - -~~~ -// Set the download requirements. -DownloadService.sendSetRequirements( - context, - MyDownloadService.class, - requirements, - /* foreground= */ false); -~~~ -{: .language-java} - -When a download cannot proceed because the requirements are not met, it -will be in the `Download.STATE_QUEUED` state. You can query the not met -requirements with `DownloadManager.getNotMetRequirements()`. - -#### Setting the maximum number of parallel downloads #### - -The maximum number of parallel downloads can be set by calling -`DownloadManager.setMaxParallelDownloads()`. This would normally be done when -creating the `DownloadManager`, as in the example [above][]. - -When a download cannot proceed because the maximum number of parallel downloads -are already in progress, it will be in the `Download.STATE_QUEUED` state. - -## Querying downloads ## - -The `DownloadIndex` of a `DownloadManager` can be queried for the state of all -downloads, including those that have completed or failed. The `DownloadIndex` -can be obtained by calling `DownloadManager.getDownloadIndex()`. A cursor that -iterates over all downloads can then be obtained by calling -`DownloadIndex.getDownloads()`. Alternatively, the state of a single download -can be queried by calling `DownloadIndex.getDownload()`. - -`DownloadManager` also provides `DownloadManager.getCurrentDownloads()`, which -returns the state of current (i.e. not completed or failed) downloads only. This -method is useful for updating notifications and other UI components that display -the progress and status of current downloads. - -## Listening to downloads ## - -You can add a listener to `DownloadManager` to be informed when current -downloads change state: - -~~~ -downloadManager.addListener( - new DownloadManager.Listener() { - // Override methods of interest here. - }); -~~~ -{: .language-java} - -See `DownloadManagerListener` in the demo app's [`DownloadTracker`][] class for -a concrete example. - -Download progress updates do not trigger calls on `DownloadManager.Listener`. To -update a UI component that shows download progress, you should periodically -query the `DownloadManager` at your desired update rate. [`DownloadService`][] -contains an example of this, which periodically updates the service foreground -notification. -{:.info} - -## Playing downloaded content ## - -Playing downloaded content is similar to playing online content, except that -data is read from the download `Cache` instead of over the network. - -It's important that you do not try and read files directly from the download -directory. Instead, use ExoPlayer library classes as described below. -{:.info} - -To play downloaded content, create a `CacheDataSource.Factory` using the same -`Cache` instance that was used for downloading, and inject it into -`DefaultMediaSourceFactory` when building the player: - -~~~ -// Create a read-only cache data source factory using the download cache. -DataSource.Factory cacheDataSourceFactory = - new CacheDataSource.Factory() - .setCache(downloadCache) - .setUpstreamDataSourceFactory(httpDataSourceFactory) - .setCacheWriteDataSinkFactory(null); // Disable writing. - -ExoPlayer player = new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context) - .setDataSourceFactory(cacheDataSourceFactory)) - .build(); -~~~ -{: .language-java} - -If the same player instance will also be used to play non-downloaded content -then the `CacheDataSource.Factory` should be configured as read-only to avoid -downloading that content as well during playback. - -Once the player has been configured with the `CacheDataSource.Factory`, it will -have access to the downloaded content for playback. Playing a download is then -as simple as passing the corresponding `MediaItem` to the player. A `MediaItem` -can be obtained from a `Download` using `Download.request.toMediaItem`, or -directly from a `DownloadRequest` using `DownloadRequest.toMediaItem`. - -### MediaSource configuration ### - -The example above makes the download cache available for playback of all -`MediaItem`s. It's also possible to make the download cache available for -individual `MediaSource` instances, which can be passed directly to the player: - -~~~ -ProgressiveMediaSource mediaSource = - new ProgressiveMediaSource.Factory(cacheDataSourceFactory) - .createMediaSource(MediaItem.fromUri(contentUri)); -player.setMediaSource(mediaSource); -player.prepare(); -~~~ -{: .language-java} - -## Downloading and playing adaptive streams ## - -Adaptive streams (e.g. DASH, SmoothStreaming and HLS) normally contain multiple -media tracks. There are often multiple tracks that contain the same content in -different qualities (e.g. SD, HD and 4K video tracks). There may also be -multiple tracks of the same type containing different content (e.g. multiple -audio tracks in different languages). - -For streaming playbacks, a track selector can be used to choose which of the -tracks are played. Similarly, for downloading, a `DownloadHelper` can be used to -choose which of the tracks are downloaded. Typical usage of a `DownloadHelper` -follows these steps: - -1. Build a `DownloadHelper` using one of the `DownloadHelper.forMediaItem` - methods. Prepare the helper and wait for the callback. - ~~~ - DownloadHelper downloadHelper = - DownloadHelper.forMediaItem( - context, - MediaItem.fromUri(contentUri), - new DefaultRenderersFactory(context), - dataSourceFactory); - downloadHelper.prepare(myCallback); - ~~~ - {: .language-java} -1. Optionally, inspect the default selected tracks using `getMappedTrackInfo` - and `getTrackSelections`, and make adjustments using `clearTrackSelections`, - `replaceTrackSelections` and `addTrackSelection`. -1. Create a `DownloadRequest` for the selected tracks by calling - `getDownloadRequest`. The request can be passed to your `DownloadService` to - add the download, as described above. -1. Release the helper using `release()`. - -Playback of downloaded adaptive content requires configuring the player and -passing the corresponding `MediaItem`, as described above. - -When building the `MediaItem`, `MediaItem.localConfiguration.streamKeys` must be -set to match those in the `DownloadRequest` so that the player only tries to -play the subset of tracks that have been downloaded. Using -`Download.request.toMediaItem` and `DownloadRequest.toMediaItem` to build the -`MediaItem` will take care of this for you. - -If you see data being requested from the network when trying to play downloaded -adaptive content, the most likely cause is that the player is trying to adapt to -a track that was not downloaded. Ensure you've set the stream keys correctly. -{:.info} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/downloading-media -[JobScheduler]: {{ site.android_sdk }}/android/app/job/JobScheduler -[PlatformScheduler]: {{ site.exo_sdk }}/scheduler/PlatformScheduler.html -[WorkManager]: https://developer.android.com/topic/libraries/architecture/workmanager/ -[`DemoDownloadService`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java -[`AndroidManifest.xml`]: {{ site.release_v2 }}/demos/main/src/main/AndroidManifest.xml -[`DemoUtil`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java -[`DownloadTracker`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java -[`DownloadService`]: {{ site.release_v2 }}/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java -[`Requirements`]: {{ site.exo_sdk }}/scheduler/Requirements.html -[further down this page]: #downloading-and-playing-adaptive-streams -[above]: #creating-a-downloadmanager -[setting stop reasons]: #setting-and-clearing-download-stop-reasons diff --git a/docs/drm.md b/docs/drm.md index f60f3299dcc..fd7eb6ec4ff 100644 --- a/docs/drm.md +++ b/docs/drm.md @@ -1,116 +1,5 @@ --- -title: Digital rights management +permalink: /drm.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/drm --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer uses Android's [`MediaDrm`][] API to support DRM protected playbacks. -The minimum Android versions required for different supported DRM schemes, along -with the streaming formats for which they're supported, are: - -| DRM scheme | Android version number | Android API level | Supported formats | -|---------|:------------:|:------------:|:---------------------| -| Widevine "cenc" | 4.4 | 19 | DASH, HLS (FMP4 only) | -| Widevine "cbcs" | 7.1 | 25 | DASH, HLS (FMP4 only) | -| ClearKey "cenc" | 5.0 | 21 | DASH | -| PlayReady SL2000 "cenc" | AndroidTV | AndroidTV | DASH, SmoothStreaming, HLS (FMP4 only) | - -In order to play DRM protected content with ExoPlayer, the UUID of the DRM -system and the license server URI should be specified -[when building a media item]({{ site.baseurl }}/media-items.html#protected-content). -The player will then use these properties to build a default implementation of -`DrmSessionManager`, called `DefaultDrmSessionManager`, that's suitable for most -use cases. For some use cases additional DRM properties may be necessary, as -outlined in the sections below. - -### Key rotation ### - -To play streams with rotating keys, pass `true` to -`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media -item. - -### Multi-key content ### - -Multi-key content consists of multiple streams, where some streams use different -keys than others. Multi-key content can be played in one of two ways, depending -on how the license server is configured. - -##### Case 1: License server responds with all keys for the content ##### - -In this case, the license server is configured so that when it receives a -request for one key, it responds with all keys for the content. This case is -handled by ExoPlayer without the need for any special configuration. Adaptation -between streams (e.g. SD and HD video) is seamless even if they use different -keys. - -Where possible, we recommend configuring your license server to behave in this -way. It's the most efficient and robust way to support playback of multikey -content, because it doesn't require the client to make multiple license requests -to access the different streams. - -##### Case 2: License server responds with requested key only ##### - -In this case, the license server is configured to respond with only the key -specified in the request. Multi-key content can be played with this license -server configuration by passing `true` to -`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media -item. - -We do not recommend configuring your license server to behave in this way. It -requires extra license requests to play multi-key content, which is less -efficient and robust than the alternative described above. - -### Offline keys ### - -An offline key set can be loaded by passing the key set ID to -`MediaItem.DrmConfiguration.Builder.setKeySetId` when building the media item. -This allows playback using the keys stored in the offline key set with the -specified ID. - -{% include known-issue-box.html issue-id="3872" description="Only one offline -key set can be specified per playback. As a result, offline playback of -multi-key content is currently supported only when the license server is -configured as described in Case 1 above." %} - -### DRM sessions for clear content ### - -Use of placeholder `DrmSessions` allows `ExoPlayer` to use the same decoders for -clear content as are used when playing encrypted content. When media contains -both clear and encrypted sections, you may want to use placeholder `DrmSessions` -to avoid re-creation of decoders when transitions between clear and encrypted -sections occur. Use of placeholder `DrmSessions` for audio and video tracks can -be enabled by passing `true` to -`MediaItem.DrmConfiguration.Builder.forceSessionsForAudioAndVideoTracks` when -building the media item. - -### Using a custom DrmSessionManager ### - -If an app wants to customise the `DrmSessionManager` used for playback, they can -implement a `DrmSessionManagerProvider` and pass this to the -`MediaSource.Factory` which is [used when building the player]. The provider can -choose whether to instantiate a new manager instance each time or not. To always -use the same instance: - -~~~ -DrmSessionManager customDrmSessionManager = - new CustomDrmSessionManager(/* ... */); -// Pass a drm session manager provider to the media source factory. -MediaSource.Factory mediaSourceFactory = - new DefaultMediaSourceFactory(context) - .setDrmSessionManagerProvider(mediaItem -> customDrmSessionManager); -~~~ -{: .language-java} - -### Improving playback performance ### - -If you're experiencing video stuttering on a device running Android 6 to 11 when -playing DRM protected content, you can try [enabling asynchronous buffer -queueing]. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/drm -[main demo app]: {{ site.release_v2 }}/demos/main -[`MediaDrm`]: {{ site.android_sdk }}/android/media/MediaDrm.html -[used when building the player]: {{ site.baseurl }}/media-sources.html#customizing-media-source-creation -[enabling asynchronous buffer queueing]: {{ site.baseurl }}/customization.html#enabling-asynchronous-buffer-queueing diff --git a/docs/glossary.md b/docs/glossary.md index 52491483f36..62031479e19 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,314 +1,5 @@ --- -title: Glossary +permalink: /glossary.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/glossary --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/glossary - -## General - Media ## - -###### ABR - -Adaptive Bitrate. An ABR algorithm is an algorithm that selects between a number -of [tracks](#track) during playback, where each track presents the same media -but at different bitrates. - -###### Adaptive streaming - -In adaptive streaming, multiple [tracks](#track) are available that present the -same media at different bitrates. The selected track is chosen dynamically -during playback using an [ABR](#abr) algorithm. - -###### Access unit - -A data item within a media [container](#container). Generally refers to a small -piece of the compressed media bitstream that can be decoded and presented to the -user (a video picture or fragment of playable audio). - -###### AV1 - -AOMedia Video 1 [codec](#codec). - -For more information, see the -[Wikipedia page](https://en.wikipedia.org/wiki/AV1). - -###### AVC - -Advanced Video Coding, also known as the H.264 video [codec](#codec). - -For more information, see the -[Wikipedia page](https://en.wikipedia.org/wiki/Advanced_Video_Coding). - -###### Codec - -This term is overloaded and has multiple meanings depending on the context. The -two following definitions are the most commonly used: - -* Hardware or software component for encoding or decoding - [access units](#access-unit). -* Audio or video sample format specification. - -###### Container - -A media container format such as MP4 and Matroska. Such formats are called -container formats because they contain one or more [tracks](#track) of media, -where each track uses a particular [codec](#codec) (e.g. AAC audio and H.264 -video in an MP4 file). Note that some media formats are both a container format -and a codec (e.g. MP3). - -###### DASH - -Dynamic [Adaptive Streaming](#adaptive-streaming) over HTTP. An industry driven -adaptive streaming protocol. It is defined by ISO/IEC 23009, which can be found -on the -[ISO Publicly Available Standards page](https://standards.iso.org/ittf/PubliclyAvailableStandards/). - -###### DRM - -Digital Rights Management. - -For more information, see the -[Wikipedia page](https://en.wikipedia.org/wiki/Digital_rights_management). - -###### Gapless playback - -Process by which the end of a [track](#track) and/or the beginning of the next -track are skipped to avoid a silent gap between tracks. - -For more information, see the -[Wikipedia page](https://en.wikipedia.org/wiki/Gapless_playback). - -###### HEVC - -High Efficiency Video Coding, also known as the H.265 video [codec](#codec). - -###### HLS - -HTTP Live Streaming. Apple’s [adaptive streaming](#adaptive-streaming) protocol. - -For more information, see the -[Apple documentation](https://developer.apple.com/streaming/). - -###### Manifest - -A file that defines the structure and location of media in -[adaptive streaming](#adaptive-streaming) protocols. Examples include -[DASH](#dash) [MPD](#mpd) files, [HLS](#hls) multivariant playlist files and -[Smooth Streaming](#smooth-streaming) manifest files. Not to be confused with an -AndroidManifest XML file. - -###### MPD - -Media Presentation Description. The [manifest](#manifest) file format used in -the [DASH](#dash) [adaptive streaming](#adaptive-streaming) protocol. - -###### PCM - -Pulse-Code Modulation. - -For more information, see the -[Wikipedia page](https://en.wikipedia.org/wiki/Pulse-code_modulation). - -###### Smooth Streaming - -Microsoft’s [adaptive streaming](#adaptive-streaming) protocol. - -For more information, see the -[Microsoft documentation](https://www.iis.net/downloads/microsoft/smooth-streaming). - -###### Track - -A single audio, video, text or metadata stream within a piece of media. A media -file will often contain multiple tracks. For example a video track and an audio -track in a video file, or multiple audio tracks in different languages. In -[adaptive streaming](#adaptive-streaming) there are also multiple tracks -containing the same content at different bitrates. - -## General - Android ## - -###### AudioTrack - -An Android API for playing audio. - -For more information, see the -[Javadoc]({{ site.android_sdk }}/android/media/AudioTrack). - -###### CDM - -Content Decryption Module. A component in the Android platform responsible for -decrypting [DRM](#drm) protected content. CDMs are accessed via Android’s -[`MediaDrm`](#mediadrm) API. - -For more information, see the -[Javadoc]({{ site.android_sdk }}/android/media/MediaDrm). - -###### IMA - -Interactive Media Ads. IMA is an SDK that makes it easy to integrate multimedia -ads into an app. - -For more information, see the -[IMA documentation](https://developers.google.com/interactive-media-ads). - -###### MediaCodec - -An Android API for accessing media [codecs](#codec) (i.e. encoder and decoder -components) in the platform. - -For more information, see the -[Javadoc]({{ site.android_sdk }}/android/media/MediaCodec). - -###### MediaDrm - -An Android API for accessing [CDMs](#cdm) in the platform. - -For more information, see the -[Javadoc]({{ site.android_sdk }}/android/media/MediaDrm). - -###### Audio offload - -The ability to send compressed audio directly to a digital signal processor -(DSP) provided by the device. Audio offload functionality is useful for low -power audio playback. - -For more information, see the -[Android interaction documentation](https://source.android.com/devices/tv/multimedia-tunneling). - -###### Passthrough - -The ability to send compressed audio directly over HDMI, without decoding it -first. This is for example used to play 5.1 surround sound on an Android TV. - -For more information, see the -[Android interaction documentation](https://source.android.com/devices/tv/multimedia-tunneling). - -###### Surface - -See the [Javadoc]({{ site.android_sdk }}/android/view/Surface) -and the -[Android graphics documentation](https://source.android.com/devices/graphics/arch-sh). - -###### Tunneling - -Process by which the Android framework receives compressed video and either -compressed or [PCM](#pcm) audio data and assumes the responsibility for -decoding, synchronizing and rendering it, taking over some tasks usually handled -by the application. Tunneling may improve audio-to-video (AV) synchronization, -may smooth video playback and can reduce the load on the application processor. -It is mostly used on Android TVs. - -For more information, see the -[Android interaction documentation](https://source.android.com/devices/tv/multimedia-tunneling) -and the -[ExoPlayer article](https://medium.com/google-exoplayer/tunneled-video-playback-in-exoplayer-84f084a8094d). - -## ExoPlayer ## - -{% include figure.html url="/images/glossary-exoplayer-architecture.png" index="1" caption="ExoPlayer architecture overview" %} - -{% include figure.html url="/images/glossary-rendering-architecture.png" index="1" caption="ExoPlayer rendering overview" %} - -###### BandwidthMeter - -Component that estimates the network bandwidth, for example by listening to data -transfers. In [adaptive streaming](#adaptive-streaming), bandwidth estimates can -be used to select between different bitrate [tracks](#track) during playback. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/upstream/BandwidthMeter.html). - -###### DataSource - -Component for requesting data (e.g. over HTTP, from a local file, etc). - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/upstream/DataSource.html). - -###### Extractor - -Component that parses a media [container](#container) format, outputting -[track](#track) information and individual [access units](#access-unit) -belonging to each track suitable for consumption by a decoder. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/extractor/Extractor.html). - -###### LoadControl - -Component that decides when to start and stop loading, and when to start -playback. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/LoadControl.html). - -###### MediaSource - -Provides high-level information about the structure of media (as a -[`Timeline`](#timeline)) and creates [`MediaPeriod`](#mediaperiod) instances -(corresponding to periods of the `Timeline`) for playback. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/source/MediaSource.html). - -###### MediaPeriod - -Loads a single piece of media (e.g. audio file, ad, content interleaved between -two ads, etc.), and allows the loaded media to be read (typically by -[`Renderers`](#renderer)). The decisions about which [tracks](#track) within the -media are loaded and when loading starts and stops are made by the -[`TrackSelector`](#trackselector) and the [`LoadControl`](#loadcontrol) -respectively. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/source/MediaPeriod.html). - -###### Renderer - -Component that reads, decodes and renders media samples. [`Surface`](#surface) -and [`AudioTrack`](#audiotrack) are the standard Android platform components to -which video and audio data are rendered. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/Renderer.html). - -###### Timeline - -Represents the structure of media, from simple cases like a single media file -through to complex compositions of media such as playlists and streams with -inserted ads. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/Timeline.html). - -###### TrackGroup - -Group containing one or more representations of the same video, audio or text -content, normally at different bitrates for -[adaptive streaming](#adaptive-streaming). - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/source/TrackGroup.html). - -###### TrackSelection - -A selection consisting of a static subset of [tracks](#track) from a -[`TrackGroup`](#trackgroup), and a possibly varying selected track from the -subset. For [adaptive streaming](#adaptive-streaming), the `TrackSelection` is -responsible for selecting the appropriate track whenever a new media chunk -starts being loaded. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/trackselection/TrackSelection.html). - -###### TrackSelector - -Selects [tracks](#track) for playback. Given track information for the -[`MediaPeriod`](#mediaperiod) to be played, along with the capabilities of the -player’s [`Renderers`](#renderer), a `TrackSelector` will generate a -[`TrackSelection`](#trackselection) for each `Renderer`. - -For more information, see the component -[Javadoc]({{ site.exo_sdk }}/trackselection/TrackSelector.html). diff --git a/docs/hello-world.md b/docs/hello-world.md index 8a6bfed655d..a8ae7a306ad 100644 --- a/docs/hello-world.md +++ b/docs/hello-world.md @@ -1,224 +1,9 @@ --- -title: Hello world! redirect_from: - /guide.html - /guide-v1.html - /getting-started.html +permalink: /hello-world.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/hello-world --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -Another way to get started is to work through -[the ExoPlayer codelab](https://codelabs.developers.google.com/codelabs/exoplayer-intro/). -{:.info} - -For simple use cases, getting started with `ExoPlayer` consists of implementing -the following steps: - -1. Add ExoPlayer as a dependency to your project. -1. Create an `ExoPlayer` instance. -1. Attach the player to a view (for video output and user input). -1. Prepare the player with a `MediaItem` to play. -1. Release the player when done. - -These steps are described in more detail below. For a complete example, refer to -`PlayerActivity` in the [main demo app][]. - -## Adding ExoPlayer as a dependency ## - -### Add ExoPlayer modules ### - -The easiest way to get started using ExoPlayer is to add it as a gradle -dependency in the `build.gradle` file of your app module. The following will add -a dependency to the full library: - -~~~ -implementation 'com.google.android.exoplayer:exoplayer:2.X.X' -~~~ -{: .language-gradle} - -where `2.X.X` is your preferred version (the latest version can be found by -consulting the [release notes][]). - -As an alternative to the full library, you can depend on only the library -modules that you actually need. For example the following will add dependencies -on the Core, DASH and UI library modules, as might be required for an app that -only plays DASH content: - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X' -implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X' -implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X' -~~~ -{: .language-gradle} - -When depending on individual modules, they must all be the same version. You can -browse the list of available modules on the [Google Maven ExoPlayer page][]. The -full library includes all of the library modules prefixed with `exoplayer-`, -except for `exoplayer-transformer`. - -In addition to library modules, ExoPlayer has extension modules that depend on -external libraries to provide additional functionality. Some extensions are -available from the Maven repository, whereas others must be built manually. -Browse the [extensions directory][] and their individual READMEs for details. - -### Turn on Java 8 support ### - -If not enabled already, you need to turn on Java 8 support in all `build.gradle` -files depending on ExoPlayer, by adding the following to the `android` section: - -~~~ -compileOptions { - targetCompatibility JavaVersion.VERSION_1_8 -} -~~~ -{: .language-gradle} - -### Enable multidex ### - -If your Gradle `minSdkVersion` is 20 or lower, you should -[enable multidex](https://developer.android.com/studio/build/multidex) in order -to prevent build errors. - -## Creating the player ## - -You can create an `ExoPlayer` instance using `ExoPlayer.Builder`, which provides -a range of customization options. The code below is the simplest example of -creating an instance. - -~~~ -ExoPlayer player = new ExoPlayer.Builder(context).build(); -~~~ -{: .language-java} - -### A note on threading ### - -ExoPlayer instances must be accessed from a single application thread. For the -vast majority of cases this should be the application's main thread. Using the -application's main thread is a requirement when using ExoPlayer's UI components -or the IMA extension. - -The thread on which an ExoPlayer instance must be accessed can be explicitly -specified by passing a `Looper` when creating the player. If no `Looper` is -specified, then the `Looper` of the thread that the player is created on is -used, or if that thread does not have a `Looper`, the `Looper` of the -application's main thread is used. In all cases the `Looper` of the thread from -which the player must be accessed can be queried using -`Player.getApplicationLooper`. - -If you see `IllegalStateException` being thrown with the message "Player is -accessed on the wrong thread", then some code in your app is accessing an -`ExoPlayer` instance on the wrong thread (the exception's stack trace shows you -where). -{:.info} - -For more information about ExoPlayer's threading model, see the -["Threading model" section of the ExoPlayer Javadoc][]. - -## Attaching the player to a view ## - -The ExoPlayer library provides a range of pre-built UI components for media -playback. These include `StyledPlayerView`, which encapsulates a -`StyledPlayerControlView`, a `SubtitleView`, and a `Surface` onto which video is -rendered. A `StyledPlayerView` can be included in your application's layout xml. -Binding the player to the view is as simple as: - -~~~ -// Bind the player to the view. -playerView.setPlayer(player); -~~~ -{: .language-java} - -You can also use `StyledPlayerControlView` as a standalone component, which is -useful for audio only use cases. - -Use of ExoPlayer's pre-built UI components is optional. For video applications -that implement their own UI, the target `SurfaceView`, `TextureView`, -`SurfaceHolder` or `Surface` can be set using `ExoPlayer`'s -`setVideoSurfaceView`, `setVideoTextureView`, `setVideoSurfaceHolder` and -`setVideoSurface` methods respectively. `ExoPlayer`'s `addTextOutput` method can -be used to receive captions that should be rendered during playback. - -## Populating the playlist and preparing the player ## - -In ExoPlayer every piece of media is represented by a `MediaItem`. To play a -piece of media you need to build a corresponding `MediaItem`, add it to the -player, prepare the player, and call `play` to start the playback: - -~~~ -// Build the media item. -MediaItem mediaItem = MediaItem.fromUri(videoUri); -// Set the media item to be played. -player.setMediaItem(mediaItem); -// Prepare the player. -player.prepare(); -// Start the playback. -player.play(); -~~~ -{: .language-java} - -ExoPlayer supports playlists directly, so it's possible to prepare the player -with multiple media items to be played one after the other: - -~~~ -// Build the media items. -MediaItem firstItem = MediaItem.fromUri(firstVideoUri); -MediaItem secondItem = MediaItem.fromUri(secondVideoUri); -// Add the media items to be played. -player.addMediaItem(firstItem); -player.addMediaItem(secondItem); -// Prepare the player. -player.prepare(); -// Start the playback. -player.play(); -~~~ -{: .language-java} - -The playlist can be updated during playback without the need to prepare the -player again. Read more about populating and manipulating the playlist on the -[Playlists page][]. Read more about the different options available when -building media items, such as clipping and attaching subtitle files, on the -[Media items page][]. - -Prior to ExoPlayer 2.12, the player needed to be given a `MediaSource` rather -than media items. From 2.12 onwards, the player converts media items to the -`MediaSource` instances that it needs internally. Read more about this process -and how it can be customized on the [Media sources page][]. It's still possible -to provide `MediaSource` instances directly to the player using -`ExoPlayer.setMediaSource(s)` and `ExoPlayer.addMediaSource(s)`. -{:.info} - -## Controlling the player ## - -Once the player has been prepared, playback can be controlled by calling methods -on the player. Some of the most commonly used methods are listed below. - -* `play` and `pause` start and pause playback. -* `seekTo` allows seeking within the media. -* `hasPrevious`, `hasNext`, `previous` and `next` allow navigating through the - playlist. -* `setRepeatMode` controls if and how media is looped. -* `setShuffleModeEnabled` controls playlist shuffling. -* `setPlaybackParameters` adjusts playback speed and audio pitch. - -If the player is bound to a `StyledPlayerView` or `StyledPlayerControlView`, -then user interaction with these components will cause corresponding methods on -the player to be invoked. - -## Releasing the player ## - -It's important to release the player when it's no longer needed, so as to free -up limited resources such as video decoders for use by other applications. This -can be done by calling `ExoPlayer.release`. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/hello-world -[main demo app]: {{ site.release_v2 }}/demos/main/ -[extensions directory]: {{ site.release_v2 }}/extensions/ -[release notes]: {{ site.release_v2 }}/RELEASENOTES.md -["Threading model" section of the ExoPlayer Javadoc]: {{ site.exo_sdk }}/ExoPlayer.html -[Playlists page]: {{ site.baseurl }}/playlists.html -[Media items page]: {{ site.baseurl }}/media-items.html -[Media sources page]: {{ site.baseurl }}/media-sources.html -[Google Maven ExoPlayer page]: https://maven.google.com/web/index.html#com.google.android.exoplayer diff --git a/docs/hls.md b/docs/hls.md index be0c8d04ac7..3c2d7e69210 100644 --- a/docs/hls.md +++ b/docs/hls.md @@ -1,139 +1,5 @@ --- -title: HLS +permalink: /hls.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/hls --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -{% include_relative _page_fragments/supported-formats-hls.md %} - -## Using MediaItem ## - -To play an HLS stream, you need to depend on the HLS module. - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-hls:2.X.X' -~~~ -{: .language-gradle} - -You can then create a `MediaItem` for an HLS playlist URI and pass it to the -player. - -~~~ -// 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(); -~~~ -{: .language-java} - -If your URI doesn't end with `.m3u8`, you can pass `MimeTypes.APPLICATION_M3U8` -to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the -content. - -The URI of the media item may point to either a media playlist or a multivariant -playlist. If the URI points to a multivariant playlist that declares multiple -`#EXT-X-STREAM-INF` tags then ExoPlayer will automatically adapt between -variants, taking into account both available bandwidth and device capabilities. - -## Using HlsMediaSource ## - -For more customization options, you can create a `HlsMediaSource` and pass it -directly to the player instead of a `MediaItem`. - -~~~ -// 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 media source to be played. -player.setMediaSource(hlsMediaSource); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Accessing the manifest ## - -You can retrieve the current manifest by calling `Player.getCurrentManifest`. -For HLS you should cast the returned object to `HlsManifest`. The -`onTimelineChanged` callback of `Player.Listener` is also called whenever -the manifest is loaded. This will happen once for a on-demand content, and -possibly many times for live content. The code snippet below shows how an app -can do something whenever the manifest is loaded. - -~~~ -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. - } - } - }); -~~~ -{: .language-java} - -## Customizing playback ## - -ExoPlayer provides multiple ways for you to tailor playback experience to your -app's needs. See the [Customization page][] for examples. - -### Disabling chunkless preparation ### - -By default, ExoPlayer will use chunkless preparation. This means that ExoPlayer -will only use the information in the multivariant playlist to prepare the -stream, which works if the `#EXT-X-STREAM-INF` tags contain the `CODECS` -attribute. - -You may need to disable this feature if your media segments contain muxed -closed-caption tracks that are not declared in the multivariant playlist with a -`#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS` tag. Otherwise, these closed-caption tracks -won't be detected and played. You can disable chunkless preparation in the -`HlsMediaSource.Factory` as shown in the following snippet. Note that this -will increase start up time as ExoPlayer needs to download a media segment to -discover these additional tracks and it is preferable to declare the -closed-caption tracks in the multivariant playlist instead. -~~~ -HlsMediaSource hlsMediaSource = - new HlsMediaSource.Factory(dataSourceFactory) - .setAllowChunklessPreparation(false) - .createMediaSource(MediaItem.fromUri(hlsUri)); -~~~ -{: .language-java} - -## Creating high quality HLS content ## - -In order to get the most out of ExoPlayer, there are certain guidelines you can -follow to improve your HLS content. Read our [Medium post about HLS playback in -ExoPlayer][] for a full explanation. The main points are: - -* Use precise segment durations. -* Use a continuous media stream; avoid changes in the media structure across - segments. -* Use the `#EXT-X-INDEPENDENT-SEGMENTS` tag. -* Prefer demuxed streams, as opposed to files that include both video and audio. -* Include all information you can in the Multivariant Playlist. - -The following guidelines apply specifically for live streams: - -* Use the `#EXT-X-PROGRAM-DATE-TIME` tag. -* Use the `#EXT-X-DISCONTINUITY-SEQUENCE` tag. -* Provide a long live window. One minute or more is great. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/hls -[HlsMediaSource]: {{ site.exo_sdk }}/source/hls/HlsMediaSource.html -[HTTP Live Streaming]: https://tools.ietf.org/html/rfc8216 -[Customization page]: {{ site.baseurl }}/customization.html -[Medium post about HLS playback in ExoPlayer]: https://medium.com/google-exoplayer/hls-playback-in-exoplayer-a33959a47be7 diff --git a/docs/index.md b/docs/index.md index ebdac8a04d6..c1e8286cd4c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,33 +1,5 @@ --- -layout: article +permalink: /index.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer is an application level media player for Android. It provides an -alternative to Android’s MediaPlayer API for playing audio and video both -locally and over the Internet. ExoPlayer supports features not currently -supported by Android’s MediaPlayer API, including DASH and SmoothStreaming -adaptive playbacks. Unlike the MediaPlayer API, ExoPlayer is easy to customize -and extend, and can be updated through Play Store application updates. - -This website provides a wealth of information to help you get started. In -addition, you can: - -* Learn how to add ExoPlayer to your app by [completing the codelab][] or - reading the [Hello world][] documentation. -* Read news, hints and tips on our [developer blog][]. -* Read the latest [release notes][]. -* Browse the library [Javadoc][]. -* Browse the source code for the [latest release][] and current [tip of tree][]. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer -[completing the codelab]: https://codelabs.developers.google.com/codelabs/exoplayer-intro/ -[Hello world]: {{ site.baseurl }}/hello-world.html -[developer blog]: https://medium.com/google-exoplayer -[release notes]: {{ site.release_v2 }}/RELEASENOTES.md -[Javadoc]: {{ site.baseurl }}/doc/reference -[latest release]: {{ site.release_v2 }} -[tip of tree]: https://github.com/google/ExoPlayer/tree/dev-v2 diff --git a/docs/listening-to-player-events.md b/docs/listening-to-player-events.md index dc861f9fddc..9a0c6f4a23c 100644 --- a/docs/listening-to-player-events.md +++ b/docs/listening-to-player-events.md @@ -1,245 +1,5 @@ --- -title: Player events +permalink: /listening-to-player-events.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/listening-to-player-events --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -## Listening to playback events ## - -Events such as changes in state and playback errors are reported to registered -[`Player.Listener`][] instances. Registering a listener to receive such -events is easy: - -~~~ -// Add a listener to receive events from the player. -player.addListener(listener); -~~~ -{: .language-java } - -`Player.Listener` has empty default methods, so you only need to implement -the methods you're interested in. See the [Javadoc][] for a full description of -the methods and when they're called. Some of the most important methods are -described in more detail below. - -Listeners have the choice between implementing individual event callbacks or a -generic `onEvents` callback that's called after one or more events occur -together. See [`Individual callbacks vs onEvents`][] for an explanation of which -should be preferred for different use cases. - -### Playback state changes ### - -Changes in player state can be received by implementing -`onPlaybackStateChanged(@State int state)` in a registered -`Player.Listener`. The player can be in one of four playback states: - -* `Player.STATE_IDLE`: This is the initial state, the state when the player is - stopped, and when playback failed. The player will hold only limited resources - in this state. -* `Player.STATE_BUFFERING`: The player is not able to immediately play from its - current position. This mostly happens because more data needs to be loaded. -* `Player.STATE_READY`: The player is able to immediately play from its current - position. -* `Player.STATE_ENDED`: The player finished playing all media. - -In addition to these states, the player has a `playWhenReady` flag to indicate -the user intention to play. Changes in this flag can be received by implementing -`onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason)`. - -A player is playing (i.e., its position is advancing and media is being -presented to the user) when it's in the `Player.STATE_READY` state, -`playWhenReady` is `true`, and playback is not suppressed for a reason returned -by `Player.getPlaybackSuppressionReason`. Rather than having to check these -properties individually, `Player.isPlaying` can be called. Changes to this -state can be received by implementing `onIsPlayingChanged(boolean isPlaying)`: - -~~~ -@Override -public void onIsPlayingChanged(boolean isPlaying) { - if (isPlaying) { - // Active playback. - } else { - // Not playing because playback is paused, ended, suppressed, or the player - // is buffering, stopped or failed. Check player.getPlayWhenReady, - // player.getPlaybackState, player.getPlaybackSuppressionReason and - // player.getPlaybackError for details. - } -} -~~~ -{: .language-java } - -### Playback errors ### - -Errors that cause playback to fail can be received by implementing -`onPlayerError(PlaybackException error)` in a registered -`Player.Listener`. When a failure occurs, this method will be called -immediately before the playback state transitions to `Player.STATE_IDLE`. -Failed or stopped playbacks can be retried by calling `ExoPlayer.prepare`. - -Note that some [`Player`][] implementations pass instances of subclasses of -`PlaybackException` to provide additional information about the failure. For -example, [`ExoPlayer`][] passes [`ExoPlaybackException`][] which has `type`, -`rendererIndex` and other ExoPlayer-specific fields. - -The following example shows how to detect when a playback has failed due to an -HTTP networking issue: - -~~~ -@Override -public void onPlayerError(PlaybackException error) { - Throwable cause = error.getCause(); - if (cause instanceof HttpDataSourceException) { - // An HTTP error occurred. - HttpDataSourceException httpError = (HttpDataSourceException) cause; - // It's possible to find out more about the error both by casting and by - // querying the cause. - if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { - // Cast to InvalidResponseCodeException and retrieve the response code, - // message and headers. - } else { - // Try calling httpError.getCause() to retrieve the underlying cause, - // although note that it may be null. - } - } -} -~~~ -{: .language-java } - -### Playlist transitions ### - -Whenever the player changes to a new media item in the playlist -`onMediaItemTransition(MediaItem mediaItem, -@MediaItemTransitionReason int reason)` is called on registered -`Player.Listener`s. The reason indicates whether this was an automatic -transition, a seek (for example after calling `player.next()`), a repetition of -the same item, or caused by a playlist change (e.g., if the currently playing -item is removed). - -### Seeking ### - -Calling `Player.seekTo` methods results in a series of callbacks to registered -`Player.Listener` instances: - -1. `onPositionDiscontinuity` with `reason=DISCONTINUITY_REASON_SEEK`. This is - the direct result of calling `Player.seekTo`. -1. `onPlaybackStateChanged` with any immediate state change related to the seek. - Note that there might not be such a change. - -If you are using an `AnalyticsListener`, there will be an additional event -`onSeekStarted` just before `onPositionDiscontinuity`, to indicate the playback -position immediately before the seek started. - -### Individual callbacks vs onEvents ### - -Listeners can choose between implementing individual callbacks like -`onIsPlayingChanged(boolean isPlaying)`, and the generic -`onEvents(Player player, Events events)` callback. The generic callback provides -access to the `Player` object and specifies the set of `events` that occurred -together. It's always called after the callbacks that correspond to the -individual events. - -~~~ -@Override -public void onEvents(Player player, Events events) { - if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) - || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) { - uiModule.updateUi(player); - } -} -~~~ -{: .language-java } - -Individual events should be preferred in the following cases: -* The listener is interested in the reasons for changes. For example the reasons - provided for `onPlayWhenReadyChanged` or `onMediaItemTransition`. -* The listener only acts on the new values provided through callback parameters, - or triggers something else that doesn't depend on the callback parameters. -* The listener implementation prefers a clear readable indication of what - triggered the event in the method name. -* The listener reports to an analytics system that needs to know about all - individual events and state changes. - -The generic `onEvents(Player player, Events events)` should be preferred in the -following cases: -* The listener wants to trigger the same logic for multiple events. For - example updating a UI for both `onPlaybackStateChanged` and - `onPlayWhenReadyChanged`. -* The listener needs access the `Player` object to trigger further events, - for example seeking after a media item transition. -* The listener intends to use multiple state values that are reported - through separate callbacks together, or in combination with `Player` getter - methods. For example, using `Player.getCurrentWindowIndex()` with the - `Timeline` provided in `onTimelineChanged` is only safe from within the - `onEvents` callback. -* The listener is interested in whether events logically occurred together. For - example `onPlaybackStateChanged` to `STATE_BUFFERING` because of a media item - transition. - -In some cases, listeners may need to combine the individual callbacks with the -generic `onEvents` callback, for example to record media item change reasons -with `onMediaItemTransition`, but only act once all state changes can be used -together in `onEvents`. - -## Using AnalyticsListener ## - -When using `ExoPlayer`, an `AnalyticsListener` can be registered with the player -by calling `addAnalyticsListener`. `AnalyticsListener` implementations are able -to listen to detailed events that may be useful for analytics and logging -purposes. Please refer to the [analytics page][] for more details. - -### Using EventLogger ### - -`EventLogger` is an `AnalyticsListener` provided directly by the library for -logging purposes. It can be added to an `ExoPlayer` to enable useful -additional logging with a single line. - -``` -player.addAnalyticsListener(new EventLogger()); -``` -{: .language-java } - -See the [debug logging page][] for more details. - -## Firing events at specified playback positions ## - -Some use cases require firing events at specified playback positions. This is -supported using `PlayerMessage`. A `PlayerMessage` can be created using -`ExoPlayer.createMessage`. The playback position at which it should be executed -can be set using `PlayerMessage.setPosition`. Messages are executed on the -playback thread by default, but this can be customized using -`PlayerMessage.setLooper`. `PlayerMessage.setDeleteAfterDelivery` can be used -to control whether the message will be executed every time the specified -playback position is encountered (this may happen multiple times due to seeking -and repeat modes), or just the first time. Once the `PlayerMessage` is -configured, it can be scheduled using `PlayerMessage.send`. - -~~~ -player - .createMessage( - (messageType, payload) -> { - // Do something at the specified playback position. - }) - .setLooper(Looper.getMainLooper()) - .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000) - .setPayload(customPayloadData) - .setDeleteAfterDelivery(false) - .send(); -~~~ -{: .language-java } - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/listening-to-player-events -[`Player.Listener`]: {{ site.exo_sdk }}/Player.Listener.html -[Javadoc]: {{ site.exo_sdk }}/Player.Listener.html -[`Individual callbacks vs onEvents`]: #individual-callbacks-vs-onevents -[`Player`]: {{ site.exo_sdk }}/Player.html -[`ExoPlayer`]: {{ site.exo_sdk }}/ExoPlayer.html -[`ExoPlaybackException`]: {{ site.exo_sdk }}/ExoPlaybackException.html -[log output]: event-logger.html -[`Parameters`]: {{ site.exo_sdk }}/trackselection/DefaultTrackSelector.Parameters.html -[`ParametersBuilder`]: {{ site.exo_sdk }}/trackselection/DefaultTrackSelector.ParametersBuilder.html -[`DefaultTrackSelector`]: {{ site.exo_sdk }}/trackselection/DefaultTrackSelector.html -[ExoPlayer demo app]: {{ site.baseurl }}/demo-application.html#playing-your-own-content -[latest ExoPlayer version]: https://github.com/google/ExoPlayer/releases -[analytics page]: {{ site.baseurl }}/analytics.html -[debug logging page]: {{ site.baseurl }}/debug-logging.html diff --git a/docs/live-streaming.md b/docs/live-streaming.md index 4f62219d32c..e45edf98c14 100644 --- a/docs/live-streaming.md +++ b/docs/live-streaming.md @@ -1,218 +1,5 @@ --- -title: Live streaming +permalink: /live-streaming.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/live-streaming --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -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} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/live-streaming -[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 diff --git a/docs/media-items.md b/docs/media-items.md index c4d701101e2..016680d1b0b 100644 --- a/docs/media-items.md +++ b/docs/media-items.md @@ -1,164 +1,5 @@ --- -title: Media items +permalink: /media-items.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/media-items --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -The [playlist API][] is based on `MediaItem`s, which can be conveniently built -using `MediaItem.Builder`. Inside the player, media items are converted into -playable `MediaSource`s by a `MediaSource.Factory`. Without -[custom configuration]({{ site.baseurl }}/media-sources.html#customizing-media-source-creation), -this conversion is carried out by a `DefaultMediaSourceFactory`, which is -capable of building complex media sources corresponding to the properties of the -media item. Some of the properties that can be set on media items are outlined -below. - -## Simple media items ## - -A media item consisting only of the stream URI can be built with the `fromUri` -convenience method: - -~~~ -MediaItem mediaItem = MediaItem.fromUri(videoUri); -~~~ -{: .language-java} - -For all other cases a `MediaItem.Builder` can be used. In the example below, a -media item is built with an ID and some attached metadata: - -~~~ -MediaItem mediaItem = new MediaItem.Builder() - .setUri(videoUri) - .setMediaId(mediaId) - .setTag(metadata) - .build(); -~~~ -{: .language-java} - -Attaching metadata can be useful for -[updating your app's UI]({{ site.baseurl }}/playlists.html#detecting-when-playback-transitions-to-another-media-item) -when playlist transitions occur. - -## Handling non-standard file extensions - -The ExoPlayer library provides adaptive media sources for DASH, HLS and -SmoothStreaming. If the URI of such an adaptive media item ends with a standard -file extension, the corresponding media source is automatically created. If the -URI has a non-standard extension or no extension at all, then the MIME type can -be set explicitly to indicate the type of the media item: - -~~~ -// Use the explicit MIME type to build an HLS media item. -MediaItem mediaItem = new MediaItem.Builder() - .setUri(hlsUri) - .setMimeType(MimeTypes.APPLICATION_M3U8) - .build(); -~~~ -{: .language-java} - -For progressive media streams a MIME type is not required. - -## Protected content ## - -For protected content, the media item's DRM properties should be set: - -~~~ -MediaItem mediaItem = new MediaItem.Builder() - .setUri(videoUri) - .setDrmConfiguration( - new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) - .setLicenseUri(licenseUri) - .setMultiSession(true) - .setLicenseRequestHeaders(httpRequestHeaders) - .build()) - .build(); -~~~ -{: .language-java} - -This example builds a media item for Widevine protected content. Inside the -player, `DefaultMediaSourceFactory` will pass these properties to a -`DrmSessionManagerProvider` to obtain a `DrmSessionManager`, which is then -injected into the created `MediaSource`. DRM behaviour can be -[further customized]({{ site.baseurl }}/drm.html#using-a-custom-drmsessionmanager) -to your needs. - -## Sideloading subtitle tracks ## - -To sideload subtitle tracks, `MediaItem.Subtitle` instances can be added when -when building a media item: - -~~~ -MediaItem.SubtitleConfiguration subtitle = - new MediaItem.SubtitleConfiguration.Builder(subtitleUri) - .setMimeType(mimeType) // The correct MIME type (required). - .setLanguage(language) // The subtitle language (optional). - .setSelectionFlags(selectionFlags) // Selection flags for the track (optional). - .build(); -MediaItem mediaItem = - new MediaItem.Builder() - .setUri(videoUri) - .setSubtitleConfigurations(ImmutableList.of(subtitle)) - .build(); -~~~ -{: .language-java} - -Internally, `DefaultMediaSourceFactory` will use a `MergingMediaSource` to -combine the content media source with a `SingleSampleMediaSource` for each -subtitle track. `DefaultMediaSourceFactory` does not support sideloading -subtitles for multi-period DASH. - -## Clipping a media stream ## - -It's possible to clip the content referred to by a media item by setting custom -start and end positions: - -~~~ -MediaItem mediaItem = - new MediaItem.Builder() - .setUri(videoUri) - .setClippingConfiguration( - new ClippingConfiguration.Builder() - .setStartPositionMs(startPositionMs) - .setEndPositionMs(endPositionMs) - .build()) - .build(); -~~~ -{: .language-java} - -Internally, `DefaultMediaSourceFactory` will use a `ClippingMediaSource` to wrap -the content media source. There are additional clipping properties. See the -[`MediaItem.Builder` Javadoc][] for more details. - -When clipping the start of a video file, try to align the start position with a -keyframe if possible. If the start position is not aligned with a keyframe then -the player will need to decode and discard data from the previous keyframe up to -the start position before playback can begin. This will introduce a short delay -at the start of playback, including when the player transitions to playing a -clipped media source as part of a playlist or due to looping. -{:.info} - -## Ad insertion ## - -To insert ads, a media item's ad tag URI property should be set: - -~~~ -MediaItem mediaItem = new MediaItem.Builder() - .setUri(videoUri) - .setAdsConfiguration( - new MediaItem.AdsConfiguration.Builder(adTagUri).build()) - .build(); -~~~ -{: .language-java} - -Internally, `DefaultMediaSourceFactory` will wrap the content media source in an -`AdsMediaSource` to insert ads as defined by the ad tag. For this to work, the -the player also needs to have its `DefaultMediaSourceFactory` -[configured accordingly]({{ site.baseurl }}/ad-insertion.html#declarative-ad-support). - -{% include media3-known-issue-box.html issue-id="185" description="Subtitles, clipping and ad insertion are only supported if you use `DefaultMediaSourceFactory`." %} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/media-items -[playlist API]: {{ site.baseurl }}/playlists.html -[`MediaItem.Builder` Javadoc]: {{ site.exo_sdk }}/MediaItem.Builder.html diff --git a/docs/media-sources.md b/docs/media-sources.md index c8dabe7fd70..6fcb103b889 100644 --- a/docs/media-sources.md +++ b/docs/media-sources.md @@ -1,88 +1,5 @@ --- -title: Media sources -redirect_from: - - /mediasource.html +permalink: /media-sources.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/media-sources --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -In ExoPlayer every piece of media is represented by a `MediaItem`. However -internally, the player needs `MediaSource` instances to play the content. The -player creates these from media items using a `MediaSource.Factory`. - -By default the player uses a `DefaultMediaSourceFactory`, which can create -instances of the following content `MediaSource` implementations: - -* `DashMediaSource` for [DASH][]. -* `SsMediaSource` for [SmoothStreaming][]. -* `HlsMediaSource` for [HLS][]. -* `ProgressiveMediaSource` for [regular media files][]. -* `RtspMediaSource` for [RTSP][]. - -`DefaultMediaSourceFactory` can also create more complex media sources depending -on the properties of the corresponding media items. This is described in more -detail on the [Media items page]({{ site.baseurl }}/media-items.html). - -For apps that need media source setups that are not supported by the -default configuration of the player, there are several options for -customization. - -## Customizing media source creation ## - -When building the player, a `MediaSource.Factory` can be injected. For example, -if an app wants to insert ads and use a `CacheDataSource.Factory` to support -caching, an instance of `DefaultMediaSourceFactory` can be configured to match -these requirements and injected during player construction: - -~~~ -MediaSource.Factory mediaSourceFactory = - new DefaultMediaSourceFactory(context) - .setDataSourceFactory(cacheDataSourceFactory) - .setLocalAdInsertionComponents( - adsLoaderProvider, /* adViewProvider= */ playerView); -ExoPlayer player = new ExoPlayer.Builder(context) - .setMediaSourceFactory(mediaSourceFactory) - .build(); -~~~ -{: .language-java} - -The -[`DefaultMediaSourceFactory` JavaDoc]({{ site.exo_sdk }}/source/DefaultMediaSourceFactory.html) -describes the available options in more detail. - -It's also possible to inject a custom `MediaSource.Factory` implementation, for -example to support creation of a custom media source type. The factory's -`createMediaSource(MediaItem)` will be called to create a media source for each -media item that is -[added to the playlist]({{ site.baseurl }}/playlists.html). - -## Media source based playlist API ## - -The [`ExoPlayer`] interface defines additional playlist methods that accept -media sources rather than media items. This makes it possible to bypass the -player's internal `MediaSource.Factory` and pass media source instances to the -player directly: - -~~~ -// Set a list of media sources as initial playlist. -exoPlayer.setMediaSources(listOfMediaSources); -// Add a single media source. -exoPlayer.addMediaSource(anotherMediaSource); - -// Can be combined with the media item API. -exoPlayer.addMediaItem(/* index= */ 3, MediaItem.fromUri(videoUri)); - -exoPlayer.prepare(); -exoPlayer.play(); -~~~ -{: .language-java} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/media-sources -[DASH]: {{ site.baseurl }}/dash.html -[SmoothStreaming]: {{ site.baseurl }}/smoothstreaming.html -[HLS]: {{ site.baseurl }}/hls.html -[RTSP]: {{ site.baseurl }}/rtsp.html -[regular media files]: {{ site.baseurl }}/progressive.html -[`ExoPlayer`]: {{ site.exo_sdk }}/ExoPlayer.html diff --git a/docs/network-stacks.md b/docs/network-stacks.md index a3a7867cc21..55fea96bd5c 100644 --- a/docs/network-stacks.md +++ b/docs/network-stacks.md @@ -1,184 +1,5 @@ --- -title: Network stacks +permalink: /network-stacks.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/network-stacks --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer is commonly used for streaming media over the internet. It supports -multiple network stacks for making its underlying network requests. Your choice -of network stack can have a significant impact on streaming performance. - -This page outlines how to configure ExoPlayer to use your network stack of -choice, lists the available options, and provides some guidance on how to choose -a network stack for your application. - -## Configuring ExoPlayer to use a specific network stack ## - -ExoPlayer loads data through `DataSource` components, which it obtains from -`DataSource.Factory` instances that are injected from application code. - -If your application only needs to play http(s) content, selecting a network -stack is as simple as updating any `DataSource.Factory` instances that your -application injects to be instances of the `HttpDataSource.Factory` -that corresponds to the network stack you wish to use. If your application also -needs to play non-http(s) content such as local files, use - -~~~ -new DefaultDataSource.Factory( - ... - /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...)); -~~~ -{: .language-java} - -where `PreferredHttpDataSource.Factory` is the factory corresponding to your -preferred network stack. The `DefaultDataSource.Factory` layer adds in support -for non-http(s) sources such as local files. - -The example below shows how to build an `ExoPlayer` that will use the Cronet -network stack and also support playback of non-http(s) content. - -~~~ -// Given a CronetEngine and Executor, build a CronetDataSource.Factory. -CronetDataSource.Factory cronetDataSourceFactory = - new CronetDataSource.Factory(cronetEngine, executor); - -// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds -// in support for requesting data from other sources (e.g., files, resources, -// etc). -DefaultDataSource.Factory dataSourceFactory = - new DefaultDataSource.Factory( - context, - /* baseDataSourceFactory= */ cronetDataSourceFactory); - -// Inject the DefaultDataSource.Factory when creating the player. -ExoPlayer player = - new ExoPlayer.Builder(context) - .setMediaSourceFactory( - new DefaultMediaSourceFactory(context) - .setDataSourceFactory(dataSourceFactory)) - .build(); -~~~ -{: .language-java} - -## Supported network stacks ## - -ExoPlayer provides direct support for Cronet, OkHttp and Android's built-in -network stack. It can also be extended to support any other network stack that -works on Android. - -### Cronet ### - -[Cronet](https://developer.android.com/guide/topics/connectivity/cronet) is the -Chromium network stack made available to Android apps as a library. It takes -advantage of multiple technologies that reduce the latency and increase the -throughput of the network requests that your app needs to work, including those -made by ExoPlayer. It natively supports the HTTP, HTTP/2, and HTTP/3 over QUIC -protocols. Cronet is used by some of the world's biggest streaming applications, -including YouTube. - -ExoPlayer supports Cronet via its -[Cronet extension](https://github.com/google/ExoPlayer/tree/dev-v2/extensions/cronet). -Please see the extension's `README.md` for detailed instructions on how to use -it. Note that the Cronet extension is able to use three underlying Cronet -implementations: - -1. **Google Play Services:** We recommend using this implementation in most - cases, and falling back to Android's built-in network stack - (i.e., `DefaultHttpDataSource`) if Google Play Services is not available. -1. **Cronet Embedded:** May be a good choice if a large percentage of your users - are in markets where Google Play Services is not widely available, or if you - want to control the exact version of the Cronet implementation being used. The - major disadvantage of Cronet Embedded is that it adds approximately 8MB to - your application. -1. **Cronet Fallback:** The fallback implementation of Cronet implements - Cronet's API as a wrapper around Android's built-in network stack. It should - not be used with ExoPlayer, since using Android's built-in network stack - directly (i.e., by using `DefaultHttpDataSource`) is more efficient. - -### OkHttp ### - -[OkHttp](https://square.github.io/okhttp/) is another modern network stack that -is widely used by many popular Android applications. It supports HTTP and -HTTP/2, but does not yet support HTTP/3 over QUIC. - -ExoPlayer supports OkHttp via its -[OkHttp extension](https://github.com/google/ExoPlayer/tree/dev-v2/extensions/okhttp). -Please see the extension's `README.md` for detailed instructions on how to use -it. When using the OkHttp extension, the network stack is embedded within the -application. This is similar to Cronet Embedded, however OkHttp is significantly -smaller, adding under 1MB to your application. - -### Android's built-in network stack ### - -ExoPlayer supports use of Android's built-in network stack with -`DefaultHttpDataSource` and `DefaultHttpDataSource.Factory`, which are part of -the core ExoPlayer library. - -The exact network stack implementation depends on the software running on the -underlying device. On most devices (as of 2021) only HTTP is supported (i.e., -HTTP/2 and HTTP/3 over QUIC are not supported). - -### Other network stacks ### - -It's possible for applications to integrate other network stacks with ExoPlayer. -To do this, implement an `HttpDataSource` that wraps the network stack, -together with a corresponding `HttpDataSource.Factory`. ExoPlayer's Cronet and -OkHttp extensions are good examples of how to do this. - -When integrating with a pure Java network stack, it's a good idea to implement a -`DataSourceContractTest` to check that your `HttpDataSource` implementation -behaves correctly. `OkHttpDataSourceContractTest` in the OkHttp extension is a -good example of how to do this. - -## Choosing a network stack ## - -The table below outlines the pros and cons of the network stacks supported by -ExoPlayer. - -| Network stack | Protocols | APK size impact | Notes | -|:---|:--:|:--:|:---| -| Cronet (Google Play Services) | HTTP
HTTP/2
HTTP/3 over QUIC | Small
(<100KB) | Requires Google Play Services. Cronet version updated automatically | -| Cronet (Embedded) | HTTP
HTTP/2
HTTP/3 over QUIC | Large
(~8MB) | Cronet version controlled by app developer | -| Cronet (Fallback) | HTTP
(varies by device) | Small
(<100KB) | Not recommended for ExoPlayer | -| OkHttp | HTTP
HTTP/2 | Small
(<1MB) | Requires Kotlin runtime | -| Built-in network stack | HTTP
(varies by device) | None | Implementation varies by device | - -The HTTP/2 and HTTP/3 over QUIC protocols can significantly improve media -streaming performance. In particular when streaming adaptive media distributed -via a content distribution network (CDN), there are cases for which use of these -protocols can allow CDNs to operate much more efficiently. For this reason, -Cronet's support for both HTTP/2 and HTTP/3 over QUIC (and OkHttp's support for -HTTP/2), is a major benefit compared to using Android's built-in network stack, -provided the servers on which the content is hosted also support these -protocols. - -When considering media streaming in isolation, we recommend use of Cronet -provided by Google Play Services, falling back to `DefaultHttpDataSource` if -Google Play Services is unavailable. This recommendation strikes a good balance -between enabling use of HTTP/2 and HTTP/3 over QUIC on most devices, and -avoiding a significant increase in APK size. There are exceptions to this -recommendation. For cases where Google Play Services is likely to be unavailable -on a significant fraction of devices that will be running your application, -using Cronet Embedded or OkHttp may be more appropriate. Use of the built-in -network stack may be acceptable if APK size is a critical concern, or if media -streaming is only a minor part of your application's functionality. - -Beyond just media, it's normally a good idea to choose a single network stack -for all of the networking performed by your application. This allows resources -(e.g., sockets) to be efficiently pooled and shared between ExoPlayer and other -application components. - -To assist with resource sharing, it's recommended to use a single `CronetEngine` -or `OkHttpClient` instance throughout your application, when using Cronet or -OkHttp respectively. -{:.info} - -Since your application will most likely need to perform networking not related -to media playback, your choice of network stack should ultimately factor in our -recommendations above for media streaming in isolation, the requirements of any -other components that perform networking, and their relative importance to your -application. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/network-stacks diff --git a/docs/oems.md b/docs/oems.md index ddc091b90be..93a5b41704e 100644 --- a/docs/oems.md +++ b/docs/oems.md @@ -1,110 +1,5 @@ --- -title: OEM testing +permalink: /oems.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/oems --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer is used by a large number of Android applications. As an OEM, it's -important to ensure that ExoPlayer works correctly both on new devices, and on -new platform builds for existing devices. This page describes compatibility -tests that we recommend running before shipping a device or platform OTA, and -some of the common failure modes encountered when running them. - -## Running the tests ## - -To run ExoPlayer's playback tests, first check out the latest release of -ExoPlayer from [GitHub][]. You can then run the tests from the command line or -Android Studio. - -### Command line ### - -From the root directory, build and install the playback tests: -~~~ -./gradlew :playbacktests:installDebug -~~~ -{: .language-shell} -Next, run the playback tests in the GTS package: -~~~ -adb shell am instrument -w -r -e debug false \ - -e package com.google.android.exoplayer2.playbacktests.gts \ - com.google.android.exoplayer2.playbacktests.test/androidx.test.runner.AndroidJUnitRunner -~~~ -{: .language-shell} -Test results appear in STDOUT. - -### Android Studio ### - -Open the ExoPlayer project, navigate to the `playbacktests` module, right click -on the `gts` folder and run the tests. Test results appear in Android Studio's -Run window. - -## Common failure modes ## - -Some of the common failure modes encountered when running ExoPlayer's playback -tests are described below, together with the likely root cause in each case. We -will add to this list as further failure modes are discovered. - -### Unexpected video buffer presentation timestamp ### - -Logcat will contain an error similar to: -~~~ -Caused by: java.lang.IllegalStateException: Expected to dequeue video buffer -with presentation timestamp: 134766000. Instead got: 134733000 (Processed -buffers since last flush: 2242). -~~~ -{: .language-shell} -This failure is most often caused by the video decoder under test incorrectly -discarding, inserting or re-ordering buffers. In the example above, the test -expected to dequeue a buffer with presentation timestamp `134766000` from -`MediaCodec.dequeueOutputBuffer`, but found that it dequeued a buffer with -presentation timestamp `134733000` instead. We recommend that you check the -decoder implementation when encountering this failure, in particular that it -correctly handles adaptive resolution switches without discarding any buffers. - -### Too many dropped buffers ### - -Logcat will contain an error similar to: -~~~ -junit.framework.AssertionFailedError: Codec(DashTest:Video) was late decoding: -200 buffers. Limit: 25. -~~~ -{: .language-shell} -This failure is a performance problem, where the video decoder under test was -late decoding a large number of buffers. In the example above, ExoPlayer dropped -200 buffers because they were late by the time they were dequeued, for a test -that imposes a limit of 25. The most obvious cause is that the video decoder -is too slow decoding buffers. If the failures only occur for the subset of tests -that play Widevine protected content, it's likely that the platform operations -for buffer decryption are too slow. We recommend checking the performance of -these components, and looking at whether any optimizations can be made to speed -them up. - -### Native window could not be authenticated ### - -Logcat will contain an error similar to: -~~~ -SurfaceUtils: native window could not be authenticated -ExoPlayerImplInternal: Internal runtime error. -ExoPlayerImplInternal: android.media.MediaCodec$CodecException: Error 0xffffffff -~~~ -{: .language-shell} -This failure is indicative of the platform failing to correctly set the secure -bit flag. - -### Test timed out ### - -Logcat will contain an error similar to: -~~~ -AssertionFailedError: Test timed out after 300000 ms. -~~~ -{: .language-shell} -This failure is most often caused by poor network connectivity during the test -run. If the device appears to have good network connectivity then it's possible -that the test is getting stuck calling into a platform component (e.g. -`MediaCodec`, `MediaDrm`, `AudioTrack` etc). Inspect the call stacks of the -threads in the test process to establish whether this is the case. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/oems -[GitHub]: https://github.com/google/ExoPlayer diff --git a/docs/playlists.md b/docs/playlists.md index 924110e6323..e2eb44208ca 100644 --- a/docs/playlists.md +++ b/docs/playlists.md @@ -1,213 +1,5 @@ --- -title: Playlists +permalink: /playlists.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/playlists --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -The playlist API is defined by the `Player` interface, which is implemented by -all `ExoPlayer` implementations. It enables sequential playback of multiple -media items. The following example shows how to start playback of a playlist -containing two videos: - -~~~ -// Build the media items. -MediaItem firstItem = MediaItem.fromUri(firstVideoUri); -MediaItem secondItem = MediaItem.fromUri(secondVideoUri); -// Add the media items to be played. -player.addMediaItem(firstItem); -player.addMediaItem(secondItem); -// Prepare the player. -player.prepare(); -// Start the playback. -player.play(); -~~~ -{: .language-java} - -Transitions between items in a playlist are seamless. There's no requirement -that they're of the same format (e.g., it’s fine for a playlist to contain both -H264 and VP9 videos). They may even be of different types (e.g., it’s fine for a -playlist to contain both videos and audio only streams). It's allowed to use the -same `MediaItem` multiple times within a playlist. - -## Modifying the playlist - -It's possible to dynamically modify a playlist by adding, moving and removing -media items. This can be done both before and during playback by calling the -corresponding playlist API methods: - -~~~ -// Adds a media item at position 1 in the playlist. -player.addMediaItem(/* index= */ 1, MediaItem.fromUri(thirdUri)); -// Moves the third media item from position 2 to the start of the playlist. -player.moveMediaItem(/* currentIndex= */ 2, /* newIndex= */ 0); -// Removes the first item from the playlist. -player.removeMediaItem(/* index= */ 0); -~~~ -{: .language-java} - -Replacing and clearing the entire playlist are also supported: - -~~~ -// Replaces the playlist with a new one. -List newItems = ImmutableList.of( - MediaItem.fromUri(fourthUri), - MediaItem.fromUri(fifthUri)); -player.setMediaItems(newItems, /* resetPosition= */ true); -// Clears the playlist. If prepared, the player transitions to the ended state. -player.clearMediaItems(); -~~~ -{: .language-java} - -The player automatically handles modifications during playback in the correct -way. For example if the currently playing media item is moved, playback is not -interrupted and its new successor will be played upon completion. If the -currently playing `MediaItem` is removed, the player will automatically move to -playing the first remaining successor, or transition to the ended state if no -such successor exists. - -## Querying the playlist - -The playlist can be queried using `Player.getMediaItemCount` and -`Player.getMediaItemAt`. The currently playing media item can be queried -by calling `Player.getCurrentMediaItem`. There are also other convenience -methods like `Player.hasNextMediaItem` or `Player.getNextMediaItemIndex` to -simplify navigation in the playlist. - -## Repeat modes - -The player supports 3 repeat modes that can be set at any time with -`Player.setRepeatMode`: - -* `Player.REPEAT_MODE_OFF`: The playlist isn't repeated and the player will - transition to `Player.STATE_ENDED` once the last item in the playlist has - been played. -* `Player.REPEAT_MODE_ONE`: The current item is repeated in an endless loop. - Methods like `Player.seekToNextMediaItem` will ignore this and seek to the - next item in the list, which will then be repeated in an endless loop. -* `Player.REPEAT_MODE_ALL`: The entire playlist is repeated in an endless loop. - -## Shuffle mode - -Shuffle mode can be enabled or disabled at any time with -`Player.setShuffleModeEnabled`. When in shuffle mode, the player will play the -playlist in a precomputed, randomized order. All items will be played once and -the shuffle mode can also be combined with `Player.REPEAT_MODE_ALL` to repeat -the same randomized order in an endless loop. When shuffle mode is turned off, -playback continues from the current item at its original position in the -playlist. - -Note that the indices as returned by methods like -`Player.getCurrentMediaItemIndex` always refer to the original, unshuffled -order. Similarly, `Player.seekToNextMediaItem` will not play the item at -`player.getCurrentMediaItemIndex() + 1`, but the next item according to the -shuffle order. Inserting new items in the playlist or removing items will keep -the existing shuffled order unchanged as far as possible. - -### Setting a custom shuffle order - -By default the player supports shuffling by using the `DefaultShuffleOrder`. -This can be customized by providing a custom shuffle order implementation, or by -setting a custom order in the `DefaultShuffleOrder` constructor: - -~~~ -// Set a custom shuffle order for the 5 items currently in the playlist: -exoPlayer.setShuffleOrder( - new DefaultShuffleOrder(new int[] {3, 1, 0, 4, 2}, randomSeed)); -// Enable shuffle mode. -exoPlayer.setShuffleModeEnabled(/* shuffleModeEnabled= */ true); -~~~ -{: .language-java} - -## Identifying playlist items - -To identify playlist items, `MediaItem.mediaId` can be set when building the -item: - -~~~ -// Build a media item with a media ID. -MediaItem mediaItem = - new MediaItem.Builder().setUri(uri).setMediaId(mediaId).build(); -~~~ -{: .language-java} - -If an app does not explicitly define a media ID for a media item, the string -representation of the URI is used. - -## Associating app data with playlist items - -In addition to an ID, each media item can also be configured with a custom tag, -which can be any app provided object. One use of custom tags is to attach -metadata to each media item: - -~~~ -// Build a media item with a custom tag. -MediaItem mediaItem = - new MediaItem.Builder().setUri(uri).setTag(metadata).build(); -~~~ -{: .language-java} - - -## Detecting when playback transitions to another media item - -When playback transitions to another media item, or starts repeating the same -media item, `Listener.onMediaItemTransition(MediaItem, -@MediaItemTransitionReason)` is called. This callback receives the new media -item, along with a `@MediaItemTransitionReason` indicating why the transition -occurred. A common use case for `onMediaItemTransition` is to update the -application's UI for the new media item: - -~~~ -@Override -public void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - updateUiForPlayingMediaItem(mediaItem); -} -~~~ -{: .language-java} - -If the metadata required to update the UI is attached to each media item using -custom tags, then an implementation might look like: - -~~~ -@Override -public void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - @Nullable CustomMetadata metadata = null; - if (mediaItem != null && mediaItem.localConfiguration != null) { - metadata = (CustomMetadata) mediaItem.localConfiguration.tag; - } - updateUiForPlayingMediaItem(metadata); -} -~~~ -{: .language-java} - -## Detecting when the playlist changes - -When a media item is added, removed or moved, -`Listener.onTimelineChanged(Timeline, @TimelineChangeReason)` is called -immediately with `TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`. This callback is -called even when the player has not yet been prepared. - -~~~ -@Override -public void onTimelineChanged( - Timeline timeline, @TimelineChangeReason int reason) { - if (reason == TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) { - // Update the UI according to the modified playlist (add, move or remove). - updateUiForPlaylist(timeline); - } -} -~~~ -{: .language-java} - -When information such as the duration of a media item in the playlist becomes -available, the `Timeline` will be updated and `onTimelineChanged` will be called -with `TIMELINE_CHANGE_REASON_SOURCE_UPDATE`. Other reasons that can cause a -timeline update include: - -* A manifest becoming available after preparing an adaptive media item. -* A manifest being updated periodically during playback of a live stream. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/playlists diff --git a/docs/progressive.md b/docs/progressive.md index fa6f51e98b2..dc6265e2abe 100644 --- a/docs/progressive.md +++ b/docs/progressive.md @@ -1,52 +1,5 @@ --- -title: Progressive +permalink: /progressive.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/progressive --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -{% include_relative _page_fragments/supported-formats-progressive.md %} - -## Using MediaItem ## - -To play a progressive stream, create a `MediaItem` with the media URI and pass -it to the player. - -~~~ -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media item to be played. -player.setMediaItem(MediaItem.fromUri(progressiveUri)); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Using ProgressiveMediaSource ## - -For more customization options, you can create a `ProgressiveMediaSource` and -directly to the player instead of a `MediaItem`. - -~~~ -// Create a data source factory. -DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory(); -// Create a progressive media source pointing to a stream uri. -MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory) - .createMediaSource(MediaItem.fromUri(progressiveUri)); -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media source to be played. -player.setMediaSource(mediaSource); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Customizing playback ## - -ExoPlayer provides multiple ways for you to tailor playback experience to your -app's needs. See the [Customization page][] for examples. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/progressive -[Customization page]: {{ site.baseurl }}/customization.html diff --git a/docs/pros-and-cons.md b/docs/pros-and-cons.md index 4fd3467031d..66fb5f81844 100644 --- a/docs/pros-and-cons.md +++ b/docs/pros-and-cons.md @@ -1,52 +1,5 @@ --- -title: Pros and cons +permalink: /pros-and-cons.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -ExoPlayer has a number of advantages over Android's built in MediaPlayer: - -* Fewer device specific issues and less variation in behavior across different - devices and versions of Android. -* The ability to update the player along with your application. Because - ExoPlayer is a library that you include in your application apk, you have - control over which version you use and you can easily update to a newer - version as part of a regular application update. -* The ability to [customize and extend the player][] to suit your use case. - ExoPlayer is designed specifically with this in mind, and allows many - components to be replaced with custom implementations. -* Support for [playlists][]. -* Support for [DASH][] and [SmoothStreaming][], neither of which are supported - by MediaPlayer. Many other formats are also supported. See the [Supported - formats page][] for details. -* Support for advanced [HLS][] features, such as correct handling of - `#EXT-X-DISCONTINUITY` tags. -* Support for [Widevine common encryption][] on Android 4.4 (API level 19) and - higher. -* The ability to quickly integrate with a number of additional libraries using - official extensions. For example the [IMA extension][] makes it easy to - monetize your content using the [Interactive Media Ads SDK][]. - -It's important to note that there are also some disadvantages: - -* For audio only playback on some devices, ExoPlayer may consume significantly - more battery than MediaPlayer. See the [Battery consumption page][] for - details. -* Including ExoPlayer in your app adds a few hundred kilobytes to the APK size. - This is likely only a concern for extremely lightweight apps. Guidance for - shrinking ExoPlayer can be found on the [APK shrinking page][]. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/pros-and-cons -[Supported formats page]: {{ site.baseurl }}/supported-formats.html -[IMA extension]: {{ site.release_v2 }}/extensions/ima -[Interactive Media Ads SDK]: https://developers.google.com/interactive-media-ads -[Battery consumption page]: {{ site.baseurl }}/battery-consumption.html -[customize and extend the player]: {{ site.baseurl }}/customization.html -[APK shrinking page]: {{ site.baseurl }}/shrinking.html -[playlists]: {{ site.baseurl }}/playlists.html -[DASH]: {{ site.baseurl }}/dash.html -[SmoothStreaming]: {{ site.baseurl }}/smoothstreaming.html -[HLS]: {{ site.baseurl }}/hls.html -[Widevine common encryption]: {{ site.baseurl }}/drm.html diff --git a/docs/retrieving-metadata.md b/docs/retrieving-metadata.md index 1e222432948..157eb895637 100644 --- a/docs/retrieving-metadata.md +++ b/docs/retrieving-metadata.md @@ -1,95 +1,5 @@ --- -title: Retrieving metadata +permalink: /retrieving-metadata.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/retrieving-metadata --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -## During playback ## - -The metadata of the media can be retrieved during playback in multiple ways. The -most straightforward is to listen for the -`Player.Listener#onMediaMetadataChanged` event; this will provide a -[`MediaMetadata`][] object for use, which has fields such as `title` and -`albumArtist`. Alternatively, calling `Player#getMediaMetadata` returns the same -object. - -~~~ -public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { - if (mediaMetadata.title != null) { - handleTitle(mediaMetadata.title); - } -} - -~~~ -{: .language-java} - -If an application needs access to specific [`Metadata.Entry`][] objects, then it -should listen to `Player.Listener#onMetadata` (for dynamic metadata delivered -during playback). Alternatively, if there is a need to look at static metadata, -this can be accessed through the `TrackSelections#getFormat`. -`Player#getMediaMetadata` is populated from both of these sources. - -## Without playback ## - -If playback is not needed, it is more efficient to use the -[`MetadataRetriever`][] to extract the metadata because it avoids having to -create and prepare a player. - -~~~ -ListenableFuture trackGroupsFuture = - MetadataRetriever.retrieveMetadata(context, mediaItem); -Futures.addCallback( - trackGroupsFuture, - new FutureCallback() { - @Override - public void onSuccess(TrackGroupArray trackGroups) { - handleMetadata(trackGroups); - } - - @Override - public void onFailure(Throwable t) { - handleFailure(t); - } - }, - executor); -~~~ -{: .language-java} - -## Motion photos ## - -It is also possible to extract the metadata of a motion photo, containing the -image and video offset and length for example. The supported formats are: - -* JPEG motion photos recorded by Google Pixel and Samsung camera apps. This - format is playable by ExoPlayer and the associated metadata can therefore be - retrieved with a player or using the `MetadataRetriever`. -* HEIC motion photos recorded by Google Pixel and Samsung camera apps. This - format is currently not playable by ExoPlayer and the associated metadata - should therefore be retrieved using the `MetadataRetriever`. - -For motion photos, the `TrackGroupArray` obtained with the `MetadataRetriever` -contains a `TrackGroup` with a single `Format` enclosing a -[`MotionPhotoMetadata`][] metadata entry. - -~~~ -for (int i = 0; i < trackGroups.length; i++) { - TrackGroup trackGroup = trackGroups.get(i); - Metadata metadata = trackGroup.getFormat(0).metadata; - if (metadata != null && metadata.length() == 1) { - Metadata.Entry metadataEntry = metadata.get(0); - if (metadataEntry instanceof MotionPhotoMetadata) { - MotionPhotoMetadata motionPhotoMetadata = (MotionPhotoMetadata) metadataEntry; - handleMotionPhotoMetadata(motionPhotoMetadata); - } - } -} -~~~ -{: .language-java} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/retrieving-metadata -[`MediaMetadata`]: {{ site.exo_sdk }}/MediaMetadata.html -[`Metadata.Entry`]: {{ site.exo_sdk }}/metadata/Metadata.Entry.html -[`MetadataRetriever`]: {{ site.exo_sdk }}/MetadataRetriever.html -[`MotionPhotoMetadata`]: {{ site.exo_sdk }}/metadata/mp4/MotionPhotoMetadata.html diff --git a/docs/rtsp.md b/docs/rtsp.md index d6f510401ff..c15166ba662 100644 --- a/docs/rtsp.md +++ b/docs/rtsp.md @@ -1,103 +1,5 @@ --- -title: RTSP +permalink: /rtsp.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/rtsp --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -{% include_relative _page_fragments/supported-formats-rtsp.md %} - -## Using MediaItem ## - -To play an RTSP stream, you need to depend on the RTSP module. - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-rtsp:2.X.X' -~~~ -{: .language-gradle} - -You can then create a `MediaItem` for an RTSP URI and pass it to the player. - -~~~ -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media item to be played. -player.setMediaItem(MediaItem.fromUri(rtspUri)); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -### Authentication ### - -ExoPlayer supports playback with RTSP BASIC and DIGEST authentication. To play -protected RTSP content, the `MediaItem`'s URI must be configured with the -authentication info. Specifically, the URI should be of the form -`rtsp://:@`. - -## Using RtspMediaSource ## - -For more customization options, you can create an `RtspMediaSource` and pass it -directly to the player instead of a `MediaItem`. - -~~~ -// Create an RTSP media source pointing to an RTSP uri. -MediaSource mediaSource = - new RtspMediaSource.Factory() - .createMediaSource(MediaItem.fromUri(rtspUri)); -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media source to be played. -player.setMediaSource(mediaSource); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Using RTSP behind a NAT (RTP/TCP support) ## - -ExoPlayer uses UDP as the default protocol for RTP transport. - -When streaming RTSP behind a NAT layer, the NAT might not be able to forward the -incoming RTP/UDP packets to the device. This occurs if the NAT lacks the -necessary UDP port mapping. If ExoPlayer detects there have not been incoming -RTP packets for a while and the playback has not started yet, ExoPlayer tears -down the current RTSP playback session, and retries playback using RTP-over-RTSP -(transmitting RTP packets using the TCP connection opened for RTSP). - -The timeout for retrying with TCP can be customized by calling the method -`RtspMediaSource.Factory.setTimeoutMs()`. For example, if the timeout is set to -four seconds, the player will retry with TCP after four seconds of UDP -inactivity. - -Setting the timeout also affects the end-of-stream detection logic. That is, -ExoPlayer will report the playback has ended if nothing is received for the -duration of the set timeout. Setting this value too small may lead to an early -end-of-stream signal under poor network conditions. - -RTP/TCP offers better compatibility under some network setups. You can configure -ExoPlayer to use RTP/TCP by default with -`RtspMediaSource.Factory.setForceUseRtpTcp()`. - -### Passing a custom SocketFactory -Custom `SocketFactory` instances can be useful when particular routing is -required (e.g. when RTSP traffic needs to pass a specific interface, or the -socket needs additional connectivity flags). - -By default, `RtspMediaSource` will use Java's standard socket factory -(`SocketFactory.getDefault()`) to create connections to the remote endpoints. -This behavior can be overridden using -`RtspMediaSource.Factory.setSocketFactory()`. - -~~~ -// Create an RTSP media source pointing to an RTSP uri and override the socket -// factory. -MediaSource mediaSource = - new RtspMediaSource.Factory() - .setSocketFactory(...) - .createMediaSource(MediaItem.fromUri(rtspUri)); -~~~ -{: .language-java} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/rtsp diff --git a/docs/shrinking.md b/docs/shrinking.md index 7855459a5f1..b24d9bf17d8 100644 --- a/docs/shrinking.md +++ b/docs/shrinking.md @@ -1,139 +1,5 @@ --- -title: APK shrinking +permalink: /shrinking.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/shrinking --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -Minimizing APK size is an important aspect of developing a good Android -application. This is particularly true when targeting developing markets, and -also when developing an Android Instant App. For such cases it may be desirable -to minimize the size of the ExoPlayer library that's included in the APK. This -page outlines some simple steps that can help to achieve this. - -## Use modular dependencies ## - -The most convenient way to use ExoPlayer is to add a dependency to the full -library: - -~~~ -implementation 'com.google.android.exoplayer:exoplayer:2.X.X' -~~~ -{: .language-gradle} - -However this may pull in more features than your app needs. Instead, depend only -on the library modules that you actually need. For example the following will -add dependencies on the Core, DASH and UI library modules, as might be required -for an app that only plays DASH content: - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X' -implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X' -implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X' -~~~ -{: .language-gradle} - -## Enable code and resource shrinking ## - -You should enable code and resource shrinking for your application's release -builds. ExoPlayer is structured in a way that allows code shrinking to -effectively remove unused functionality. For example, for an application that -plays DASH content, ExoPlayer's contribution to APK size can be reduced by -approximately 40% by enabling code shrinking. - -Read [Shrink, obfuscate, and optimize your app][] on the Android Developer site -to learn how to enable code and resource shrinking. - -## Specify which renderers your app needs ## - -By default, the player's renderers will be created using -`DefaultRenderersFactory`. `DefaultRenderersFactory` depends on all of the -`Renderer` implementations provided in the ExoPlayer library, and as a result -none of them will be removed by code shrinking. If you know that your app only -needs a subset of renderers, you can specify your own `RenderersFactory` -instead. For example, an app that only plays audio can define a factory like -this when instantiating `ExoPlayer` instances: - -~~~ -RenderersFactory audioOnlyRenderersFactory = - (handler, videoListener, audioListener, textOutput, metadataOutput) - -> new Renderer[] { - new MediaCodecAudioRenderer( - context, MediaCodecSelector.DEFAULT, handler, audioListener) - }; -ExoPlayer player = - new ExoPlayer.Builder(context, audioOnlyRenderersFactory).build(); -~~~ -{: .language-java} - -This will allow other `Renderer` implementations to be removed by code -shrinking. In this particular example video, text and metadata renderers are -removed. - -## Specify which extractors your app needs ## - -By default, the player will create `Extractor`s to play progressive media using -`DefaultExtractorsFactory`. `DefaultExtractorsFactory` depends on all of the -`Extractor` implementations provided in the ExoPlayer library, and as a result -none of them will be removed by code shrinking. If you know that your app only -needs to play a small number of container formats, or doesn't play progressive -media at all, you can specify your own `ExtractorsFactory` instead. For example, -an app that only needs to play mp4 files can provide a factory like: - -~~~ -ExtractorsFactory mp4ExtractorFactory = - () -> new Extractor[] {new Mp4Extractor()}; -ExoPlayer player = - new ExoPlayer.Builder( - context, - new DefaultMediaSourceFactory(context, mp4ExtractorFactory)) - .build(); -~~~ -{: .language-java} - -This will allow other `Extractor` implementations to be removed by code -shrinking, which can result in a significant reduction in size. - -If your app is not playing progressive content at all, you should pass -`ExtractorsFactory.EMPTY` to the `DefaultMediaSourceFactory` constructor, then -pass that `mediaSourceFactory` to the `ExoPlayer.Builder` constructor. - -~~~ -ExoPlayer player = - new ExoPlayer.Builder( - context, - new DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY)) - .build(); -~~~ -{: .language-java} - -## Custom MediaSource instantiation ## - -If your app is using a custom `MediaSource.Factory` and you want -`DefaultMediaSourceFactory` to be removed by code stripping, you should pass -your `MediaSource.Factory` directly to the `ExoPlayer.Builder` constructor. - -~~~ -ExoPlayer player = - new ExoPlayer.Builder(context, customMediaSourceFactory).build(); -~~~ -{: .language-java} - -If your app is using `MediaSource`s directly instead of `MediaItem`s you should -pass `MediaSource.Factory.UNSUPPORTED` to the `ExoPlayer.Builder` constructor, -to ensure `DefaultMediaSourceFactory` and `DefaultExtractorsFactory` can be -stripped by code shrinking. - -~~~ -ExoPlayer player = - new ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build(); -ProgressiveMediaSource mediaSource = - new ProgressiveMediaSource.Factory( - dataSourceFactory, customExtractorsFactory) - .createMediaSource(MediaItem.fromUri(uri)); -~~~ -{: .language-java} - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/shrinking -[Shrink, obfuscate, and optimize your app]: https://developer.android.com/studio/build/shrink-code diff --git a/docs/smoothstreaming.md b/docs/smoothstreaming.md index 84845aa7723..29bfb091685 100644 --- a/docs/smoothstreaming.md +++ b/docs/smoothstreaming.md @@ -1,93 +1,5 @@ --- -title: SmoothStreaming +permalink: /smoothstreaming.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/smoothstreaming --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -{% include_relative _page_fragments/supported-formats-smoothstreaming.md %} - -## Using MediaItem ## - -To play a SmoothStreaming stream, you need to depend on the SmoothStreaming -module. - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.X.X' -~~~ -{: .language-gradle} - -You can then create a `MediaItem` for a SmoothStreaming manifest URI and pass it -to the player. - -~~~ -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media item to be played. -player.setMediaItem(MediaItem.fromUri(ssUri)); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -If your URI doesn't end with `.ism/Manifest`, you can pass -`MimeTypes.APPLICATION_SS` to `setMimeType` of `MediaItem.Builder` to explicitly -indicate the type of the content. - -ExoPlayer will automatically adapt between representations defined in the -manifest, taking into account both available bandwidth and device capabilities. - -## Using SsMediaSource ## - -For more customization options, you can create a `SsMediaSource` and pass it -directly to the player instead of a `MediaItem`. - -~~~ -// Create a data source factory. -DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory(); -// Create a SmoothStreaming media source pointing to a manifest uri. -MediaSource mediaSource = - new SsMediaSource.Factory(dataSourceFactory) - .createMediaSource(MediaItem.fromUri(ssUri)); -// Create a player instance. -ExoPlayer player = new ExoPlayer.Builder(context).build(); -// Set the media source to be played. -player.setMediaSource(mediaSource); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -## Accessing the manifest ## - -You can retrieve the current manifest by calling `Player.getCurrentManifest`. -For SmoothStreaming you should cast the returned object to `SsManifest`. The -`onTimelineChanged` callback of `Player.Listener` is also called whenever -the manifest is loaded. This will happen once for a on-demand content, and -possibly many times for live content. The code snippet below shows how an app -can do something whenever the manifest is loaded. - -~~~ -player.addListener( - new Player.Listener() { - @Override - public void onTimelineChanged( - Timeline timeline, @Player.TimelineChangeReason int reason) { - Object manifest = player.getCurrentManifest(); - if (manifest != null) { - SsManifest ssManifest = (SsManifest) manifest; - // Do something with the manifest. - } - } - }); -~~~ -{: .language-java} - -## Customizing playback ## - -ExoPlayer provides multiple ways for you to tailor playback experience to your -app's needs. See the [Customization page][] for examples. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/smoothstreaming -[Customization page]: {{ site.baseurl }}/customization.html diff --git a/docs/supported-devices.md b/docs/supported-devices.md index 5248c0226e1..792188b04a3 100644 --- a/docs/supported-devices.md +++ b/docs/supported-devices.md @@ -1,57 +1,5 @@ --- -title: Supported devices +permalink: /supported-devices.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/supported-devices --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -The minimum Android versions required for core ExoPlayer use cases are: - -| Use case | Android version number | Android API level | -|----------|:------------:|:------------:| -| Audio playback | 4.1 | 16 | -| Video playback | 4.1 | 16 | -| DASH (no DRM) | 4.1 | 16 | -| DASH (Widevine CENC; "cenc" scheme) | 4.4 | 19 | -| DASH (Widevine CENC; "cbcs" scheme) | 7.1 | 25 | -| DASH (ClearKey; "cenc" scheme) | 5.0 | 21 | -| SmoothStreaming (no DRM) | 4.1 | 16 | -| SmoothStreaming (PlayReady SL2000; "cenc" scheme) | AndroidTV | AndroidTV | -| HLS (no DRM) | 4.1 | 16 | -| HLS (AES-128 encryption) | 4.1 | 16 | -| HLS (Widevine CENC; "cenc" scheme) | 4.4 | 19 | -| HLS (Widevine CENC; "cbcs" scheme) | 7.1 | 25 | - -For a given use case, we aim to support ExoPlayer on all Android devices that -satisfy the minimum version requirement. Known device specific compatibility -issues are listed below. Device specific issues on our GitHub issue tracker can -be found -[here](https://github.com/google/ExoPlayer/labels/bug%3A%20device%20specific). - -* **FireOS (version 4 and earlier)** - Whilst we endeavour to support FireOS - devices, FireOS is a fork of Android and as a result we are unable to - guarantee support. Device specific issues encountered on FireOS are normally - caused by incompatibilities in the support that FireOS provides for running - Android applications. Such issues should be reported to Amazon in the first - instance. We are aware of issues affecting FireOS version 4 and earlier. We - believe FireOS version 5 resolved these issues. -* **Nexus Player (only when using an HDMI to DVI cable)** - There is a known - issue affecting Nexus Player, only when the device is connected to a monitor - using a certain type of HDMI to DVI cable, which causes video being played too - quickly. Use of an HDMI to DVI cable is not realistic for an end user setup - because such cables cannot carry audio. Hence this issue can be safely - ignored. We suggest using a realistic end user setup (e.g., the device - connected to a TV using a standard HDMI cable) for development and testing. -* **Emulators** - Some Android emulators do not properly implement components of - Android's media stack, and as a result do not support ExoPlayer. This is an - issue with the emulator, not with ExoPlayer. Android's official emulator - ("Virtual Devices" in Android Studio) supports ExoPlayer provided the system - image has an API level of at least 23. System images with earlier API levels - do not support ExoPlayer. The level of support provided by third party - emulators varies. Issues running ExoPlayer on third party emulators should be - reported to the developer of the emulator rather than to the ExoPlayer team. - Where possible, we recommend testing media applications on physical devices - rather than emulators. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/supported-devices diff --git a/docs/supported-formats.md b/docs/supported-formats.md index 84f1de7fb2a..7168a96ec11 100644 --- a/docs/supported-formats.md +++ b/docs/supported-formats.md @@ -1,127 +1,5 @@ --- -title: Supported formats +permalink: /supported-formats.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/supported-formats --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -When defining the formats that ExoPlayer supports, it's important to note that -"media formats" are defined at multiple levels. From the lowest level to the -highest, these are: - -* The format of the individual media samples (e.g., a frame of video or a frame - of audio). These are *sample formats*. Note that a typical video file will - contain media in at least two sample formats; one for video (e.g., H.264) and - one for audio (e.g., AAC). -* The format of the container that houses the media samples and associated - metadata. These are *container formats*. A media file has a single container - format (e.g., MP4), which is commonly indicated by the file extension. Note - that for some audio only formats (e.g., MP3), the sample and container formats - may be the same. -* Adaptive streaming technologies such as DASH, SmoothStreaming and HLS. These - are not media formats as such, however it's still necessary to define what - level of support ExoPlayer provides. - -The following sections define ExoPlayer's support at each level, from highest to -lowest. The last two sections describe support for standalone subtitle formats -and HDR video playback. - -## Adaptive streaming ## - -### DASH ### - -{% include_relative _page_fragments/supported-formats-dash.md %} - -### SmoothStreaming ### - -{% include_relative _page_fragments/supported-formats-smoothstreaming.md %} - -### HLS ### - -{% include_relative _page_fragments/supported-formats-hls.md %} - -## Progressive container formats ## - -{% include_relative _page_fragments/supported-formats-progressive.md %} - -## RTSP ## - -{% include_relative _page_fragments/supported-formats-rtsp.md %} - -## Sample formats ## - -By default ExoPlayer uses Android's platform decoders. Hence the supported -sample formats depend on the underlying platform rather than on ExoPlayer. -Sample formats supported by Android devices are documented -[here](https://developer.android.com/guide/appendix/media-formats.html#core). -Note that individual devices may support additional formats beyond those listed. - -In addition to Android's platform decoders, ExoPlayer can also make use of -software decoder extensions. These must be manually built and included in -projects that wish to make use of them. We currently provide software decoder -extensions for -[AV1]({{ site.release_v2 }}/extensions/av1), -[VP9]({{ site.release_v2 }}/extensions/vp9), -[FLAC]({{ site.release_v2 }}/extensions/flac), -[Opus]({{ site.release_v2 }}/extensions/opus) and -[FFmpeg]({{ site.release_v2 }}/extensions/ffmpeg). - -### FFmpeg extension ### - -The [FFmpeg extension]({{ site.release_v2 }}/extensions/ffmpeg) supports -decoding a variety of different audio sample formats. You can choose which -decoders to include when building the extension, as documented in the -extension's [README.md]({{ site.release_v2 }}/extensions/ffmpeg/README.md). The -following table provides a mapping from audio sample format to the corresponding -FFmpeg decoder name. - -| Sample format | Decoder name(s) | -|---------------:|----------------------------| -| Vorbis | vorbis | -| Opus | opus | -| FLAC | flac | -| ALAC | alac | -| PCM μ-law | pcm_mulaw | -| PCM A-law | pcm_alaw | -| MP1, MP2, MP3 | mp3 | -| AMR-NB | amrnb | -| AMR-WB | amrwb | -| AAC | aac | -| AC-3 | ac3 | -| E-AC-3 | eac3 | -| DTS, DTS-HD | dca | -| TrueHD | mlp truehd | - -## Standalone subtitle formats ## - -ExoPlayer supports standalone subtitle files in a variety of formats. Subtitle -files can be side-loaded as described on the [media items page][]. - -| Container format | Supported | MIME type | -|---------------------------|:------------:|:----------| -| WebVTT | YES | MimeTypes.TEXT_VTT | -| TTML / SMPTE-TT | YES | MimeTypes.APPLICATION_TTML | -| SubRip | YES | MimeTypes.APPLICATION_SUBRIP | -| SubStationAlpha (SSA/ASS) | YES | MimeTypes.TEXT_SSA | - -[media items page]: {{ site.baseurl }}/media-items.html#sideloading-subtitle-tracks - -## HDR video playback ## - -ExoPlayer handles extracting high dynamic range (HDR) video in various -containers, including Dolby Vision in MP4 and HDR10+ in Matroska/WebM. Decoding -and displaying HDR content depends on support from the Android platform and -device. See -[HDR Video Playback](https://source.android.com/devices/tech/display/hdr.html) -to learn about checking for HDR decoding/display capabilities and limitations of -HDR support across Android versions. - -When playing an HDR stream that requires support for a particular codec profile, -ExoPlayer's default `MediaCodec` selector will pick a decoder that supports that -profile (if available), even if another decoder for the same MIME type that -doesn't support that profile appears higher up the codec list. This can result -in selecting a software decoder in cases where the stream exceeds the -capabilities of a hardware decoder for the same MIME type. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/supported-formats diff --git a/docs/track-selection.md b/docs/track-selection.md index 3434a749e36..0b474bc6b81 100644 --- a/docs/track-selection.md +++ b/docs/track-selection.md @@ -1,219 +1,5 @@ --- -title: Track selection +permalink: /track-selection.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/track-selection --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -When a media item contains multiple tracks, track selection is the process that -determines which of them are chosen for playback. The track selection process is -configured by [`TrackSelectionParameters`][], which allows many different -constraints and overrides influencing track selection to be specified. - -## Querying the available tracks - -You can listen to `Player.Listener.onTracksChanged` to be notified about changes -to tracks, including: - -* The available tracks becoming known when preparation of the media item being - played completes. Note that the player needs to prepare a media item to know - what tracks it contains. -* The available tracks changing due to playback transitioning from one media - item to another. -* Changes to the selected tracks. - -~~~ -player.addListener(new Player.Listener() { - @Override - public void onTracksChanged(Tracks tracks) { - // Update UI using current tracks. - } -}); -~~~ -{: .language-java} - -You can also query the current tracks by calling `player.getCurrentTracks()`. -The returned `Tracks` contains a list of `Track.Group`s, where tracks within a -single `Group` present the same content but in different formats. - -As an example of how tracks can be grouped, consider an adaptive playback where -a main video feed is provided in five bitrates, and an alternative video feed -(e.g., a different camera angle in a sports match) is provided in two bitrates. -In this case there will be two video track groups, one corresponding to the main -video feed containing five tracks, and a second for the alternative video feed -containing two tracks. - -Audio tracks whose languages differ are not grouped, because content in -different languages is not considered to be the same. Conversely, audio tracks -in the same language that only differ in properties such as bitrate, sampling -rate, channel count and so on can be grouped. This also applies to text tracks. - -Each `Group` can be queried to determine which tracks are supported for -playback, which are currently selected, and what `Format` each track uses: - -~~~ -for (Tracks.Group trackGroup : tracks.getGroups()) { - // Group level information. - @C.TrackType int trackType = trackGroup.getTrackType(); - boolean trackInGroupIsSelected = trackGroup.isSelected(); - boolean trackInGroupIsSupported = trackGroup.isSupported(); - for (int i = 0; i < trackGroup.length; i++) { - // Individual track information. - boolean isSupported = trackGroup.isTrackSupported(i); - boolean isSelected = trackGroup.isTrackSelected(i); - Format trackFormat = trackGroup.getTrackFormat(i); - } -} -~~~ -{: .language-java} - -* A track is 'supported' if the `Player` is able to decode and render its - samples. Note that even if multiple track groups of the same type (for example - multiple audio track groups) are supported, it only means that they are - supported individually and the player is not necessarily able to play them at - the same time. -* A track is 'selected' if it has been chosen for playback given the current - `TrackSelectionParameters`. If multiple tracks within one track group are - selected, the player uses these tracks for adaptive playback (for example, - multiple video tracks with different bitrates). Note that only one of these - tracks will be played at any one time. - -## Modifying track selection parameters - -The track selection process can be configured using -`Player.setTrackSelectionParameters`. This can be done both before and during -playback. The example below demonstrates how to obtain the current -`TrackSelectionParameters` from the player, modify them, and update the `Player` -with the modified result: - -~~~ -player.setTrackSelectionParameters( - player.getTrackSelectionParameters() - .buildUpon() - .setMaxVideoSizeSd() - .setPreferredAudioLanguage("hu") - .build()); -~~~ -{: .language-java} - -### Constraint based track selection - -Most options in `TrackSelectionParameters` allow you to specify constraints, -which are independent of the tracks that are actually available. Available -constraints include: - - * Maximum and minimum video width, height, frame rate, and bitrate. - * Maximum audio channel count and bitrate. - * Preferred MIME types for video and audio. - * Preferred audio languages and role flags. - * Preferred text languages and role flags. - -ExoPlayer uses sensible defaults for these constraints, for example restricting -video resolution to the display size and preferring the audio language that -matches the user's system Locale setting. - -There are several benefits to using constraint based track selection rather than -selecting specific tracks from those that are available: - -* You can specify constraints before knowing what tracks a media item provides. - This means that constraints can be specified before the player has prepared a - media item, whereas selecting specific tracks requires application code to - wait until the available tracks become known. -* Constraints are applied for all media items in a playlist, even when those - items have different available tracks. For example, a preferred audio language - constraint will be automatically applied for all media items, even if the - `Format` of the track in that language varies from one media item to the next. - This is not the case when selecting specific tracks, as described below. - -### Selecting specific tracks - -It's possible to select specific tracks using `TrackSelectionParameters`. First, -the player's currently available tracks should be queried using -`Player.getCurrentTracks`. Second, having identified which tracks to select, -they can be set on `TrackSelectionParameters` using a `TrackSelectionOverride`. -For example, to select the first track from a specific `audioTrackGroup`: - -~~~ -player.setTrackSelectionParameters( - player.getTrackSelectionParameters() - .buildUpon() - .setOverrideForType( - new TrackSelectionOverride( - audioTrackGroup.getMediaTrackGroup(), - /* trackIndex= */ 0)) - .build()); -~~~ -{: .language-java} - -A `TrackSelectionOverride` will only apply to media items that contain a -`TrackGroup` exactly matching the one specified in the override. Hence an -override may not apply to a subsequent media item if that item contains -different tracks. - -### Disabling track types or groups - -Track types like video, audio or text, can be disabled completely using -`TrackSelectionParameters.Builder.setTrackTypeDisabled`. A disabled track type -will be disabled for all media items: - -~~~ -player.setTrackSelectionParameters( - player.getTrackSelectionParameters() - .buildUpon() - .setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, /* disabled= */ true) - .build()); -~~~ -{: .language-java} - -Alternatively, it's possible to prevent the selection of tracks from a specific -`TrackGroup` by specifying an empty override for that group: - -~~~ -player.setTrackSelectionParameters( - player.getTrackSelectionParameters() - .buildUpon() - .addOverride( - new TrackSelectionOverride( - disabledTrackGroup.getMediaTrackGroup(), - /* trackIndices= */ ImmutableList.of())) - .build()); -~~~ -{: .language-java} - -## Customizing the track selector - -Track selection is the responsibility of a `TrackSelector`, an instance -of which can be provided whenever an `ExoPlayer` is built and later obtained -with `ExoPlayer.getTrackSelector()`. - -~~~ -DefaultTrackSelector trackSelector = new DefaultTrackSelector(context); -ExoPlayer player = - new ExoPlayer.Builder(context) - .setTrackSelector(trackSelector) - .build(); -~~~ -{: .language-java} - -`DefaultTrackSelector` is a flexible `TrackSelector` suitable for most use -cases. It uses the `TrackSelectionParameters` set in the `Player`, but also -provides some advanced customization options that can be specified in the -`DefaultTrackSelector.ParametersBuilder`: - -~~~ -trackSelector.setParameters( - trackSelector - .buildUponParameters() - .setAllowVideoMixedMimeTypeAdaptiveness(true)); -~~~ -{: .language-java} - -### Tunneling - -Tunneled playback can be enabled in cases where the combination of renderers and -selected tracks supports it. This can be done by using -`DefaultTrackSelector.ParametersBuilder.setTunnelingEnabled(true)`. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/track-selection -[`TrackSelectionParameters`]: {{ site.exo_sdk }}/trackselection/TrackSelectionParameters.html diff --git a/docs/transforming-media.md b/docs/transforming-media.md index 08b898268ee..8c86d6dd53c 100644 --- a/docs/transforming-media.md +++ b/docs/transforming-media.md @@ -1,143 +1,5 @@ --- -title: Transforming media +permalink: /transforming-media.html +redirect_to: + - https://developer.android.com/media/media3/transformer --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -The [Transformer API][] can be used to convert media streams. It takes an input -media stream, applies changes to it as configured by the app, and produces the -corresponding output file. The available transformations are: - -* Track removal. -* Flattening of slow motion videos or, in other words, their conversion into - normal videos that retain the desired slow motion effects, but can be played - with a player that is not aware of slow motion video formats. The purpose of - this transformation is to make slow motion videos suitable for sharing with - other apps or uploading to a server. - -## Starting a transformation ## - -To transform media, you need to add the following dependency to your app’s -`build.gradle` file: - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-transformer:2.X.X' -~~~ -{: .language-gradle} - -where `2.X.X` is your preferred ExoPlayer version. - -You can then start a transformation by building a `Transformer` instance and -calling `startTransformation` on it. The code sample below starts a -transformation that removes the audio track from the input: - -~~~ -// Configure and create a Transformer instance. -Transformer transformer = - new Transformer.Builder(context) - .setRemoveAudio(true) - .addListener(transformerListener) - .build(); -// Start the transformation. -transformer.startTransformation(inputMediaItem, outputPath); -~~~ -{: .language-java} - -Other parameters, such as the `MediaSource.Factory`, can be passed to the -builder. - -`startTransformation` receives a `MediaItem` describing the input, and a path or -a `ParcelFileDescriptor` indicating where the output should be written. The -input can be a progressive or an adaptive stream, but the output is always a -progressive stream. For adaptive inputs, the highest resolution tracks are -always selected for the transformation. The input can be of any container format -supported by ExoPlayer (see the [Supported formats page][] for details), but the -output is always an MP4 file. - -Multiple transformations can be executed sequentially with the same -`Transformer` instance, but concurrent transformations with the same instance -are not supported. - -## Listening to events ## - -The `startTransformation` method is asynchronous. It returns immediately and the -app is notified of events via the listener passed to the `Transformer` builder. - -~~~ -Transformer.Listener transformerListener = - new Transformer.Listener() { - @Override - public void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult result) { - playOutput(); - } - - @Override - public void onTransformationError(MediaItem inputMediaItem, TransformationException exception) { - displayError(exception); - } - }; -~~~ -{: .language-java} - -## Displaying progress updates ## - -`Transformer.getProgress` can be called to query the current progress of a -transformation. The returned value indicates the progress state. If the progress -state is `PROGRESS_STATE_AVAILABLE` then the passed `ProgressHolder` will have -been updated with the current progress percentage. The snippet below -demonstrates how to periodically query the progress of a transformation, where -the `updateProgressInUi` method could be implemented to update a progress bar -displayed to the user. - -~~~ -transformer.startTransformation(inputMediaItem, outputPath); -ProgressHolder progressHolder = new ProgressHolder(); -mainHandler.post( - new Runnable() { - @Override - public void run() { - @ProgressState int progressState = transformer.getProgress(progressHolder); - updateProgressInUi(progressState, progressHolder); - if (progressState != PROGRESS_STATE_NO_TRANSFORMATION) { - mainHandler.postDelayed(/* r= */ this, /* delayMillis= */ 500); - } - } - }); -~~~ -{: .language-java} - -## Flattening slow motion videos ## - -We define a slow motion video as a media stream whose metadata points to -sections of the stream that should be slowed during playback. Flattening is the -process of converting a slow motion video to a regular media format (for example -MP4) where the slow motion sections are played at the requested speed. The slow -motion metadata is removed, and the video and audio streams are modified so as -to produce the desired effect when the output is played with a standard player -(that is, a player that is not aware of slow motion formats). - -To flatten slow motion streams, use the `setFlattenForSlowMotion` builder -method. - -~~~ -Transformer transformer = - new Transformer.Builder(context) - .setFlattenForSlowMotion(true) - .addListener(transformerListener) - .build(); -transformer.startTransformation(inputMediaItem, outputPath); -~~~ -{: .language-java} - -This allows apps to support slow motion videos without having to worry about -handling these special formats. All they need to do is to store and play the -flattened version of the video instead of the original one. - -Currently, Samsung's slow motion format is the only one supported. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/transforming-media -[Transformer API]: {{ site.exo_sdk }}/transformer/Transformer.html -[Supported formats page]: {{ site.baseurl }}/supported-formats.html - diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 15376049515..1ea270eb0d0 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,421 +1,5 @@ --- -title: Troubleshooting -redirect_from: - - /faqs.html - - /debugging-playback-issues.html +permalink: /troubleshooting.html +redirect_to: + - https://developer.android.com/media/media3/exoplayer/troubleshooting --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -* [Fixing "Cleartext HTTP traffic not permitted" errors][] -* [Fixing "SSLHandshakeException", "CertPathValidatorException" and "ERR_CERT_AUTHORITY_INVALID" errors][] -* [Why are some media files not seekable?][] -* [Why is seeking inaccurate in some MP3 files?][] -* [Why is seeking in my video slow?][] -* [Why do some MPEG-TS files fail to play?][] -* [Why are subtitles not found in some MPEG-TS files?][] -* [Why do some MP4/FMP4 files play incorrectly?][] -* [Why do some streams fail with HTTP response code 301 or 302?][] -* [Why do some streams fail with UnrecognizedInputFormatException?][] -* [Why doesn't setPlaybackParameters work properly on some devices?][] -* [What do "Player is accessed on the wrong thread" errors mean?][] -* [How can I fix "Unexpected status line: ICY 200 OK"?][] -* [How can I query whether the stream being played is a live stream?][] -* [How do I keep audio playing when my app is backgrounded?][] -* [Why does ExoPlayer support my content but the Cast extension doesn't?][] -* [Why does content fail to play, but no error is surfaced?][] -* [How can I get a decoding extension to load and be used for playback?][] -* [Can I play YouTube videos directly with ExoPlayer?][] -* [Video playback is stuttering][] - ---- - -#### Fixing "Cleartext HTTP traffic not permitted" errors #### - -This error will occur if your app requests cleartext HTTP traffic (i.e., -`http://` rather than `https://`) when its Network Security Configuration does -not permit it. If your app targets Android 9 (API level 28) or later, cleartext -HTTP traffic is disabled by the default configuration. - -If your app needs to work with cleartext HTTP traffic then you need to use a -Network Security Configuration that permits it. Please see Android's -[network security documentation](https://developer.android.com/training/articles/security-config.html) -for details. To enable all cleartext HTTP traffic, you can simply add -`android:usesCleartextTraffic="true"` to the `application` element of your app's -`AndroidManifest.xml`. - -The ExoPlayer demo app uses the default Network Security Configuration, and so -does not allow cleartext HTTP traffic. You can enable it using the instructions -above. - -#### Fixing "SSLHandshakeException", "CertPathValidatorException" and "ERR_CERT_AUTHORITY_INVALID" errors #### - -`SSLHandshakeException`, `CertPathValidatorException` and -`ERR_CERT_AUTHORITY_INVALID` all indicate a problem with the server's SSL -certificate. These errors are not ExoPlayer specific. Please see -[Android's SSL documentation](https://developer.android.com/training/articles/security-ssl#CommonProblems) -for more details. - -#### Why are some media files not seekable? #### - -By default ExoPlayer does not support seeking in media where the only method for -performing accurate seek operations is for the player to scan and index the -entire file. ExoPlayer considers such files as unseekable. Most modern media -container formats include metadata for seeking (e.g., a sample index), have a -well defined seek algorithm (e.g., interpolated bisection search for Ogg), or -indicate that their content is constant bitrate. Efficient seek operations are -possible and supported by ExoPlayer in these cases. - -If you require seeking but have unseekable media, we suggest converting your -content to use a more appropriate container format. For MP3, ADTS and AMR files, -you can also enable seeking under the assumption that the files have a constant -bitrate, as described -[here](customization.html#enabling-constant-bitrate-seeking). - -#### Why is seeking inaccurate in some MP3 files? #### - -Variable bitrate (VBR) MP3 files are fundamentally unsuitable for use cases that -require exact seeking. There are two reasons for this: - -1. For exact seeking, a container format will ideally provide a precise - time-to-byte mapping in a header. This mapping allows a player to map a - requested seek time to the corresponding byte offset, and start requesting, - parsing and playing media from that offset. The headers available for - specifying this mapping in MP3 (e.g., XING headers) are, unfortunately, often - imprecise. -1. For container formats that don't provide a precise time-to-byte mapping (or - any time-to-byte mapping at all), it's still possible to perform an exact - seek if the container includes absolute sample timestamps in the stream. In - this case a player can map the seek time to a best guess of the corresponding - byte offset, start requesting media from that offset, parse the first - absolute sample timestamp, and effectively perform a guided binary search - into the media until it finds the right sample. Unfortunately MP3 does not - include absolute sample timestamps in the stream, so this approach is not - possible. - -For these reasons, the only way to perform an exact seek into a VBR MP3 file is -to scan the entire file and manually build up a time-to-byte mapping in the -player. This strategy can be enabled by using [`FLAG_ENABLE_INDEX_SEEKING`][], -which can be [set on a `DefaultExtractorsFactory`][] using -[`setMp3ExtractorFlags`][]. Note that it doesn't scale well to large MP3 files, -particularly if the user tries to seek to near the end of the stream shortly -after starting playback, which requires the player to wait until it's downloaded -and indexed the entire stream before performing the seek. In ExoPlayer, we -decided to optimize for speed over accuracy in this case and -[`FLAG_ENABLE_INDEX_SEEKING`][] is therefore disabled by default. - -If you control the media you're playing, we strongly advise that you use a more -appropriate container format, such as MP4. There are no use cases we're aware of -where MP3 is the best choice of media format. - -#### Why is seeking in my video slow? #### - -When the player seeks to a new playback position in a video it needs to do two -things: - -1. Load the data corresponding to the new playback position into the buffer - (this may not be necessary if this data is already buffered). -2. Flush the video decoder and start decoding from the I-frame (keyframe) before - the new playback position, due to the [intra-frame coding] used by most video - compression formats. In order to ensure the seek is 'accurate' (i.e. - playback starts exactly at the seek position), all frames between the - preceding I-frame and the seek position need to be decoded and immediately - discarded (without being shown on the screen). - -The latency introduced by (1) can be mitigated by either increasing the amount -of data buffered in memory by the player, or [pre-caching the data to disk]. - -The latency introduced by (2) can be mitigated by either reducing the accuracy -of the seek using [`ExoPlayer.setSeekParameters`], or re-encoding the video -to have more frequent I-frames (which will result in a larger output file). - -#### Why do some MPEG-TS files fail to play? #### - -Some MPEG-TS files do not contain access unit delimiters (AUDs). By default -ExoPlayer relies on AUDs to cheaply detect frame boundaries. Similarly, some -MPEG-TS files do not contain IDR keyframes. By default these are the only type -of keyframes considered by ExoPlayer. - -ExoPlayer will appear to be stuck in the buffering state when asked to play an -MPEG-TS file that lacks AUDs or IDR keyframes. If you need to play such files, -you can do so using [`FLAG_DETECT_ACCESS_UNITS`][] and -[`FLAG_ALLOW_NON_IDR_KEYFRAMES`][] respectively. These flags can be [set on a -`DefaultExtractorsFactory`][] using [`setTsExtractorFlags`][] or on a -`DefaultHlsExtractorFactory` using the -[constructor]({{ site.exo_sdk }}/source/hls/DefaultHlsExtractorFactory.html#DefaultHlsExtractorFactory-int-boolean-). -Use of `FLAG_DETECT_ACCESS_UNITS` has no side effects other than being -computationally expensive relative to AUD based frame boundary detection. Use of -`FLAG_ALLOW_NON_IDR_KEYFRAMES` may result in temporary visual corruption at the -start of playback and immediately after seeks when playing some MPEG-TS files. - -#### Why are subtitles not found in some MPEG-TS files? #### - -Some MPEG-TS files include CEA-608 tracks but don't declare them in the -container metadata, so ExoPlayer is unable to detect them. You can manually -specify that the subtitle track(s) exist by providing a list of expected -subtitle formats to the `DefaultExtractorsFactory`, including the accessibility -channels that can be used to identify them in the MPEG-TS stream: - -```java -DefaultExtractorsFactory extractorsFactory = - new DefaultExtractorsFactory() - .setTsSubtitleFormats( - ImmutableList.of( - new Format.Builder() - .setSampleMimeType(MimeTypes.APPLICATION_CEA608) - .setAccessibilityChannel(accessibilityChannel) - // Set other subtitle format info, e.g. language. - .build())); -Player player = - new ExoPlayer.Builder( - context, - new DefaultMediaSourceFactory(context, extractorsFactory)) - .build(); -``` - -#### Why do some MP4/FMP4 files play incorrectly? #### - -Some MP4/FMP4 files contain edit lists that rewrite the media timeline by -skipping, moving or repeating lists of samples. ExoPlayer has partial support -for applying edit lists. For example, it can delay or repeat groups of samples -starting on a synchronization sample, but it does not truncate audio samples or -preroll media for edits that don't start on a synchronization sample. - -If you are seeing that part of the media is unexpectedly missing or repeated, -try setting [`Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS`][] or -[`FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS`][], which will cause -the extractor to ignore edit lists entirely. These can be [set on a -`DefaultExtractorsFactory`][] using [`setMp4ExtractorFlags`][] or -[`setFragmentedMp4ExtractorFlags`][]. - -#### Why do some streams fail with HTTP response code 301 or 302? #### - -HTTP response codes 301 and 302 both indicate redirection. Brief descriptions -can be found on [Wikipedia][]. When ExoPlayer makes a request and receives a -response with status code 301 or 302, it will normally follow the redirect -and start playback as normal. The one case where this does not happen by default -is for cross-protocol redirects. A cross-protocol redirect is one that redirects -from HTTPS to HTTP or vice-versa (or less commonly, between another pair of -protocols). You can test whether a URL causes a cross-protocol redirect using -the [wget][] command line tool as follows: -``` -wget "https://yourserver.com/test.mp3" 2>&1 | grep Location -``` -The output should look something like this: -``` -$ wget "https://yourserver.com/test.mp3" 2>&1 | grep Location -Location: https://second.com/test.mp3 [following] -Location: http://third.com/test.mp3 [following] -``` -In this example there are two redirects. The first redirect is from -`https://yourserver.com/test.mp3` to `https://second.com/test.mp3`. Both are -HTTPS, and so this is not a cross-protocol redirect. The second redirect is from -`https://second.com/test.mp3` to `http://third.com/test.mp3`. This redirects -from HTTPS to HTTP and so is a cross-protocol redirect. ExoPlayer will not -follow this redirect in its default configuration, meaning playback will fail. - -If you need to, you can configure ExoPlayer to follow cross-protocol redirects -when instantiating [`DefaultHttpDataSource.Factory`][] instances used in your -application. Learn about selecting and configuring the network stack -[here]({{ site.base_url }}/customization.html#configuring-the-network-stack). - -#### Why do some streams fail with UnrecognizedInputFormatException? #### - -This question relates to playback failures of the form: -``` -UnrecognizedInputFormatException: None of the available extractors -(MatroskaExtractor, FragmentedMp4Extractor, ...) could read the stream. -``` -There are two possible causes of this failure. The most common cause is that -you're trying to play DASH (mpd), HLS (m3u8) or SmoothStreaming (ism, isml) -content, but the player tries to play it as a progressive stream. To play such -streams, you must depend on the respective [ExoPlayer module][]. In cases where -the stream URI doesn't end with the standard file extension, you can also pass -`MimeTypes.APPLICATION_MPD`, `MimeTypes.APPLICATION_M3U8` or -`MimeTypes.APPLICATION_SS` to `setMimeType` of `MediaItem.Builder` to explicitly -specify the type of stream. - -The second, less common cause, is that ExoPlayer does not support the container -format of the media that you're trying to play. In this case the failure is -working as intended, however feel free to submit a feature request to our -[issue tracker][], including details of the container format and a test stream. -Please search for an existing feature request before submitting a new one. - -#### Why doesn't setPlaybackParameters work properly on some devices? #### - -When running a debug build of your app on Android M and earlier, you may -experience choppy performance, audible artifacts and high CPU utilization when -using the [`setPlaybackParameters`][] API. This is because an optimization -that's important to this API is disabled for debug builds running on these -versions of Android. - -It's important to note that this issue affects debug builds only. It does *not* -affect release builds, for which the optimization is always enabled. Hence the -releases you provide to end users should not be affected by this issue. - -#### What do "Player is accessed on the wrong thread" errors mean? #### - -See [A note on threading][] on the getting started page. - -#### How can I fix "Unexpected status line: ICY 200 OK"? #### - -This problem can occur if the server response includes an ICY status line, -rather than one that's HTTP compliant. ICY status lines are deprecated and -should not be used, so if you control the server you should update it to provide -an HTTP compliant response. If you're unable to do this then using the -[OkHttp extension][] will resolve the problem, since it's able to handle ICY -status lines correctly. - -#### How can I query whether the stream being played is a live stream? #### - -You can query the player's [`isCurrentWindowLive`][] method. In addition, you -can check [`isCurrentWindowDynamic`][] to find out whether the window is dynamic -(i.e., still updating over time). - -#### How do I keep audio playing when my app is backgrounded? #### - -There are a few steps that you need to take to ensure continued playback of -audio when your app is in the background: - -1. You need to have a running [foreground service][]. This prevents the system - from killing your process to free up resources. -1. You need to hold a [`WifiLock`][] and a [`WakeLock`][]. These ensure that the - system keeps the WiFi radio and CPU awake. This can be easily done if using - [`ExoPlayer`][] by calling [`setWakeMode`][], which will automatically - acquire and release the required locks at the correct times. - -It's important that you release the locks (if not using `setWakeMode`) and stop -the service as soon as audio is no longer being played. - -#### Why does ExoPlayer support my content but the Cast extension doesn't? #### - -It's possible that the content that you are trying to play is not -[CORS enabled][]. The [Cast framework][] requires content to be CORS enabled in -order to play it. - -#### Why does content fail to play, but no error is surfaced? #### - -It's possible that the device on which you are playing the content does not -support a specific media sample format. This can be easily confirmed by adding -an [`EventLogger`][] as a listener to your player, and looking for a line -similar to this one in Logcat: -``` -[ ] Track:x, id=x, mimeType=mime/type, ... , supported=NO_UNSUPPORTED_TYPE -``` -`NO_UNSUPPORTED_TYPE` means that the device is not able to decode the media -sample format specified by the `mimeType`. See the [Android media formats -documentation][] for information about supported sample formats. [How can I get -a decoding extension to load and be used for playback?] may also be useful. - -#### How can I get a decoding extension to load and be used for playback? #### - -* Most extensions have manual steps to check out and build the dependencies, so - make sure you've followed the steps in the README for the relevant extension. - For example, for the FFmpeg extension it's necessary to follow the - instructions in [extensions/ffmpeg/README.md][], including passing - configuration flags to [enable decoders][] for the format(s) you want to play. -* For extensions that have native code, make sure you're using the correct - version of the Android NDK as specified in the README, and look out for any - errors that appear during configuration and building. You should see `.so` - files appear in the `libs` subdirectory of the extension's path for each - supported architecture after following the steps in the README. -* To try out playback using the extension in the [demo application][], see - [enabling extension decoders][]. See the README for the extension for - instructions on using the extension from your own app. -* If you're using [`DefaultRenderersFactory`][], you should see an info-level - log line like "Loaded FfmpegAudioRenderer" in Logcat when the extension loads. - If that's missing, make sure the application has a dependency on the - extension. -* If you see warning-level logs from [`LibraryLoader`][] in Logcat, this - indicates that loading the native component of the extension failed. If this - happens, check you've followed the steps in the extension's README correctly - and that no errors were output while following the instructions. - -If you're still experiencing problems using extensions, please check the -ExoPlayer [issue tracker][] for any relevant recent issues. If you need to file -a new issue and it relates to building the native part of the extension, please -include full command line output from running README instructions, to help us -diagnose the issue. - -#### Can I play YouTube videos directly with ExoPlayer? #### - -No, ExoPlayer cannot play videos from YouTube, i.e., urls of the form -`https://www.youtube.com/watch?v=...`. Instead, you should use the [YouTube -Android Player API](https://developers.google.com/youtube/android/player/) which -is the official way to play YouTube videos on Android. - -#### Video playback is stuttering ### - -The device may not be able to decode the content fast enough if, for example, -the content bitrate or resolution exceeds the device capabilities. You may need -to use lower quality content to obtain good performance on such devices. - -If you're experiencing video stuttering on a device running Android 6 to 11, -particularly when playing DRM protected or high frame rate content, you can try -[enabling asynchronous buffer queueing]. - -[Fixing "Cleartext HTTP traffic not permitted" errors]: #fixing-cleartext-http-traffic-not-permitted-errors -[Fixing "SSLHandshakeException", "CertPathValidatorException" and "ERR_CERT_AUTHORITY_INVALID" errors]: #fixing-sslhandshakeexception-certpathvalidatorexception-and-err_cert_authority_invalid-errors -[What formats does ExoPlayer support?]: #what-formats-does-exoplayer-support -[Why are some media files not seekable?]: #why-are-some-media-files-not-seekable -[Why is seeking inaccurate in some MP3 files?]: #why-is-seeking-inaccurate-in-some-mp3-files -[Why is seeking in my video slow?]: #why-is-seeking-in-my-video-slow -[Why do some MPEG-TS files fail to play?]: #why-do-some-mpeg-ts-files-fail-to-play -[Why are subtitles not found in some MPEG-TS files?]: #why-are-subtitles-not-found-in-some-mpeg-ts-files -[Why do some MP4/FMP4 files play incorrectly?]: #why-do-some-mp4fmp4-files-play-incorrectly -[Why do some streams fail with HTTP response code 301 or 302?]: #why-do-some-streams-fail-with-http-response-code-301-or-302 -[Why do some streams fail with UnrecognizedInputFormatException?]: #why-do-some-streams-fail-with-unrecognizedinputformatexception -[Why doesn't setPlaybackParameters work properly on some devices?]: #why-doesnt-setplaybackparameters-work-properly-on-some-devices -[What do "Player is accessed on the wrong thread" errors mean?]: #what-do-player-is-accessed-on-the-wrong-thread-errors-mean -[How can I fix "Unexpected status line: ICY 200 OK"?]: #how-can-i-fix-unexpected-status-line-icy-200-ok -[How can I query whether the stream being played is a live stream?]: #how-can-i-query-whether-the-stream-being-played-is-a-live-stream -[How do I keep audio playing when my app is backgrounded?]: #how-do-i-keep-audio-playing-when-my-app-is-backgrounded -[Why does ExoPlayer support my content but the Cast extension doesn't?]: #why-does-exoplayer-support-my-content-but-the-cast-extension-doesnt -[Why does content fail to play, but no error is surfaced?]: #why-does-content-fail-to-play-but-no-error-is-surfaced -[How can I get a decoding extension to load and be used for playback?]: #how-can-i-get-a-decoding-extension-to-load-and-be-used-for-playback -[Can I play YouTube videos directly with ExoPlayer?]: #can-i-play-youtube-videos-directly-with-exoplayer -[Video playback is stuttering]: #video-playback-is-stuttering - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/troubleshooting -[Supported formats]: {{ site.baseurl }}/supported-formats.html -[set on a `DefaultExtractorsFactory`]: {{ site.base_url }}/customization.html#customizing-extractor-flags -[`setMp3ExtractorFlags`]: {{ site.exo_sdk }}/extractor/DefaultExtractorsFactory#setMp3ExtractorFlags(@com.google.android.exoplayer2.extractor.mp4.Mp4Extractor.Flagsint) -[`FLAG_ENABLE_INDEX_SEEKING`]: {{ site.exo_sdk }}/extractor/mp3/Mp3Extractor.html#FLAG_ENABLE_INDEX_SEEKING -[intra-frame coding]: https://en.wikipedia.org/wiki/Intra-frame_coding -[pre-caching the data to disk]: https://exoplayer.dev/downloading-media.html -[`ExoPlayer.setSeekParameters]: {{ site.exo_sdk }}/ExoPlayer.html#setSeekParameters(com.google.android.exoplayer2.SeekParameters) -[`FLAG_DETECT_ACCESS_UNITS`]: {{ site.exo_sdk }}/extractor/ts/DefaultTsPayloadReaderFactory.html#FLAG_DETECT_ACCESS_UNITS -[`FLAG_ALLOW_NON_IDR_KEYFRAMES`]: {{ site.exo_sdk }}/extractor/ts/DefaultTsPayloadReaderFactory.html#FLAG_ALLOW_NON_IDR_KEYFRAMES -[`setTsExtractorFlags`]: {{ site.exo_sdk }}/extractor/DefaultExtractorsFactory#setTsExtractorFlags(@com.google.android.exoplayer2.extractor.mp4.Mp4Extractor.Flagsint) -[`Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS`]: {{ site.exo_sdk }}/extractor/mp4/Mp4Extractor.html#FLAG_WORKAROUND_IGNORE_EDIT_LISTS -[`FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS`]: {{ site.exo_sdk }}/extractor/mp4/FragmentedMp4Extractor.html#FLAG_WORKAROUND_IGNORE_EDIT_LISTS -[`setMp4ExtractorFlags`]: {{ site.exo_sdk }}/extractor/DefaultExtractorsFactory#setMp4ExtractorFlags(@com.google.android.exoplayer2.extractor.mp4.Mp4Extractor.Flagsint) -[`setFragmentedMp4ExtractorFlags`]: {{ site.exo_sdk }}/extractor/DefaultExtractorsFactory#setFragmentedMp4ExtractorFlags(@com.google.android.exoplayer2.extractor.mp4.Mp4Extractor.Flagsint) -[Wikipedia]: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes -[wget]: https://www.gnu.org/software/wget/manual/wget.html -[`DefaultHttpDataSource.Factory`]: {{ site.exo_sdk }}/upstream/DefaultHttpDataSource.Factory.html -[ExoPlayer module]: {{ site.base_url }}/hello-world.html#add-exoplayer-modules -[issue tracker]: https://github.com/google/ExoPlayer/issues -[`isCurrentWindowLive`]: {{ site.exo_sdk }}/Player.html#isCurrentWindowLive() -[`isCurrentWindowDynamic`]: {{ site.exo_sdk }}/Player.html#isCurrentWindowDynamic() -[`setPlaybackParameters`]: {{ site.exo_sdk }}/Player.html#setPlaybackParameters(com.google.android.exoplayer2.PlaybackParameters) -[foreground service]: https://developer.android.com/guide/components/services.html#Foreground -[`WifiLock`]: {{ site.android_sdk }}/android/net/wifi/WifiManager.WifiLock.html -[`WakeLock`]: {{ site.android_sdk }}/android/os/PowerManager.WakeLock.html -[`ExoPlayer`]: {{ site.exo_sdk }}/ExoPlayer.html -[`setWakeMode`]: {{ site.exo_sdk }}/ExoPlayer.html#setWakeMode(int) -[A note on threading]: {{ site.base_url }}/hello-world.html#a-note-on-threading -[OkHttp extension]: {{ site.release_v2 }}/extensions/okhttp -[CORS enabled]: https://www.w3.org/wiki/CORS_Enabled -[Cast framework]: {{ site.google_sdk }}/cast/docs/chrome_sender/advanced#cors_requirements -[Android media formats documentation]: https://developer.android.com/guide/topics/media/media-formats#core -[extensions/ffmpeg/README.md]: {{ site.release_v2 }}/extensions/ffmpeg/README.md -[enable decoders]: {{ site.base_url }}/supported-formats.html#ffmpeg-extension -[demo application]: {{ site.base_url }}/demo-application.html -[enabling extension decoders]: {{ site.base_url }}/demo-application.html#enabling-extension-decoders -[`DefaultRenderersFactory`]: {{ site.exo_sdk }}/DefaultRenderersFactory.html -[`LibraryLoader`]: {{ site.exo_sdk }}/util/LibraryLoader.html -[`EventLogger`]: {{ site.baseurl }}/debug-logging.html -[enabling asynchronous buffer queueing]: {{ site.baseurl }}/customization.html#enabling-asynchronous-buffer-queueing diff --git a/docs/ui-components.md b/docs/ui-components.md index 92cb2367891..016d12b8e15 100644 --- a/docs/ui-components.md +++ b/docs/ui-components.md @@ -1,150 +1,5 @@ --- -title: UI components +permalink: /ui-components.html +redirect_to: + - https://developer.android.com/media/media3/ui/playerview --- - -This documentation may be out-of-date. Please refer to the -[documentation for the latest ExoPlayer release][] on developer.android.com. -{:.info} - -An app playing media requires user interface components for displaying media and -controlling playback. The ExoPlayer library includes a UI module that contains -a number of UI components. To depend on the UI module add a dependency as shown -below. - -~~~ -implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X' -~~~ -{: .language-gradle} - -The most important component is `StyledPlayerView`, a view for media -playbacks. It displays video, subtitles and album art during playback, as -well as playback controls. - -`StyledPlayerView` has a `setPlayer` method for attaching and detaching (by -passing `null`) player instances. - -## StyledPlayerView ## - -`StyledPlayerView` can be used for both video and audio playbacks. It renders -video and subtitles in the case of video playback, and can display artwork -included as metadata in audio files. You can include it in your layout files -like any other UI component. For example, a `StyledPlayerView` can be included -with the following XML: - -~~~ - -~~~ -{: .language-xml} - -The snippet above illustrates that `StyledPlayerView` provides several -attributes. These attributes can be used to customize the view's behavior, as -well as its look and feel. Most of these attributes have corresponding setter -methods, which can be used to customize the view at runtime. The -[`StyledPlayerView`][] Javadoc lists these attributes and setter methods in -more detail. - -Once the view is declared in the layout file, it can be looked up in the -`onCreate` method of the activity: - -~~~ -@Override -protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // ... - playerView = findViewById(R.id.player_view); -} -~~~ -{: .language-java} - -When a player has been initialized, it can be attached to the view by calling -`setPlayer`: - -~~~ -// Instantiate the player. -player = new ExoPlayer.Builder(context).build(); -// Attach player to the view. -playerView.setPlayer(player); -// Set the media item to be played. -player.setMediaItem(mediaItem); -// Prepare the player. -player.prepare(); -~~~ -{: .language-java} - -### Choosing a surface type ### - -The `surface_type` attribute of `StyledPlayerView` lets you set the type of -surface used for video playback. Besides the values `spherical_gl_surface_view` -(which is a special value for spherical video playback) and -`video_decoder_gl_surface_view` (which is for video rendering using extension -renderers), the allowed values are `surface_view`, `texture_view` and `none`. If -the view is for audio playback only, `none` should be used to avoid having to -create a surface, since doing so can be expensive. - -If the view is for regular video playback then `surface_view` or `texture_view` -should be used. `SurfaceView` has a number of benefits over `TextureView` for -video playback: - -* Significantly lower power consumption on many devices. -* More accurate frame timing, resulting in smoother video playback. -* Support for secure output when playing DRM protected content. -* The ability to render video content at the full resolution of the display on - Android TV devices that upscale the UI layer. - -`SurfaceView` should therefore be preferred over `TextureView` where possible. -`TextureView` should be used only if `SurfaceView` does not meet your needs. One -example is where smooth animations or scrolling of the video surface is required -prior to Android N, as described below. For this case, it's preferable to use -`TextureView` only when [`SDK_INT`][] is less than 24 (Android N) and -`SurfaceView` otherwise. - -`SurfaceView` rendering wasn't properly synchronized with view animations until -Android N. On earlier releases this could result in unwanted effects when a -`SurfaceView` was placed into scrolling container, or when it was subjected to -animation. Such effects included the view's contents appearing to lag slightly -behind where it should be displayed, and the view turning black when subjected -to animation. To achieve smooth animation or scrolling of video prior to Android -N, it's therefore necessary to use `TextureView` rather than `SurfaceView`. -{:.info} - -Some Android TV devices run their UI layer at a resolution that's lower than the -full resolution of the display, upscaling it for presentation to the user. For -example, the UI layer may be run at 1080p on an Android TV that has a 4K -display. On such devices, `SurfaceView` must be used to render content at the -full resolution of the display. The full resolution of the display (in its -current display mode) can be queried using [`Util.getCurrentDisplayModeSize`][]. -The UI layer resolution can be queried using Android's [`Display.getSize`] API. -{:.info} - -### Overriding drawables ### - -We don't guarantee that the customizations described in the following section -will continue to work in future versions of the library. The resource IDs may -change name, or some may be deleted entirely. This is indicated by the -[resource IDs being marked 'private'][]. -{:.info} - -`StyledPlayerView` uses `StyledPlayerControlView` to display the playback -controls and progress bar. The drawables used by `StyledPlayerControlView` can -be overridden by drawables with the same names defined in your application. See -the [`StyledPlayerControlView`][] Javadoc for a list of control drawables that -can be overridden. - -## Further customization ## - -Where customization beyond that described above is required, we expect that app -developers will implement their own UI components rather than use those provided -by ExoPlayer's UI module. - -[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/ui-components -[`StyledPlayerView`]: {{ site.exo_sdk }}/ui/StyledPlayerView.html -[`StyledPlayerControlView`]: {{ site.exo_sdk }}/ui/StyledPlayerControlView.html -[resource IDs being marked 'private']: https://developer.android.com/studio/projects/android-library#PrivateResources -[`SDK_INT`]: {{ site.android_sdk }}/android/os/Build.VERSION.html#SDK_INT -[`Util.getCurrentDisplayModeSize`]: {{ site.exo_sdk }}/util/Util.html#getCurrentDisplayModeSize(android.content.Context) -[`Display.getSize`]: {{ site.android_sdk }}/android/view/Display.html#getSize(android.graphics.Point)