Skip to content

Commit

Permalink
Support non-extractor ads in AdsMediaSource and demo apps
Browse files Browse the repository at this point in the history
Issue: #3302

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178615074
  • Loading branch information
andrewlewis authored and ojw28 committed Dec 12, 2017
1 parent f31696b commit 7a089c3
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 32 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
* Use surfaceless context for secure DummySurface, if available
([#3558](https://github.com/google/ExoPlayer/issues/3558)).
* IMA extension:
* Support non-ExtractorMediaSource ads
([#3302](https://github.com/google/ExoPlayer/issues/3302)).
* Skip ads before the ad preceding the player's initial seek position
([#3527](https://github.com/google/ExoPlayer/issues/3527)).
* Fix ad loading when there is no preroll.
Expand Down
2 changes: 2 additions & 0 deletions demos/ima/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,7 @@ android {
dependencies {
compile project(modulePrefix + 'library-core')
compile project(modulePrefix + 'library-ui')
compile project(modulePrefix + 'library-dash')
compile project(modulePrefix + 'library-hls')
compile project(modulePrefix + 'extension-ima')
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@

import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.ima.ImaAdsLoader;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
import com.google.android.exoplayer2.source.dash.DashMediaSource;
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
Expand All @@ -35,19 +43,27 @@
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;

/**
* Manages the {@link ExoPlayer}, the IMA plugin and all video playback.
*/
/* package */ final class PlayerManager {
/** Manages the {@link ExoPlayer}, the IMA plugin and all video playback. */
/* package */ final class PlayerManager implements AdsMediaSource.MediaSourceFactory {

private final ImaAdsLoader adsLoader;
private final DataSource.Factory manifestDataSourceFactory;
private final DataSource.Factory mediaDataSourceFactory;

private SimpleExoPlayer player;
private long contentPosition;

public PlayerManager(Context context) {
String adTag = context.getString(R.string.ad_tag_url);
adsLoader = new ImaAdsLoader(context, Uri.parse(adTag));
manifestDataSourceFactory =
new DefaultDataSourceFactory(
context, Util.getUserAgent(context, context.getString(R.string.application_name)));
mediaDataSourceFactory =
new DefaultDataSourceFactory(
context,
Util.getUserAgent(context, context.getString(R.string.application_name)),
new DefaultBandwidthMeter());
}

public void init(Context context, SimpleExoPlayerView simpleExoPlayerView) {
Expand All @@ -74,8 +90,14 @@ public void init(Context context, SimpleExoPlayerView simpleExoPlayerView) {
.createMediaSource(Uri.parse(contentUrl));

// Compose the content media source into a new AdsMediaSource with both ads and content.
MediaSource mediaSourceWithAds = new AdsMediaSource(contentMediaSource, dataSourceFactory,
adsLoader, simpleExoPlayerView.getOverlayFrameLayout());
MediaSource mediaSourceWithAds =
new AdsMediaSource(
contentMediaSource,
/* adMediaSourceFactory= */ this,
adsLoader,
simpleExoPlayerView.getOverlayFrameLayout(),
/* eventHandler= */ null,
/* eventListener= */ null);

// Prepare the player with the source.
player.seekTo(contentPosition);
Expand All @@ -99,4 +121,32 @@ public void release() {
adsLoader.release();
}

// AdsMediaSource.MediaSourceFactory implementation.

@Override
public MediaSource createMediaSource(
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
@ContentType int type = Util.inferContentType(uri);
switch (type) {
case C.TYPE_DASH:
return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
manifestDataSourceFactory)
.createMediaSource(uri, handler, listener);
case C.TYPE_HLS:
return new HlsMediaSource.Factory(mediaDataSourceFactory)
.createMediaSource(uri, handler, listener);
case C.TYPE_OTHER:
return new ExtractorMediaSource.Factory(mediaDataSourceFactory)
.createMediaSource(uri, handler, listener);
case C.TYPE_SS:
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}

@Override
public int[] getSupportedTypes() {
return new int[] {C.TYPE_DASH, C.TYPE_HLS, C.TYPE_OTHER};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
Expand Down Expand Up @@ -52,6 +53,7 @@
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdsLoader;
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
Expand Down Expand Up @@ -332,7 +334,7 @@ private void initializePlayer() {
}
MediaSource[] mediaSources = new MediaSource[uris.length];
for (int i = 0; i < uris.length; i++) {
mediaSources[i] = buildMediaSource(uris[i], extensions[i]);
mediaSources[i] = buildMediaSource(uris[i], extensions[i], mainHandler, eventLogger);
}
MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
: new ConcatenatingMediaSource(mediaSources);
Expand Down Expand Up @@ -360,26 +362,30 @@ private void initializePlayer() {
updateButtonVisibilities();
}

private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
private MediaSource buildMediaSource(
Uri uri,
String overrideExtension,
@Nullable Handler handler,
@Nullable MediaSourceEventListener listener) {
@ContentType int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri)
: Util.inferContentType("." + overrideExtension);
switch (type) {
case C.TYPE_SS:
return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false))
.createMediaSource(uri, mainHandler, eventLogger);
case C.TYPE_DASH:
return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false))
.createMediaSource(uri, mainHandler, eventLogger);
.createMediaSource(uri, handler, listener);
case C.TYPE_SS:
return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false))
.createMediaSource(uri, handler, listener);
case C.TYPE_HLS:
return new HlsMediaSource.Factory(mediaDataSourceFactory)
.createMediaSource(uri, mainHandler, eventLogger);
.createMediaSource(uri, handler, listener);
case C.TYPE_OTHER:
return new ExtractorMediaSource.Factory(mediaDataSourceFactory)
.createMediaSource(uri, mainHandler, eventLogger);
.createMediaSource(uri, handler, listener);
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
Expand Down Expand Up @@ -466,8 +472,22 @@ private MediaSource createAdsMediaSource(MediaSource mediaSource, Uri adTagUri)
// The demo app has a non-null overlay frame layout.
simpleExoPlayerView.getOverlayFrameLayout().addView(adUiViewGroup);
}
return new AdsMediaSource(mediaSource, mediaDataSourceFactory, adsLoader, adUiViewGroup,
mainHandler, eventLogger);
AdsMediaSource.MediaSourceFactory adMediaSourceFactory =
new AdsMediaSource.MediaSourceFactory() {
@Override
public MediaSource createMediaSource(
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
return PlayerActivity.this.buildMediaSource(
uri, /* overrideExtension= */ null, handler, listener);
}

@Override
public int[] getSupportedTypes() {
return new int[] {C.TYPE_DASH, C.TYPE_SS, C.TYPE_HLS, C.TYPE_OTHER};
}
};
return new AdsMediaSource(
mediaSource, adMediaSourceFactory, adsLoader, adUiViewGroup, mainHandler, eventLogger);
}

private void releaseAdsLoader() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ public interface EventListener extends MediaSourceEventListener {
private static final String TAG = "AdsMediaSource";

private final MediaSource contentMediaSource;
private final MediaSourceFactory adMediaSourceFactory;
private final AdsLoader adsLoader;
private final ViewGroup adUiViewGroup;
@Nullable private final Handler eventHandler;
@Nullable private final EventListener eventListener;
private final Handler mainHandler;
private final ComponentListener componentListener;
private final MediaSourceFactory adMediaSourceFactory;
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
private final Timeline.Period period;

Expand All @@ -119,28 +119,31 @@ public interface EventListener extends MediaSourceEventListener {
private MediaSource.Listener listener;

/**
* Constructs a new source that inserts ads linearly with the content specified by
* {@code contentMediaSource}.
* <p>
* Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
* non-{@code null} it will be notified of both ad tag and ad media load errors.
* Constructs a new source that inserts ads linearly with the content specified by {@code
* contentMediaSource}. Ad media is loaded using {@link ExtractorMediaSource}.
*
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param dataSourceFactory Factory for data sources used to load ad media.
* @param adsLoader The loader for ads.
* @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
*/
public AdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
AdsLoader adsLoader, ViewGroup adUiViewGroup) {
this(contentMediaSource, dataSourceFactory, adsLoader, adUiViewGroup, null, null);
public AdsMediaSource(
MediaSource contentMediaSource,
DataSource.Factory dataSourceFactory,
AdsLoader adsLoader,
ViewGroup adUiViewGroup) {
this(
contentMediaSource,
dataSourceFactory,
adsLoader,
adUiViewGroup,
/* eventHandler= */ null,
/* eventListener= */ null);
}

/**
* Constructs a new source that inserts ads linearly with the content specified by {@code
* contentMediaSource}.
*
* <p>Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
* non-{@code null} it will be notified of both ad tag and ad media load errors.
* contentMediaSource}. Ad media is loaded using {@link ExtractorMediaSource}.
*
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param dataSourceFactory Factory for data sources used to load ad media.
Expand All @@ -156,14 +159,41 @@ public AdsMediaSource(
ViewGroup adUiViewGroup,
@Nullable Handler eventHandler,
@Nullable EventListener eventListener) {
this(
contentMediaSource,
new ExtractorMediaSource.Factory(dataSourceFactory),
adsLoader,
adUiViewGroup,
eventHandler,
eventListener);
}

/**
* Constructs a new source that inserts ads linearly with the content specified by {@code
* contentMediaSource}.
*
* @param contentMediaSource The {@link MediaSource} providing the content to play.
* @param adMediaSourceFactory Factory for media sources used to load ad media.
* @param adsLoader The loader for ads.
* @param adUiViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
* @param eventHandler A handler for events. May be null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
*/
public AdsMediaSource(
MediaSource contentMediaSource,
MediaSourceFactory adMediaSourceFactory,
AdsLoader adsLoader,
ViewGroup adUiViewGroup,
@Nullable Handler eventHandler,
@Nullable EventListener eventListener) {
this.contentMediaSource = contentMediaSource;
this.adMediaSourceFactory = adMediaSourceFactory;
this.adsLoader = adsLoader;
this.adUiViewGroup = adUiViewGroup;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
mainHandler = new Handler(Looper.getMainLooper());
componentListener = new ComponentListener();
adMediaSourceFactory = new ExtractorMediaSource.Factory(dataSourceFactory);
deferredMediaPeriodByAdMediaSource = new HashMap<>();
period = new Timeline.Period();
adGroupMediaSources = new MediaSource[0][];
Expand Down

0 comments on commit 7a089c3

Please sign in to comment.