mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add AdsMediaSourceFactory for HLS interstitials
PiperOrigin-RevId: 707109323
This commit is contained in:
parent
acc41cb5f7
commit
3dede2415d
@ -101,6 +101,13 @@
|
|||||||
* Cronet Extension:
|
* Cronet Extension:
|
||||||
* RTMP Extension:
|
* RTMP Extension:
|
||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
|
* Add a first version of `HlsInterstitialsAdsLoader`. The ads loader reads
|
||||||
|
the HLS interstitials of an HLS media playlist and maps them to the
|
||||||
|
`AdPlaybackState` that is passed to the `AdsMediaSource`. This initial
|
||||||
|
version only supports HLS VOD streams with `X-ASSET-URI` attributes.
|
||||||
|
* Add `HlsInterstitialsAdsLoader.AdsMediaSourceFactory`. Apps can use it
|
||||||
|
to create `AdsMediaSource` instances that use an
|
||||||
|
`HlsInterstitialsAdsLoader` in a convenient and safe way.
|
||||||
* DASH Extension:
|
* DASH Extension:
|
||||||
* Add AC-4 Level-4 format support for DASH
|
* Add AC-4 Level-4 format support for DASH
|
||||||
([#1898](https://github.com/androidx/media/pull/1898)).
|
([#1898](https://github.com/androidx/media/pull/1898)).
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
package androidx.media3.exoplayer.hls;
|
package androidx.media3.exoplayer.hls;
|
||||||
|
|
||||||
import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION;
|
import static androidx.media3.common.Player.DISCONTINUITY_REASON_AUTO_TRANSITION;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.AdPlaybackState;
|
import androidx.media3.common.AdPlaybackState;
|
||||||
import androidx.media3.common.AdViewProvider;
|
import androidx.media3.common.AdViewProvider;
|
||||||
@ -38,10 +40,15 @@ import androidx.media3.common.util.Log;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.DataSpec;
|
import androidx.media3.datasource.DataSpec;
|
||||||
|
import androidx.media3.datasource.DefaultDataSource;
|
||||||
|
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
||||||
import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist;
|
import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist;
|
||||||
import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.Interstitial;
|
import androidx.media3.exoplayer.hls.playlist.HlsMediaPlaylist.Interstitial;
|
||||||
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.ads.AdsLoader;
|
import androidx.media3.exoplayer.source.ads.AdsLoader;
|
||||||
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
||||||
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -64,6 +71,110 @@ import java.util.Set;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class HlsInterstitialsAdsLoader implements AdsLoader {
|
public final class HlsInterstitialsAdsLoader implements AdsLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link MediaSource.Factory} to create a media source to play HLS streams with interstitials.
|
||||||
|
*/
|
||||||
|
public static final class AdsMediaSourceFactory implements MediaSource.Factory {
|
||||||
|
|
||||||
|
private final MediaSource.Factory mediaSourceFactory;
|
||||||
|
private final AdViewProvider adViewProvider;
|
||||||
|
private final HlsInterstitialsAdsLoader adsLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with a {@link
|
||||||
|
* androidx.media3.exoplayer.source.DefaultMediaSourceFactory}.
|
||||||
|
*
|
||||||
|
* @param adsLoader The {@link HlsInterstitialsAdsLoader}.
|
||||||
|
* @param adViewProvider Provider of views for the ad UI.
|
||||||
|
* @param context The {@link Context}.
|
||||||
|
*/
|
||||||
|
public AdsMediaSourceFactory(
|
||||||
|
HlsInterstitialsAdsLoader adsLoader, AdViewProvider adViewProvider, Context context) {
|
||||||
|
this(adsLoader, context, /* mediaSourceFactory= */ null, adViewProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance with a custom {@link MediaSource.Factory}.
|
||||||
|
*
|
||||||
|
* @param adsLoader The {@link HlsInterstitialsAdsLoader}.
|
||||||
|
* @param adViewProvider Provider of views for the ad UI.
|
||||||
|
* @param mediaSourceFactory The {@link MediaSource.Factory} used to create content and ad media
|
||||||
|
* sources.
|
||||||
|
* @throws IllegalStateException If the provided {@linkplain MediaSource.Factory media source
|
||||||
|
* factory} doesn't support content type {@link C#CONTENT_TYPE_HLS}.
|
||||||
|
*/
|
||||||
|
public AdsMediaSourceFactory(
|
||||||
|
HlsInterstitialsAdsLoader adsLoader,
|
||||||
|
AdViewProvider adViewProvider,
|
||||||
|
MediaSource.Factory mediaSourceFactory) {
|
||||||
|
this(adsLoader, /* context= */ null, mediaSourceFactory, adViewProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AdsMediaSourceFactory(
|
||||||
|
HlsInterstitialsAdsLoader adsLoader,
|
||||||
|
@Nullable Context context,
|
||||||
|
@Nullable MediaSource.Factory mediaSourceFactory,
|
||||||
|
AdViewProvider adViewProvider) {
|
||||||
|
checkArgument(context != null || mediaSourceFactory != null);
|
||||||
|
this.adsLoader = adsLoader;
|
||||||
|
this.mediaSourceFactory =
|
||||||
|
mediaSourceFactory != null
|
||||||
|
? mediaSourceFactory
|
||||||
|
: new HlsMediaSource.Factory(new DefaultDataSource.Factory(checkNotNull(context)));
|
||||||
|
this.adViewProvider = adViewProvider;
|
||||||
|
boolean supportsHls = false;
|
||||||
|
for (int supportedType : this.mediaSourceFactory.getSupportedTypes()) {
|
||||||
|
if (supportedType == C.CONTENT_TYPE_HLS) {
|
||||||
|
supportsHls = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkState(supportsHls);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @C.ContentType int[] getSupportedTypes() {
|
||||||
|
return new int[] {C.CONTENT_TYPE_HLS};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public AdsMediaSourceFactory setDrmSessionManagerProvider(
|
||||||
|
DrmSessionManagerProvider drmSessionManagerProvider) {
|
||||||
|
mediaSourceFactory.setDrmSessionManagerProvider(drmSessionManagerProvider);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public AdsMediaSourceFactory setLoadErrorHandlingPolicy(
|
||||||
|
LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
|
||||||
|
mediaSourceFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaSource createMediaSource(MediaItem mediaItem) {
|
||||||
|
checkNotNull(mediaItem.localConfiguration);
|
||||||
|
MediaSource contentMediaSource = mediaSourceFactory.createMediaSource(mediaItem);
|
||||||
|
if (mediaItem.localConfiguration.adsConfiguration == null) {
|
||||||
|
return contentMediaSource;
|
||||||
|
} else if (!(mediaItem.localConfiguration.adsConfiguration.adsId instanceof String)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Please use an AdsConfiguration with an adsId of type String when using"
|
||||||
|
+ " HlsInterstitialsAdsLoader");
|
||||||
|
}
|
||||||
|
return new AdsMediaSource(
|
||||||
|
contentMediaSource,
|
||||||
|
new DataSpec(mediaItem.localConfiguration.adsConfiguration.adTagUri), // unused
|
||||||
|
checkNotNull(mediaItem.localConfiguration.adsConfiguration.adsId),
|
||||||
|
mediaSourceFactory,
|
||||||
|
adsLoader,
|
||||||
|
adViewProvider,
|
||||||
|
/* useLazyContentSourcePreparation= */ false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** A listener to be notified of events emitted by the ads loader. */
|
/** A listener to be notified of events emitted by the ads loader. */
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
|
|
||||||
|
@ -90,18 +90,15 @@ public class HlsInterstitialsAdsLoaderTest {
|
|||||||
.setAdsConfiguration(
|
.setAdsConfiguration(
|
||||||
new MediaItem.AdsConfiguration.Builder(Uri.EMPTY).setAdsId("adsId").build())
|
new MediaItem.AdsConfiguration.Builder(Uri.EMPTY).setAdsId("adsId").build())
|
||||||
.build();
|
.build();
|
||||||
adTagDataSpec = new DataSpec(contentMediaItem.localConfiguration.adsConfiguration.adTagUri);
|
adTagDataSpec = new DataSpec(Uri.EMPTY);
|
||||||
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
|
||||||
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
|
|
||||||
// The ads media source using the ads loader.
|
// The ads media source using the ads loader.
|
||||||
adsMediaSource =
|
adsMediaSource =
|
||||||
new AdsMediaSource(
|
(AdsMediaSource)
|
||||||
defaultMediaSourceFactory.createMediaSource(contentMediaItem),
|
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
|
||||||
new DataSpec(Uri.EMPTY),
|
adsLoader,
|
||||||
"adsId",
|
mockAdViewProvider,
|
||||||
defaultMediaSourceFactory,
|
(Context) ApplicationProvider.getApplicationContext())
|
||||||
adsLoader,
|
.createMediaSource(contentMediaItem);
|
||||||
mockAdViewProvider);
|
|
||||||
// The content timeline with empty ad playback state.
|
// The content timeline with empty ad playback state.
|
||||||
contentWindowDefinition =
|
contentWindowDefinition =
|
||||||
new FakeTimeline.TimelineWindowDefinition(
|
new FakeTimeline.TimelineWindowDefinition(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user