1--- 2title: Ad insertion 3--- 4 5ExoPlayer can be used for both client-side and server-side ad insertion. 6 7## Client-side ad insertion ## 8 9In client-side ad insertion, the player switches between loading media from 10different URLs as it transitions between playing content and ads. Information 11about ads is loaded separately from the media, such as from an XML [VAST][] or 12[VMAP][] ad tag. This can include ad cue positions relative to the start of the 13content, the actual ad media URIs and metadata such as whether a given ad is 14skippable. 15 16When using ExoPlayer's `AdsMediaSource` for client-side ad insertion, the player 17has information about the ads to be played. This has several benefits: 18 19* The player can expose metadata and functionality relating to ads via its API. 20* [ExoPlayer UI components][] can show markers for ad positions automatically, 21 and change their behavior depending on whether ad is playing. 22* Internally, the player can keep a consistent buffer across transitions between 23 ads and content. 24 25In this setup, the player takes care of switching between ads and content, which 26means that apps don't need to take care of controlling multiple separate 27background/foreground players for ads and content. 28 29When preparing content videos and ad tags for use with client-side ad insertion, 30ads should ideally be positioned at synchronization samples (keyframes) in the 31content video so that the player can resume content playback seamlessly. 32 33### Declarative ad support ### 34 35An ad tag URI can be specified when building a `MediaItem`: 36 37~~~ 38MediaItem mediaItem = 39 new MediaItem.Builder() 40 .setUri(videoUri) 41 .setAdsConfiguration( 42 new MediaItem.AdsConfiguration.Builder(adTagUri).build()) 43 .build(); 44~~~ 45{: .language-java} 46 47To enable player support for media items that specify ad tags, it's necessary to 48build and inject a `DefaultMediaSourceFactory` configured with an 49`AdsLoader.Provider` and an `AdViewProvider` when creating the player: 50 51~~~ 52MediaSource.Factory mediaSourceFactory = 53 new DefaultMediaSourceFactory(context) 54 .setAdsLoaderProvider(adsLoaderProvider) 55 .setAdViewProvider(playerView); 56ExoPlayer player = new ExoPlayer.Builder(context) 57 .setMediaSourceFactory(mediaSourceFactory) 58 .build(); 59~~~ 60{: .language-java} 61 62Internally, `DefaultMediaSourceFactory` will wrap the content media source in an 63`AdsMediaSource`. The `AdsMediaSource` will obtain an `AdsLoader` from the 64`AdsLoader.Provider` and use it to insert ads as defined by the media item's ad 65tag. 66 67ExoPlayer's `StyledPlayerView` implements `AdViewProvider`. The IMA extension 68provides an easy to use `AdsLoader`, as described below. 69 70### Playlists with ads ### 71 72When playing a [playlist][] with multiple media items, the default behavior is 73to request the ad tag and store ad playback state once for each media ID, 74content URI and ad tag URI combination. This means that users will see ads for 75every media item with ads that has a distinct media ID or content URI, even if 76the ad tag URIs match. If a media item is repeated, the user will see the 77corresponding ads only once (the ad playback state stores whether ads have been 78played, so they are skipped after their first occurrence). 79 80It's possible to customize this behavior by passing an opaque ads identifier 81with which ad playback state for a given media item is linked, based on object 82equality. Here is an example where ad playback state is linked to the ad tag 83URI only, rather than the combination of the media ID and ad tag URI, by 84passing the ad tag URI as the ads identifier. The effect is that ads will load 85only once and the user will not see ads on the second item when playing the 86playlist from start to finish. 87 88~~~ 89// Build the media items, passing the same ads identifier for both items, 90// which means they share ad playback state so ads play only once. 91MediaItem firstItem = 92 new MediaItem.Builder() 93 .setUri(firstVideoUri) 94 .setAdsConfiguration( 95 new MediaItem.AdsConfiguration.Builder(adTagUri) 96 .setAdsId(adTagUri) 97 .build()) 98 .build(); 99MediaItem secondItem = 100 new MediaItem.Builder() 101 .setUri(secondVideoUri) 102 .setAdsConfiguration( 103 new MediaItem.AdsConfiguration.Builder(adTagUri) 104 .setAdsId(adTagUri) 105 .build()) 106 .build(); 107player.addMediaItem(firstItem); 108player.addMediaItem(secondItem); 109~~~ 110{: .language-java} 111 112### IMA extension ### 113 114The [ExoPlayer IMA extension][] provides `ImaAdsLoader`, making it easy to 115integrate client-side ad insertion into your app. It wraps the functionality of 116the [client-side IMA SDK][] to support insertion of VAST/VMAP ads. For 117instructions on how to use the extension, including how to handle backgrounding 118and resuming playback, please see the [README][]. 119 120The [demo application][] uses the IMA extension, and includes several sample 121VAST/VMAP ad tags in the sample list. 122 123#### UI considerations #### 124 125`StyledPlayerView` hides its transport controls during playback of ads by 126default, but apps can toggle this behavior by calling 127`setControllerHideDuringAds`. The IMA SDK will show additional views on top of 128the player while an ad is playing (e.g., a 'more info' link and a skip button, 129if applicable). 130 131Since advertisers expect a consistent experience across apps, the IMA SDK does 132not allow customization of the views that it shows while an ad is playing. It is 133therefore not possible to remove or reposition the skip button, change the 134fonts, or make other customizations to the visual appearance of these views. 135{:.info} 136 137The IMA SDK may report whether ads are obscured by application provided views 138rendered on top of the player. Apps that need to overlay views that are 139essential for controlling playback must register them with the IMA SDK so that 140they can be omitted from viewability calculations. When using `StyledPlayerView` 141as the `AdViewProvider`, it will automatically register its control overlays. 142Apps that use a custom player UI must register overlay views by returning them 143from `AdViewProvider.getAdOverlayInfos`. 144 145For more information about overlay views, see 146[Open Measurement in the IMA SDK][]. 147 148#### Companion ads #### 149 150Some ad tags contain additional companion ads that can be shown in 'slots' in an 151app UI. These slots can be passed via 152`ImaAdsLoader.Builder.setCompanionAdSlots(slots)`. For more information see 153[Adding Companion Ads][]. 154 155#### Standalone ads #### 156 157The IMA SDK is designed for inserting ads into media content, not for playing 158standalone ads by themselves. Hence playback of standalone ads is not supported 159by the IMA extension. We recommend using the [Google Mobile Ads SDK][] instead 160for this use case. 161 162### Using a third-party ads SDK ### 163 164If you need to load ads via a third-party ads SDK, it's worth checking whether 165it already provides an ExoPlayer integration. If not, implementing a custom 166`AdsLoader` that wraps the third-party ads SDK is the recommended approach, 167since it provides the benefits of `AdsMediaSource` described above. 168`ImaAdsLoader` acts as an example implementation. 169 170Alternatively, you can use ExoPlayer's [playlist support][] to build a sequence 171of ads and content clips: 172 173~~~ 174// A pre-roll ad. 175MediaItem preRollAd = MediaItem.fromUri(preRollAdUri); 176// The start of the content. 177MediaItem contentStart = 178 new MediaItem.Builder() 179 .setUri(contentUri) 180 .setClippingConfiguration( 181 new ClippingConfiguration.Builder() 182 .setEndPositionMs(120_000) 183 .build()) 184 .build(); 185// A mid-roll ad. 186MediaItem midRollAd = MediaItem.fromUri(midRollAdUri); 187// The rest of the content 188MediaItem contentEnd = 189 new MediaItem.Builder() 190 .setUri(contentUri) 191 .setClippingConfiguration( 192 new ClippingConfiguration.Builder() 193 .setStartPositionMs(120_000) 194 .build()) 195 .build(); 196 197// Build the playlist. 198player.addMediaItem(preRollAd); 199player.addMediaItem(contentStart); 200player.addMediaItem(midRollAd); 201player.addMediaItem(contentEnd); 202~~~ 203{: .language-java} 204 205## Server-side ad insertion ## 206 207In server-side ad insertion (also called dynamic ad insertion, or DAI), the 208media stream contains both ads and content. A DASH manifest may point to both 209content and ad segments, possibly in separate periods. For HLS, see the Apple 210documentation on [incorporating ads into a playlist][]. 211 212When using server-side ad insertion, the client may need to resolve the media 213URL dynamically to get the stitched stream, it may need to display ads overlays 214in the UI or it may need to report events to an ads SDK or ad server. 215 216ExoPlayer's `DefaultMediaSourceFactory` can delegate all these tasks to a 217server-side ad insertion `MediaSource` for URIs using the `ssai://` scheme: 218 219``` 220Player player = 221 new ExoPlayer.Builder(context) 222 .setMediaSourceFactory( 223 new DefaultMediaSourceFactory(dataSourceFactory) 224 .setServerSideAdInsertionMediaSourceFactory(ssaiFactory)) 225 .build(); 226``` 227 228### IMA extension ### 229 230The [ExoPlayer IMA extension][] provides `ImaServerSideAdInsertionMediaSource`, 231making it easy to integrate with IMA's server-side inserted ad streams in your 232app. It wraps the functionality of the [IMA DAI SDK for Android][] and fully 233integrates the provided ad metadata into the player. For example, this allows 234you to use methods like `Player.isPlayingAd()`, listen to content-ad transitions 235and let the player handle ad playback logic like skipping already played ads. 236 237In order to use this class, you need to set up the 238`ImaServerSideAdInsertionMediaSource.AdsLoader` and the 239`ImaServerSideAdInsertionMediaSource.Factory` and connect them to the player: 240 241``` 242// MediaSource.Factory to load the actual media stream. 243DefaultMediaSourceFactory defaultMediaSourceFactory = 244 new DefaultMediaSourceFactory(dataSourceFactory); 245// AdsLoader that can be reused for multiple playbacks. 246ImaServerSideAdInsertionMediaSource.AdsLoader adsLoader = 247 new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(context, adViewProvider) 248 .build(); 249// MediaSource.Factory to create the ad sources for the current player. 250ImaServerSideAdInsertionMediaSource.Factory adsMediaSourceFactory = 251 new ImaServerSideAdInsertionMediaSource.Factory(adsLoader, defaultMediaSourceFactory); 252// Configure DefaultMediaSourceFactory to create both IMA DAI sources and 253// regular media sources. If you just play IMA DAI streams, you can also use 254// adsMediaSourceFactory directly. 255defaultMediaSourceFactory.setServerSideAdInsertionMediaSourceFactory(adsMediaSourceFactory); 256// Set the MediaSource.Factory on the Player. 257Player player = 258 new ExoPlayer.Builder(context) 259 .setMediaSourceFactory(defaultMediaSourceFactory) 260 .build(); 261// Set the player on the AdsLoader 262adsLoader.setPlayer(player); 263``` 264 265Load your IMA asset key, or content source id and video id, by building an URL 266with `ImaServerSideAdInsertionUriBuilder`: 267 268``` 269Uri ssaiUri = 270 new ImaServerSideAdInsertionUriBuilder().setAssetKey(assetKey).build(); 271player.setMediaItem(MediaItem.fromUri(ssaiUri)); 272``` 273 274Finally, release your ads loader once it's no longer used: 275``` 276adsLoader.release(); 277``` 278 279Currently only a single IMA server-side ad insertion stream is supported in the 280same playlist. You can combine the stream with other media but not with another 281IMA server-side ad insertion stream. 282{:.info} 283 284#### UI considerations #### 285 286The same [UI considerations as for client-side ad insertion][] apply to 287server-side ad insertion too. 288 289#### Companion ads #### 290 291Some ad tags contain additional companion ads that can be shown in 'slots' in an 292app UI. These slots can be passed via 293`ImaServerSideAdInsertionMediaSource.AdsLoader.Builder.setCompanionAdSlots(slots)`. 294For more information see [Adding Companion Ads][]. 295 296### Using a third-party ads SDK ### 297 298If you need to load ads via a third-party ads SDK, it’s worth checking whether 299it already provides an ExoPlayer integration. If not, it's recommended to 300provide a custom `MediaSource` that accepts URIs with the `ssai://` scheme 301similar to `ImaServerSideAdInsertionMediaSource`. 302 303The actual logic of creating the ad structure can be delegated to the general 304purpose `ServerSideAdInsertionMediaSource`, which wraps a stream `MediaSource` 305and allows the user to set and update the `AdPlaybackState` representing the ad 306metadata. 307 308Often, server-side inserted ad streams contain timed events to notify the player 309about ad metadata. Please see [supported formats][] for information on what 310timed metadata formats are supported by ExoPlayer. Custom ads SDK `MediaSource`s 311can listen for timed metadata events from the player, e.g., via 312`ExoPlayer.addMetadataOutput`. 313 314[VAST]: https://www.iab.com/wp-content/uploads/2015/06/VASTv3_0.pdf 315[VMAP]: https://www.iab.com/guidelines/digital-video-multiple-ad-playlist-vmap-1-0-1/ 316[ExoPlayer UI components]: {{ site.baseurl }}/ui-components.html 317[ExoPlayer IMA extension]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima 318[client-side IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android 319[README]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima 320[demo application]: {{ site.baseurl }}/demo-application.html 321[Open Measurement in the IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android/omsdk 322[Adding Companion Ads]: https://developers.google.com/interactive-media-ads/docs/sdks/android/companions 323[playlist]: {{ site.baseurl }}/playlists.html 324[playlist support]: {{ site.baseurl }}/playlists.html 325[incorporating ads into a playlist]: https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/incorporating_ads_into_a_playlist 326[supported formats]: {{ site.baseurl }}/supported-formats.html 327[Google Mobile Ads SDK]: https://developers.google.com/admob/android/quick-start 328[IMA DAI SDK for Android]: https://developers.google.com/interactive-media-ads/docs/sdks/android/dai 329[UI considerations as for client-side ad insertion]: #ui-considerations 330