Integrate IMA SSAI samples in main demo app

PiperOrigin-RevId: 429059793
This commit is contained in:
bachinger 2022-02-16 16:55:33 +00:00 committed by Ian Baker
parent 0842295a88
commit 5ee9c48244
3 changed files with 148 additions and 30 deletions

View File

@ -76,6 +76,7 @@
<data android:scheme="content"/> <data android:scheme="content"/>
<data android:scheme="asset"/> <data android:scheme="asset"/>
<data android:scheme="file"/> <data android:scheme="file"/>
<data android:scheme="ssai"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="androidx.media3.demo.main.action.VIEW_LIST"/> <action android:name="androidx.media3.demo.main.action.VIEW_LIST"/>

View File

@ -387,6 +387,98 @@
} }
] ]
}, },
{
"name": "IMA DAI streams",
"samples": [
{
"name": "HLS VOD: Demo (skippable pre/post), single ads [30 s]",
"uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1"
},
{
"name": "HLS VOD: Tears of Steel (pre/mid/mid/mid/post), single ads [10s]",
"uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1"
},
{
"name": "HLS Live: Big Buck Bunny (mid), 3 ads each [10 s]",
"uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3"
},
{
"name": "DASH VOD: Tears of Steel (11 periods, pre/mid/post), 2/5/2 ads [5/10s]",
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"name": "Playlist: No ads - HLS VOD: Demo (skippable pre/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2483977&videoId=ima-vod-skippable-test&format=2&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - HLS VOD: Tears of steel (pre/mid/mid/mid/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2528370&videoId=tears-of-steel&format=2&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - HLS Live: Big Buck Bunny (mid) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads",
"playlist": [
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
},
{
"name": "Playlist: Client-side Ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads",
"playlist": [
{
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator="
},
{
"uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1"
},
{
"uri": "https://html5demos.com/assets/dizzy.mp4"
}
]
}
]
},
{ {
"name": "Playlists", "name": "Playlists",
"samples": [ "samples": [

View File

@ -15,8 +15,6 @@
*/ */
package androidx.media3.demo.main; package androidx.media3.demo.main;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
@ -43,6 +41,7 @@ import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.RenderersFactory; import androidx.media3.exoplayer.RenderersFactory;
import androidx.media3.exoplayer.drm.FrameworkMediaDrm; import androidx.media3.exoplayer.drm.FrameworkMediaDrm;
import androidx.media3.exoplayer.ima.ImaAdsLoader; import androidx.media3.exoplayer.ima.ImaAdsLoader;
import androidx.media3.exoplayer.ima.ImaServerSideAdInsertionMediaSource;
import androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.DecoderInitializationException; import androidx.media3.exoplayer.mediacodec.MediaCodecRenderer.DecoderInitializationException;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil.DecoderQueryException;
import androidx.media3.exoplayer.offline.DownloadRequest; import androidx.media3.exoplayer.offline.DownloadRequest;
@ -57,6 +56,7 @@ import androidx.media3.ui.PlayerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** An activity that plays media using {@link ExoPlayer}. */ /** An activity that plays media using {@link ExoPlayer}. */
public class PlayerActivity extends AppCompatActivity public class PlayerActivity extends AppCompatActivity
@ -65,6 +65,7 @@ public class PlayerActivity extends AppCompatActivity
// Saved instance state keys. // Saved instance state keys.
private static final String KEY_TRACK_SELECTION_PARAMETERS = "track_selection_parameters"; private static final String KEY_TRACK_SELECTION_PARAMETERS = "track_selection_parameters";
private static final String KEY_SERVER_SIDE_ADS_LOADER_STATE = "server_side_ads_loader_state";
private static final String KEY_ITEM_INDEX = "item_index"; private static final String KEY_ITEM_INDEX = "item_index";
private static final String KEY_POSITION = "position"; private static final String KEY_POSITION = "position";
private static final String KEY_AUTO_PLAY = "auto_play"; private static final String KEY_AUTO_PLAY = "auto_play";
@ -88,7 +89,10 @@ public class PlayerActivity extends AppCompatActivity
// For ad playback only. // For ad playback only.
private AdsLoader adsLoader; @Nullable private AdsLoader clientSideAdsLoader;
@Nullable private ImaServerSideAdInsertionMediaSource.AdsLoader serverSideAdsLoader;
private ImaServerSideAdInsertionMediaSource.AdsLoader.@MonotonicNonNull State
serverSideAdsLoaderState;
// Activity lifecycle. // Activity lifecycle.
@ -116,6 +120,12 @@ public class PlayerActivity extends AppCompatActivity
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY); startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX); startItemIndex = savedInstanceState.getInt(KEY_ITEM_INDEX);
startPosition = savedInstanceState.getLong(KEY_POSITION); startPosition = savedInstanceState.getLong(KEY_POSITION);
Bundle adsLoaderStateBundle = savedInstanceState.getBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE);
if (adsLoaderStateBundle != null) {
serverSideAdsLoaderState =
ImaServerSideAdInsertionMediaSource.AdsLoader.State.CREATOR.fromBundle(
adsLoaderStateBundle);
}
} else { } else {
trackSelectionParameters = trackSelectionParameters =
new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build(); new DefaultTrackSelector.ParametersBuilder(/* context= */ this).build();
@ -127,7 +137,7 @@ public class PlayerActivity extends AppCompatActivity
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
releasePlayer(); releasePlayer();
releaseAdsLoader(); releaseClientSideAdsLoader();
clearStartPosition(); clearStartPosition();
setIntent(intent); setIntent(intent);
} }
@ -179,7 +189,7 @@ public class PlayerActivity extends AppCompatActivity
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
releaseAdsLoader(); releaseClientSideAdsLoader();
} }
@Override @Override
@ -208,6 +218,9 @@ public class PlayerActivity extends AppCompatActivity
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay); outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_ITEM_INDEX, startItemIndex); outState.putInt(KEY_ITEM_INDEX, startItemIndex);
outState.putLong(KEY_POSITION, startPosition); outState.putLong(KEY_POSITION, startPosition);
if (serverSideAdsLoaderState != null) {
outState.putBundle(KEY_SERVER_SIDE_ADS_LOADER_STATE, serverSideAdsLoaderState.toBundle());
}
} }
// Activity input // Activity input
@ -261,17 +274,13 @@ public class PlayerActivity extends AppCompatActivity
intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false); intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false);
RenderersFactory renderersFactory = RenderersFactory renderersFactory =
DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders); DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders);
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(dataSourceFactory)
.setAdsLoaderProvider(this::getAdsLoader)
.setAdViewProvider(playerView);
trackSelector = new DefaultTrackSelector(/* context= */ this); trackSelector = new DefaultTrackSelector(/* context= */ this);
lastSeenTracksInfo = TracksInfo.EMPTY; lastSeenTracksInfo = TracksInfo.EMPTY;
player = player =
new ExoPlayer.Builder(/* context= */ this) new ExoPlayer.Builder(/* context= */ this)
.setRenderersFactory(renderersFactory) .setRenderersFactory(renderersFactory)
.setMediaSourceFactory(mediaSourceFactory) .setMediaSourceFactory(createMediaSourceFactory())
.setTrackSelector(trackSelector) .setTrackSelector(trackSelector)
.build(); .build();
player.setTrackSelectionParameters(trackSelectionParameters); player.setTrackSelectionParameters(trackSelectionParameters);
@ -280,6 +289,7 @@ public class PlayerActivity extends AppCompatActivity
player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true); player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
player.setPlayWhenReady(startAutoPlay); player.setPlayWhenReady(startAutoPlay);
playerView.setPlayer(player); playerView.setPlayer(player);
serverSideAdsLoader.setPlayer(player);
debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper = new DebugTextViewHelper(player, debugTextView);
debugViewHelper.start(); debugViewHelper.start();
} }
@ -293,6 +303,22 @@ public class PlayerActivity extends AppCompatActivity
return true; return true;
} }
private MediaSource.Factory createMediaSourceFactory() {
ImaServerSideAdInsertionMediaSource.AdsLoader.Builder serverSideAdLoaderBuilder =
new ImaServerSideAdInsertionMediaSource.AdsLoader.Builder(/* context= */ this, playerView);
if (serverSideAdsLoaderState != null) {
serverSideAdLoaderBuilder.setAdsLoaderState(serverSideAdsLoaderState);
}
serverSideAdsLoader = serverSideAdLoaderBuilder.build();
ImaServerSideAdInsertionMediaSource.Factory imaServerSideAdInsertionMediaSourceFactory =
new ImaServerSideAdInsertionMediaSource.Factory(
serverSideAdsLoader, new DefaultMediaSourceFactory(dataSourceFactory));
return new DefaultMediaSourceFactory(dataSourceFactory)
.setAdsLoaderProvider(this::getClientSideAdsLoader)
.setAdViewProvider(playerView)
.setServerSideAdInsertionMediaSourceFactory(imaServerSideAdInsertionMediaSourceFactory);
}
private List<MediaItem> createMediaItems(Intent intent) { private List<MediaItem> createMediaItems(Intent intent) {
String action = intent.getAction(); String action = intent.getAction();
boolean actionIsListView = IntentUtil.ACTION_VIEW_LIST.equals(action); boolean actionIsListView = IntentUtil.ACTION_VIEW_LIST.equals(action);
@ -304,7 +330,6 @@ public class PlayerActivity extends AppCompatActivity
List<MediaItem> mediaItems = List<MediaItem> mediaItems =
createMediaItems(intent, DemoUtil.getDownloadTracker(/* context= */ this)); createMediaItems(intent, DemoUtil.getDownloadTracker(/* context= */ this));
boolean hasAds = false;
for (int i = 0; i < mediaItems.size(); i++) { for (int i = 0; i < mediaItems.size(); i++) {
MediaItem mediaItem = mediaItems.get(i); MediaItem mediaItem = mediaItems.get(i);
@ -318,8 +343,7 @@ public class PlayerActivity extends AppCompatActivity
return Collections.emptyList(); return Collections.emptyList();
} }
MediaItem.DrmConfiguration drmConfiguration = MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
checkNotNull(mediaItem.localConfiguration).drmConfiguration;
if (drmConfiguration != null) { if (drmConfiguration != null) {
if (Util.SDK_INT < 18) { if (Util.SDK_INT < 18) {
showToast(R.string.error_drm_unsupported_before_api_18); showToast(R.string.error_drm_unsupported_before_api_18);
@ -331,43 +355,44 @@ public class PlayerActivity extends AppCompatActivity
return Collections.emptyList(); return Collections.emptyList();
} }
} }
hasAds |= mediaItem.localConfiguration.adsConfiguration != null;
}
if (!hasAds) {
releaseAdsLoader();
} }
return mediaItems; return mediaItems;
} }
private AdsLoader getAdsLoader(MediaItem.AdsConfiguration adsConfiguration) { private AdsLoader getClientSideAdsLoader(MediaItem.AdsConfiguration adsConfiguration) {
// The ads loader is reused for multiple playbacks, so that ad playback can resume. // The ads loader is reused for multiple playbacks, so that ad playback can resume.
if (adsLoader == null) { if (clientSideAdsLoader == null) {
adsLoader = new ImaAdsLoader.Builder(/* context= */ this).build(); clientSideAdsLoader = new ImaAdsLoader.Builder(/* context= */ this).build();
} }
adsLoader.setPlayer(player); clientSideAdsLoader.setPlayer(player);
return adsLoader; return clientSideAdsLoader;
} }
protected void releasePlayer() { protected void releasePlayer() {
if (player != null) { if (player != null) {
updateTrackSelectorParameters(); updateTrackSelectorParameters();
updateStartPosition(); updateStartPosition();
serverSideAdsLoaderState = serverSideAdsLoader.release();
serverSideAdsLoader = null;
debugViewHelper.stop(); debugViewHelper.stop();
debugViewHelper = null; debugViewHelper = null;
player.release(); player.release();
player = null; player = null;
playerView.setPlayer(/* player= */ null);
mediaItems = Collections.emptyList(); mediaItems = Collections.emptyList();
} }
if (adsLoader != null) { if (clientSideAdsLoader != null) {
adsLoader.setPlayer(null); clientSideAdsLoader.setPlayer(null);
} else {
playerView.getAdViewGroup().removeAllViews();
} }
} }
private void releaseAdsLoader() { private void releaseClientSideAdsLoader() {
if (adsLoader != null) { if (clientSideAdsLoader != null) {
adsLoader.release(); clientSideAdsLoader.release();
adsLoader = null; clientSideAdsLoader = null;
playerView.getOverlayFrameLayout().removeAllViews(); playerView.getAdViewGroup().removeAllViews();
} }
} }
@ -490,7 +515,7 @@ public class PlayerActivity extends AppCompatActivity
for (MediaItem item : IntentUtil.createMediaItemsFromIntent(intent)) { for (MediaItem item : IntentUtil.createMediaItemsFromIntent(intent)) {
@Nullable @Nullable
DownloadRequest downloadRequest = DownloadRequest downloadRequest =
downloadTracker.getDownloadRequest(checkNotNull(item.localConfiguration).uri); downloadTracker.getDownloadRequest(item.localConfiguration.uri);
if (downloadRequest != null) { if (downloadRequest != null) {
MediaItem.Builder builder = item.buildUpon(); MediaItem.Builder builder = item.buildUpon();
builder builder