1*30877f79SAndroid Build Coastguard Worker--- 2*30877f79SAndroid Build Coastguard Workertitle: Ad insertion 3*30877f79SAndroid Build Coastguard Worker--- 4*30877f79SAndroid Build Coastguard Worker 5*30877f79SAndroid Build Coastguard WorkerExoPlayer can be used for both client-side and server-side ad insertion. 6*30877f79SAndroid Build Coastguard Worker 7*30877f79SAndroid Build Coastguard Worker## Client-side ad insertion ## 8*30877f79SAndroid Build Coastguard Worker 9*30877f79SAndroid Build Coastguard WorkerIn client-side ad insertion, the player switches between loading media from 10*30877f79SAndroid Build Coastguard Workerdifferent URLs as it transitions between playing content and ads. Information 11*30877f79SAndroid Build Coastguard Workerabout ads is loaded separately from the media, such as from an XML [VAST][] or 12*30877f79SAndroid Build Coastguard Worker[VMAP][] ad tag. This can include ad cue positions relative to the start of the 13*30877f79SAndroid Build Coastguard Workercontent, the actual ad media URIs and metadata such as whether a given ad is 14*30877f79SAndroid Build Coastguard Workerskippable. 15*30877f79SAndroid Build Coastguard Worker 16*30877f79SAndroid Build Coastguard WorkerWhen using ExoPlayer's `AdsMediaSource` for client-side ad insertion, the player 17*30877f79SAndroid Build Coastguard Workerhas information about the ads to be played. This has several benefits: 18*30877f79SAndroid Build Coastguard Worker 19*30877f79SAndroid Build Coastguard Worker* The player can expose metadata and functionality relating to ads via its API. 20*30877f79SAndroid Build Coastguard Worker* [ExoPlayer UI components][] can show markers for ad positions automatically, 21*30877f79SAndroid Build Coastguard Worker and change their behavior depending on whether ad is playing. 22*30877f79SAndroid Build Coastguard Worker* Internally, the player can keep a consistent buffer across transitions between 23*30877f79SAndroid Build Coastguard Worker ads and content. 24*30877f79SAndroid Build Coastguard Worker 25*30877f79SAndroid Build Coastguard WorkerIn this setup, the player takes care of switching between ads and content, which 26*30877f79SAndroid Build Coastguard Workermeans that apps don't need to take care of controlling multiple separate 27*30877f79SAndroid Build Coastguard Workerbackground/foreground players for ads and content. 28*30877f79SAndroid Build Coastguard Worker 29*30877f79SAndroid Build Coastguard WorkerWhen preparing content videos and ad tags for use with client-side ad insertion, 30*30877f79SAndroid Build Coastguard Workerads should ideally be positioned at synchronization samples (keyframes) in the 31*30877f79SAndroid Build Coastguard Workercontent video so that the player can resume content playback seamlessly. 32*30877f79SAndroid Build Coastguard Worker 33*30877f79SAndroid Build Coastguard Worker### Declarative ad support ### 34*30877f79SAndroid Build Coastguard Worker 35*30877f79SAndroid Build Coastguard WorkerAn ad tag URI can be specified when building a `MediaItem`: 36*30877f79SAndroid Build Coastguard Worker 37*30877f79SAndroid Build Coastguard Worker~~~ 38*30877f79SAndroid Build Coastguard WorkerMediaItem mediaItem = 39*30877f79SAndroid Build Coastguard Worker new MediaItem.Builder() 40*30877f79SAndroid Build Coastguard Worker .setUri(videoUri) 41*30877f79SAndroid Build Coastguard Worker .setAdsConfiguration( 42*30877f79SAndroid Build Coastguard Worker new MediaItem.AdsConfiguration.Builder(adTagUri).build()) 43*30877f79SAndroid Build Coastguard Worker .build(); 44*30877f79SAndroid Build Coastguard Worker~~~ 45*30877f79SAndroid Build Coastguard Worker{: .language-java} 46*30877f79SAndroid Build Coastguard Worker 47*30877f79SAndroid Build Coastguard WorkerTo enable player support for media items that specify ad tags, it's necessary to 48*30877f79SAndroid Build Coastguard Workerbuild and inject a `DefaultMediaSourceFactory` configured with an 49*30877f79SAndroid Build Coastguard Worker`AdsLoader.Provider` and an `AdViewProvider` when creating the player: 50*30877f79SAndroid Build Coastguard Worker 51*30877f79SAndroid Build Coastguard Worker~~~ 52*30877f79SAndroid Build Coastguard WorkerMediaSource.Factory mediaSourceFactory = 53*30877f79SAndroid Build Coastguard Worker new DefaultMediaSourceFactory(context) 54*30877f79SAndroid Build Coastguard Worker .setAdsLoaderProvider(adsLoaderProvider) 55*30877f79SAndroid Build Coastguard Worker .setAdViewProvider(playerView); 56*30877f79SAndroid Build Coastguard WorkerExoPlayer player = new ExoPlayer.Builder(context) 57*30877f79SAndroid Build Coastguard Worker .setMediaSourceFactory(mediaSourceFactory) 58*30877f79SAndroid Build Coastguard Worker .build(); 59*30877f79SAndroid Build Coastguard Worker~~~ 60*30877f79SAndroid Build Coastguard Worker{: .language-java} 61*30877f79SAndroid Build Coastguard Worker 62*30877f79SAndroid Build Coastguard WorkerInternally, `DefaultMediaSourceFactory` will wrap the content media source in an 63*30877f79SAndroid Build Coastguard Worker`AdsMediaSource`. The `AdsMediaSource` will obtain an `AdsLoader` from the 64*30877f79SAndroid Build Coastguard Worker`AdsLoader.Provider` and use it to insert ads as defined by the media item's ad 65*30877f79SAndroid Build Coastguard Workertag. 66*30877f79SAndroid Build Coastguard Worker 67*30877f79SAndroid Build Coastguard WorkerExoPlayer's `StyledPlayerView` and `PlayerView` UI components both 68*30877f79SAndroid Build Coastguard Workerimplement `AdViewProvider`. The IMA extension provides an easy to use 69*30877f79SAndroid Build Coastguard Worker`AdsLoader`, as described below. 70*30877f79SAndroid Build Coastguard Worker 71*30877f79SAndroid Build Coastguard Worker### Playlists with ads ### 72*30877f79SAndroid Build Coastguard Worker 73*30877f79SAndroid Build Coastguard WorkerWhen playing a [playlist][] with multiple media items, the default behavior is 74*30877f79SAndroid Build Coastguard Workerto request the ad tag and store ad playback state once for each media ID, 75*30877f79SAndroid Build Coastguard Workercontent URI and ad tag URI combination. This means that users will see ads for 76*30877f79SAndroid Build Coastguard Workerevery media item with ads that has a distinct media ID or content URI, even if 77*30877f79SAndroid Build Coastguard Workerthe ad tag URIs match. If a media item is repeated, the user will see the 78*30877f79SAndroid Build Coastguard Workercorresponding ads only once (the ad playback state stores whether ads have been 79*30877f79SAndroid Build Coastguard Workerplayed, so they are skipped after their first occurrence). 80*30877f79SAndroid Build Coastguard Worker 81*30877f79SAndroid Build Coastguard WorkerIt's possible to customize this behavior by passing an opaque ads identifier 82*30877f79SAndroid Build Coastguard Workerwith which ad playback state for a given media item is linked, based on object 83*30877f79SAndroid Build Coastguard Workerequality. Here is an example where ad playback state is linked to the ad tag 84*30877f79SAndroid Build Coastguard WorkerURI only, rather than the combination of the media ID and ad tag URI, by 85*30877f79SAndroid Build Coastguard Workerpassing the ad tag URI as the ads identifier. The effect is that ads will load 86*30877f79SAndroid Build Coastguard Workeronly once and the user will not see ads on the second item when playing the 87*30877f79SAndroid Build Coastguard Workerplaylist from start to finish. 88*30877f79SAndroid Build Coastguard Worker 89*30877f79SAndroid Build Coastguard Worker~~~ 90*30877f79SAndroid Build Coastguard Worker// Build the media items, passing the same ads identifier for both items, 91*30877f79SAndroid Build Coastguard Worker// which means they share ad playback state so ads play only once. 92*30877f79SAndroid Build Coastguard WorkerMediaItem firstItem = 93*30877f79SAndroid Build Coastguard Worker new MediaItem.Builder() 94*30877f79SAndroid Build Coastguard Worker .setUri(firstVideoUri) 95*30877f79SAndroid Build Coastguard Worker .setAdsConfiguration( 96*30877f79SAndroid Build Coastguard Worker new MediaItem.AdsConfiguration.Builder(adTagUri) 97*30877f79SAndroid Build Coastguard Worker .setAdsId(adTagUri) 98*30877f79SAndroid Build Coastguard Worker .build()) 99*30877f79SAndroid Build Coastguard Worker .build(); 100*30877f79SAndroid Build Coastguard WorkerMediaItem secondItem = 101*30877f79SAndroid Build Coastguard Worker new MediaItem.Builder() 102*30877f79SAndroid Build Coastguard Worker .setUri(secondVideoUri) 103*30877f79SAndroid Build Coastguard Worker .setAdsConfiguration( 104*30877f79SAndroid Build Coastguard Worker new MediaItem.AdsConfiguration.Builder(adTagUri) 105*30877f79SAndroid Build Coastguard Worker .setAdsId(adTagUri) 106*30877f79SAndroid Build Coastguard Worker .build()) 107*30877f79SAndroid Build Coastguard Worker .build(); 108*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(firstItem); 109*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(secondItem); 110*30877f79SAndroid Build Coastguard Worker~~~ 111*30877f79SAndroid Build Coastguard Worker{: .language-java} 112*30877f79SAndroid Build Coastguard Worker 113*30877f79SAndroid Build Coastguard Worker### IMA extension ### 114*30877f79SAndroid Build Coastguard Worker 115*30877f79SAndroid Build Coastguard WorkerThe [ExoPlayer IMA extension][] provides `ImaAdsLoader`, making it easy to 116*30877f79SAndroid Build Coastguard Workerintegrate client-side ad insertion into your app. It wraps the functionality of 117*30877f79SAndroid Build Coastguard Workerthe [client-side IMA SDK][] to support insertion of VAST/VMAP ads. For 118*30877f79SAndroid Build Coastguard Workerinstructions on how to use the extension, including how to handle backgrounding 119*30877f79SAndroid Build Coastguard Workerand resuming playback, please see the [README][]. 120*30877f79SAndroid Build Coastguard Worker 121*30877f79SAndroid Build Coastguard WorkerThe [demo application][] uses the IMA extension, and includes several sample 122*30877f79SAndroid Build Coastguard WorkerVAST/VMAP ad tags in the sample list. 123*30877f79SAndroid Build Coastguard Worker 124*30877f79SAndroid Build Coastguard Worker#### UI considerations #### 125*30877f79SAndroid Build Coastguard Worker 126*30877f79SAndroid Build Coastguard Worker`StyledPlayerView` and `PlayerView` hide controls during playback of ads 127*30877f79SAndroid Build Coastguard Workerby default, but apps can toggle this behavior by calling 128*30877f79SAndroid Build Coastguard Worker`setControllerHideDuringAds`, which is defined on both views. The IMA SDK will 129*30877f79SAndroid Build Coastguard Workershow additional views on top of the player while an ad is playing (e.g., a 'more 130*30877f79SAndroid Build Coastguard Workerinfo' link and a skip button, if applicable). 131*30877f79SAndroid Build Coastguard Worker 132*30877f79SAndroid Build Coastguard WorkerSince advertisers expect a consistent experience across apps, the IMA SDK does 133*30877f79SAndroid Build Coastguard Workernot allow customization of the views that it shows while an ad is playing. It is 134*30877f79SAndroid Build Coastguard Workertherefore not possible to remove or reposition the skip button, change the 135*30877f79SAndroid Build Coastguard Workerfonts, or make other customizations to the visual appearance of these views. 136*30877f79SAndroid Build Coastguard Worker{:.info} 137*30877f79SAndroid Build Coastguard Worker 138*30877f79SAndroid Build Coastguard WorkerThe IMA SDK may report whether ads are obscured by application provided views 139*30877f79SAndroid Build Coastguard Workerrendered on top of the player. Apps that need to overlay views that are 140*30877f79SAndroid Build Coastguard Workeressential for controlling playback must register them with the IMA SDK so that 141*30877f79SAndroid Build Coastguard Workerthey can be omitted from viewability calculations. When using `StyledPlayerView` 142*30877f79SAndroid Build Coastguard Workeror `PlayerView` as the `AdViewProvider`, they will automatically register 143*30877f79SAndroid Build Coastguard Workertheir control overlays. Apps that use a custom player UI must register overlay 144*30877f79SAndroid Build Coastguard Workerviews by returning them from `AdViewProvider.getAdOverlayInfos`. 145*30877f79SAndroid Build Coastguard Worker 146*30877f79SAndroid Build Coastguard WorkerFor more information about overlay views, see 147*30877f79SAndroid Build Coastguard Worker[Open Measurement in the IMA SDK][]. 148*30877f79SAndroid Build Coastguard Worker 149*30877f79SAndroid Build Coastguard Worker#### Companion ads #### 150*30877f79SAndroid Build Coastguard Worker 151*30877f79SAndroid Build Coastguard WorkerSome ad tags contain additional companion ads that can be shown in 'slots' in an 152*30877f79SAndroid Build Coastguard Workerapp UI. These slots can be passed via 153*30877f79SAndroid Build Coastguard Worker`ImaAdsLoader.Builder.setCompanionAdSlots(slots)`. For more information see 154*30877f79SAndroid Build Coastguard Worker[Adding Companion Ads][]. 155*30877f79SAndroid Build Coastguard Worker 156*30877f79SAndroid Build Coastguard Worker#### Standalone ads #### 157*30877f79SAndroid Build Coastguard Worker 158*30877f79SAndroid Build Coastguard WorkerThe IMA SDK is designed for inserting ads into media content, not for playing 159*30877f79SAndroid Build Coastguard Workerstandalone ads by themselves. Hence playback of standalone ads is not supported 160*30877f79SAndroid Build Coastguard Workerby the IMA extension. We recommend using the [Google Mobile Ads SDK][] instead 161*30877f79SAndroid Build Coastguard Workerfor this use case. 162*30877f79SAndroid Build Coastguard Worker 163*30877f79SAndroid Build Coastguard Worker### Using a third-party ads SDK ### 164*30877f79SAndroid Build Coastguard Worker 165*30877f79SAndroid Build Coastguard WorkerIf you need to load ads via a third-party ads SDK, it's worth checking whether 166*30877f79SAndroid Build Coastguard Workerit already provides an ExoPlayer integration. If not, implementing a custom 167*30877f79SAndroid Build Coastguard Worker`AdsLoader` that wraps the third-party ads SDK is the recommended approach, 168*30877f79SAndroid Build Coastguard Workersince it provides the benefits of `AdsMediaSource` described above. 169*30877f79SAndroid Build Coastguard Worker`ImaAdsLoader` acts as an example implementation. 170*30877f79SAndroid Build Coastguard Worker 171*30877f79SAndroid Build Coastguard WorkerAlternatively, you can use ExoPlayer's [playlist support][] to build a sequence 172*30877f79SAndroid Build Coastguard Workerof ads and content clips: 173*30877f79SAndroid Build Coastguard Worker 174*30877f79SAndroid Build Coastguard Worker~~~ 175*30877f79SAndroid Build Coastguard Worker// A pre-roll ad. 176*30877f79SAndroid Build Coastguard WorkerMediaItem preRollAd = MediaItem.fromUri(preRollAdUri); 177*30877f79SAndroid Build Coastguard Worker// The start of the content. 178*30877f79SAndroid Build Coastguard WorkerMediaItem contentStart = 179*30877f79SAndroid Build Coastguard Worker new MediaItem.Builder() 180*30877f79SAndroid Build Coastguard Worker .setUri(contentUri) 181*30877f79SAndroid Build Coastguard Worker .setClippingConfiguration( 182*30877f79SAndroid Build Coastguard Worker new ClippingConfiguration.Builder() 183*30877f79SAndroid Build Coastguard Worker .setEndPositionMs(120_000) 184*30877f79SAndroid Build Coastguard Worker .build()) 185*30877f79SAndroid Build Coastguard Worker .build(); 186*30877f79SAndroid Build Coastguard Worker// A mid-roll ad. 187*30877f79SAndroid Build Coastguard WorkerMediaItem midRollAd = MediaItem.fromUri(midRollAdUri); 188*30877f79SAndroid Build Coastguard Worker// The rest of the content 189*30877f79SAndroid Build Coastguard WorkerMediaItem contentEnd = 190*30877f79SAndroid Build Coastguard Worker new MediaItem.Builder() 191*30877f79SAndroid Build Coastguard Worker .setUri(contentUri) 192*30877f79SAndroid Build Coastguard Worker .setClippingConfiguration( 193*30877f79SAndroid Build Coastguard Worker new ClippingConfiguration.Builder() 194*30877f79SAndroid Build Coastguard Worker .setStartPositionMs(120_000) 195*30877f79SAndroid Build Coastguard Worker .build()) 196*30877f79SAndroid Build Coastguard Worker .build(); 197*30877f79SAndroid Build Coastguard Worker 198*30877f79SAndroid Build Coastguard Worker// Build the playlist. 199*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(preRollAd); 200*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(contentStart); 201*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(midRollAd); 202*30877f79SAndroid Build Coastguard Workerplayer.addMediaItem(contentEnd); 203*30877f79SAndroid Build Coastguard Worker~~~ 204*30877f79SAndroid Build Coastguard Worker{: .language-java} 205*30877f79SAndroid Build Coastguard Worker 206*30877f79SAndroid Build Coastguard Worker## Server-side ad insertion ## 207*30877f79SAndroid Build Coastguard Worker 208*30877f79SAndroid Build Coastguard WorkerIn server-side ad insertion (also called dynamic ad insertion, or DAI), the 209*30877f79SAndroid Build Coastguard Workermedia stream contains both ads and content. A DASH manifest may point to both 210*30877f79SAndroid Build Coastguard Workercontent and ad segments, possibly in separate periods. For HLS, see the Apple 211*30877f79SAndroid Build Coastguard Workerdocumentation on [incorporating ads into a playlist][]. 212*30877f79SAndroid Build Coastguard Worker 213*30877f79SAndroid Build Coastguard WorkerWhen using server-side ad insertion the client may need to report tracking 214*30877f79SAndroid Build Coastguard Workerevents to an ad SDK or ad server. For example, the media stream may include 215*30877f79SAndroid Build Coastguard Workertimed events that need to be reported by the client (see [supported formats][] 216*30877f79SAndroid Build Coastguard Workerfor information on what timed metadata formats are supported by ExoPlayer). Apps 217*30877f79SAndroid Build Coastguard Workercan listen for timed metadata events from the player, e.g., via 218*30877f79SAndroid Build Coastguard Worker`ExoPlayer.addMetadataOutput`. 219*30877f79SAndroid Build Coastguard Worker 220*30877f79SAndroid Build Coastguard WorkerThe IMA extension currently only handles client-side ad insertion. It does not 221*30877f79SAndroid Build Coastguard Workerprovide any integration with the DAI part of the IMA SDK. 222*30877f79SAndroid Build Coastguard Worker{:.info} 223*30877f79SAndroid Build Coastguard Worker 224*30877f79SAndroid Build Coastguard Worker[VAST]: https://www.iab.com/wp-content/uploads/2015/06/VASTv3_0.pdf 225*30877f79SAndroid Build Coastguard Worker[VMAP]: https://www.iab.com/guidelines/digital-video-multiple-ad-playlist-vmap-1-0-1/ 226*30877f79SAndroid Build Coastguard Worker[ExoPlayer UI components]: {{ site.baseurl }}/ui-components.html 227*30877f79SAndroid Build Coastguard Worker[ExoPlayer IMA extension]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima 228*30877f79SAndroid Build Coastguard Worker[client-side IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android 229*30877f79SAndroid Build Coastguard Worker[README]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/ima 230*30877f79SAndroid Build Coastguard Worker[demo application]: {{ site.baseurl }}/demo-application.html 231*30877f79SAndroid Build Coastguard Worker[Open Measurement in the IMA SDK]: https://developers.google.com/interactive-media-ads/docs/sdks/android/omsdk 232*30877f79SAndroid Build Coastguard Worker[Adding Companion Ads]: https://developers.google.com/interactive-media-ads/docs/sdks/android/companions 233*30877f79SAndroid Build Coastguard Worker[playlist]: {{ site.baseurl }}/playlists.html 234*30877f79SAndroid Build Coastguard Worker[playlist support]: {{ site.baseurl }}/playlists.html 235*30877f79SAndroid Build Coastguard Worker[incorporating ads into a playlist]: https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/incorporating_ads_into_a_playlist 236*30877f79SAndroid Build Coastguard Worker[supported formats]: {{ site.baseurl }}/supported-formats.html 237*30877f79SAndroid Build Coastguard Worker[Google Mobile Ads SDK]: https://developers.google.com/admob/android/quick-start 238