DefaultMediaSourceFactory: Lazily load media source factories

Th purpose of this change is to speed up the instantiation of the
DefaultMediaSourceFactory.

PiperOrigin-RevId: 404665352
This commit is contained in:
christosts 2021-10-21 00:31:04 +01:00 committed by Oliver Woodman
parent 140ef753f7
commit bbe2cef740

View File

@ -15,10 +15,11 @@
*/ */
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import static com.google.android.exoplayer2.util.Util.castNonNull; import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.content.Context; import android.content.Context;
import android.util.SparseArray;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
@ -44,14 +45,19 @@ import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** /**
* The default {@link MediaSourceFactory} implementation. * The default {@link MediaSourceFactory} implementation.
@ -111,8 +117,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
private static final String TAG = "DefaultMediaSourceFactory"; private static final String TAG = "DefaultMediaSourceFactory";
private final DataSource.Factory dataSourceFactory; private final DataSource.Factory dataSourceFactory;
private final SparseArray<MediaSourceFactory> mediaSourceFactories; private final DelegateFactoryLoader delegateFactoryLoader;
@C.ContentType private final int[] supportedTypes;
@Nullable private AdsLoaderProvider adsLoaderProvider; @Nullable private AdsLoaderProvider adsLoaderProvider;
@Nullable private AdViewProvider adViewProvider; @Nullable private AdViewProvider adViewProvider;
@ -165,11 +170,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
public DefaultMediaSourceFactory( public DefaultMediaSourceFactory(
DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory) { DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory) {
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
mediaSourceFactories = loadDelegates(dataSourceFactory, extractorsFactory); delegateFactoryLoader = new DelegateFactoryLoader(dataSourceFactory, extractorsFactory);
supportedTypes = new int[mediaSourceFactories.size()];
for (int i = 0; i < mediaSourceFactories.size(); i++) {
supportedTypes[i] = mediaSourceFactories.keyAt(i);
}
liveTargetOffsetMs = C.TIME_UNSET; liveTargetOffsetMs = C.TIME_UNSET;
liveMinOffsetMs = C.TIME_UNSET; liveMinOffsetMs = C.TIME_UNSET;
liveMaxOffsetMs = C.TIME_UNSET; liveMaxOffsetMs = C.TIME_UNSET;
@ -278,41 +279,30 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
return this; return this;
} }
@SuppressWarnings("deprecation") // Calling through to the same deprecated method.
@Override @Override
public DefaultMediaSourceFactory setDrmHttpDataSourceFactory( public DefaultMediaSourceFactory setDrmHttpDataSourceFactory(
@Nullable HttpDataSource.Factory drmHttpDataSourceFactory) { @Nullable HttpDataSource.Factory drmHttpDataSourceFactory) {
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory);
mediaSourceFactories.valueAt(i).setDrmHttpDataSourceFactory(drmHttpDataSourceFactory);
}
return this; return this;
} }
@SuppressWarnings("deprecation") // Calling through to the same deprecated method.
@Override @Override
public DefaultMediaSourceFactory setDrmUserAgent(@Nullable String userAgent) { public DefaultMediaSourceFactory setDrmUserAgent(@Nullable String userAgent) {
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setDrmUserAgent(userAgent);
mediaSourceFactories.valueAt(i).setDrmUserAgent(userAgent);
}
return this; return this;
} }
@SuppressWarnings("deprecation") // Calling through to the same deprecated method.
@Override @Override
public DefaultMediaSourceFactory setDrmSessionManager( public DefaultMediaSourceFactory setDrmSessionManager(
@Nullable DrmSessionManager drmSessionManager) { @Nullable DrmSessionManager drmSessionManager) {
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setDrmSessionManager(drmSessionManager);
mediaSourceFactories.valueAt(i).setDrmSessionManager(drmSessionManager);
}
return this; return this;
} }
@Override @Override
public DefaultMediaSourceFactory setDrmSessionManagerProvider( public DefaultMediaSourceFactory setDrmSessionManagerProvider(
@Nullable DrmSessionManagerProvider drmSessionManagerProvider) { @Nullable DrmSessionManagerProvider drmSessionManagerProvider) {
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setDrmSessionManagerProvider(drmSessionManagerProvider);
mediaSourceFactories.valueAt(i).setDrmSessionManagerProvider(drmSessionManagerProvider);
}
return this; return this;
} }
@ -320,9 +310,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
public DefaultMediaSourceFactory setLoadErrorHandlingPolicy( public DefaultMediaSourceFactory setLoadErrorHandlingPolicy(
@Nullable LoadErrorHandlingPolicy loadErrorHandlingPolicy) { @Nullable LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy);
mediaSourceFactories.valueAt(i).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy);
}
return this; return this;
} }
@ -334,26 +322,25 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
@Deprecated @Deprecated
@Override @Override
public DefaultMediaSourceFactory setStreamKeys(@Nullable List<StreamKey> streamKeys) { public DefaultMediaSourceFactory setStreamKeys(@Nullable List<StreamKey> streamKeys) {
for (int i = 0; i < mediaSourceFactories.size(); i++) { delegateFactoryLoader.setStreamKeys(streamKeys);
mediaSourceFactories.valueAt(i).setStreamKeys(streamKeys);
}
return this; return this;
} }
@Override @Override
public int[] getSupportedTypes() { public int[] getSupportedTypes() {
return Arrays.copyOf(supportedTypes, supportedTypes.length); return delegateFactoryLoader.getSupportedTypes();
} }
@Override @Override
public MediaSource createMediaSource(MediaItem mediaItem) { public MediaSource createMediaSource(MediaItem mediaItem) {
Assertions.checkNotNull(mediaItem.localConfiguration); checkNotNull(mediaItem.localConfiguration);
@C.ContentType @C.ContentType
int type = int type =
Util.inferContentTypeForUriAndMimeType( Util.inferContentTypeForUriAndMimeType(
mediaItem.localConfiguration.uri, mediaItem.localConfiguration.mimeType); mediaItem.localConfiguration.uri, mediaItem.localConfiguration.mimeType);
@Nullable MediaSourceFactory mediaSourceFactory = mediaSourceFactories.get(type); @Nullable
Assertions.checkNotNull( MediaSourceFactory mediaSourceFactory = delegateFactoryLoader.getMediaSourceFactory(type);
checkStateNotNull(
mediaSourceFactory, "No suitable media source factory found for content type: " + type); mediaSourceFactory, "No suitable media source factory found for content type: " + type);
MediaItem.LiveConfiguration.Builder liveConfigurationBuilder = MediaItem.LiveConfiguration.Builder liveConfigurationBuilder =
@ -433,22 +420,22 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
} }
return new ClippingMediaSource( return new ClippingMediaSource(
mediaSource, mediaSource,
C.msToUs(mediaItem.clippingConfiguration.startPositionMs), Util.msToUs(mediaItem.clippingConfiguration.startPositionMs),
C.msToUs(mediaItem.clippingConfiguration.endPositionMs), Util.msToUs(mediaItem.clippingConfiguration.endPositionMs),
/* enableInitialDiscontinuity= */ !mediaItem.clippingConfiguration.startsAtKeyFrame, /* enableInitialDiscontinuity= */ !mediaItem.clippingConfiguration.startsAtKeyFrame,
/* allowDynamicClippingUpdates= */ mediaItem.clippingConfiguration.relativeToLiveWindow, /* allowDynamicClippingUpdates= */ mediaItem.clippingConfiguration.relativeToLiveWindow,
mediaItem.clippingConfiguration.relativeToDefaultPosition); mediaItem.clippingConfiguration.relativeToDefaultPosition);
} }
private MediaSource maybeWrapWithAdsMediaSource(MediaItem mediaItem, MediaSource mediaSource) { private MediaSource maybeWrapWithAdsMediaSource(MediaItem mediaItem, MediaSource mediaSource) {
Assertions.checkNotNull(mediaItem.localConfiguration); checkNotNull(mediaItem.localConfiguration);
@Nullable @Nullable
MediaItem.AdsConfiguration adsConfiguration = mediaItem.localConfiguration.adsConfiguration; MediaItem.AdsConfiguration adsConfiguration = mediaItem.localConfiguration.adsConfiguration;
if (adsConfiguration == null) { if (adsConfiguration == null) {
return mediaSource; return mediaSource;
} }
AdsLoaderProvider adsLoaderProvider = this.adsLoaderProvider; @Nullable AdsLoaderProvider adsLoaderProvider = this.adsLoaderProvider;
AdViewProvider adViewProvider = this.adViewProvider; @Nullable AdViewProvider adViewProvider = this.adViewProvider;
if (adsLoaderProvider == null || adViewProvider == null) { if (adsLoaderProvider == null || adViewProvider == null) {
Log.w( Log.w(
TAG, TAG,
@ -473,51 +460,181 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
adViewProvider); adViewProvider);
} }
private static SparseArray<MediaSourceFactory> loadDelegates( /** Loads media source factories lazily. */
private static final class DelegateFactoryLoader {
private final DataSource.Factory dataSourceFactory;
private final ExtractorsFactory extractorsFactory;
private final Map<Integer, @NullableType Supplier<MediaSourceFactory>>
mediaSourceFactorySuppliers;
private final Set<Integer> supportedTypes;
private final Map<Integer, MediaSourceFactory> mediaSourceFactories;
@Nullable private HttpDataSource.Factory drmHttpDataSourceFactory;
@Nullable private String userAgent;
@Nullable private DrmSessionManager drmSessionManager;
@Nullable private DrmSessionManagerProvider drmSessionManagerProvider;
@Nullable private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
@Nullable private List<StreamKey> streamKeys;
public DelegateFactoryLoader(
DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory) { DataSource.Factory dataSourceFactory, ExtractorsFactory extractorsFactory) {
SparseArray<MediaSourceFactory> factories = new SparseArray<>(); this.dataSourceFactory = dataSourceFactory;
this.extractorsFactory = extractorsFactory;
mediaSourceFactorySuppliers = new HashMap<>();
supportedTypes = new HashSet<>();
mediaSourceFactories = new HashMap<>();
}
@C.ContentType
public int[] getSupportedTypes() {
ensureAllSuppliersAreLoaded();
return Ints.toArray(supportedTypes);
}
@SuppressWarnings("deprecation") // Forwarding to deprecated methods.
@Nullable
public MediaSourceFactory getMediaSourceFactory(@C.ContentType int contentType) {
@Nullable MediaSourceFactory mediaSourceFactory = mediaSourceFactories.get(contentType);
if (mediaSourceFactory != null) {
return mediaSourceFactory;
}
@Nullable
Supplier<MediaSourceFactory> mediaSourceFactorySupplier = maybeLoadSupplier(contentType);
if (mediaSourceFactorySupplier == null) {
return null;
}
mediaSourceFactory = mediaSourceFactorySupplier.get();
if (drmHttpDataSourceFactory != null) {
mediaSourceFactory.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory);
}
if (userAgent != null) {
mediaSourceFactory.setDrmUserAgent(userAgent);
}
if (drmSessionManager != null) {
mediaSourceFactory.setDrmSessionManager(drmSessionManager);
}
if (drmSessionManagerProvider != null) {
mediaSourceFactory.setDrmSessionManagerProvider(drmSessionManagerProvider);
}
if (loadErrorHandlingPolicy != null) {
mediaSourceFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy);
}
if (streamKeys != null) {
mediaSourceFactory.setStreamKeys(streamKeys);
}
mediaSourceFactories.put(contentType, mediaSourceFactory);
return mediaSourceFactory;
}
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
public void setDrmHttpDataSourceFactory(
@Nullable HttpDataSource.Factory drmHttpDataSourceFactory) {
this.drmHttpDataSourceFactory = drmHttpDataSourceFactory;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setDrmHttpDataSourceFactory(drmHttpDataSourceFactory);
}
}
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
public void setDrmUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setDrmUserAgent(userAgent);
}
}
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
public void setDrmSessionManager(@Nullable DrmSessionManager drmSessionManager) {
this.drmSessionManager = drmSessionManager;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setDrmSessionManager(drmSessionManager);
}
}
public void setDrmSessionManagerProvider(
@Nullable DrmSessionManagerProvider drmSessionManagerProvider) {
this.drmSessionManagerProvider = drmSessionManagerProvider;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setDrmSessionManagerProvider(drmSessionManagerProvider);
}
}
public void setLoadErrorHandlingPolicy(
@Nullable LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setLoadErrorHandlingPolicy(loadErrorHandlingPolicy);
}
}
@SuppressWarnings("deprecation") // Forwarding to deprecated method.
public void setStreamKeys(@Nullable List<StreamKey> streamKeys) {
this.streamKeys = streamKeys;
for (MediaSourceFactory mediaSourceFactory : mediaSourceFactories.values()) {
mediaSourceFactory.setStreamKeys(streamKeys);
}
}
private void ensureAllSuppliersAreLoaded() {
maybeLoadSupplier(C.TYPE_DASH);
maybeLoadSupplier(C.TYPE_SS);
maybeLoadSupplier(C.TYPE_HLS);
maybeLoadSupplier(C.TYPE_RTSP);
maybeLoadSupplier(C.TYPE_OTHER);
}
@Nullable
private Supplier<MediaSourceFactory> maybeLoadSupplier(@C.ContentType int contentType) {
if (mediaSourceFactorySuppliers.containsKey(contentType)) {
return mediaSourceFactorySuppliers.get(contentType);
}
@Nullable Supplier<MediaSourceFactory> mediaSourceFactorySupplier = null;
try { try {
Class<? extends MediaSourceFactory> factoryClazz = Class<? extends MediaSourceFactory> clazz;
switch (contentType) {
case C.TYPE_DASH:
clazz =
Class.forName("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory") Class.forName("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory")
.asSubclass(MediaSourceFactory.class); .asSubclass(MediaSourceFactory.class);
factories.put( mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
C.TYPE_DASH, break;
factoryClazz.getConstructor(DataSource.Factory.class).newInstance(dataSourceFactory)); case C.TYPE_SS:
} catch (Exception e) { clazz =
// Expected if the app was built without the dash module.
}
try {
Class<? extends MediaSourceFactory> factoryClazz =
Class.forName( Class.forName(
"com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory") "com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory")
.asSubclass(MediaSourceFactory.class); .asSubclass(MediaSourceFactory.class);
factories.put( mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
C.TYPE_SS, break;
factoryClazz.getConstructor(DataSource.Factory.class).newInstance(dataSourceFactory)); case C.TYPE_HLS:
} catch (Exception e) { clazz =
// Expected if the app was built without the smoothstreaming module.
}
try {
Class<? extends MediaSourceFactory> factoryClazz =
Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory") Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory")
.asSubclass(MediaSourceFactory.class); .asSubclass(MediaSourceFactory.class);
factories.put( mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
C.TYPE_HLS, break;
factoryClazz.getConstructor(DataSource.Factory.class).newInstance(dataSourceFactory)); case C.TYPE_RTSP:
} catch (Exception e) { clazz =
// Expected if the app was built without the hls module.
}
try {
Class<? extends MediaSourceFactory> factoryClazz =
Class.forName("com.google.android.exoplayer2.source.rtsp.RtspMediaSource$Factory") Class.forName("com.google.android.exoplayer2.source.rtsp.RtspMediaSource$Factory")
.asSubclass(MediaSourceFactory.class); .asSubclass(MediaSourceFactory.class);
factories.put(C.TYPE_RTSP, factoryClazz.getConstructor().newInstance()); mediaSourceFactorySupplier = () -> newInstance(clazz);
} catch (Exception e) { break;
// Expected if the app was built without the RTSP module. case C.TYPE_OTHER:
mediaSourceFactorySupplier =
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
break;
default:
// Do nothing.
}
} catch (ClassNotFoundException e) {
// Expected if the app was built without the specific module.
}
mediaSourceFactorySuppliers.put(contentType, mediaSourceFactorySupplier);
if (mediaSourceFactorySupplier != null) {
supportedTypes.add(contentType);
}
return mediaSourceFactorySupplier;
} }
factories.put(
C.TYPE_OTHER, new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory));
return factories;
} }
private static final class UnknownSubtitlesExtractor implements Extractor { private static final class UnknownSubtitlesExtractor implements Extractor {
@ -555,11 +672,26 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
} }
@Override @Override
public void seek(long position, long timeUs) { public void seek(long position, long timeUs) {}
return;
}
@Override @Override
public void release() {} public void release() {}
} }
private static MediaSourceFactory newInstance(
Class<? extends MediaSourceFactory> clazz, DataSource.Factory dataSourceFactory) {
try {
return clazz.getConstructor(DataSource.Factory.class).newInstance(dataSourceFactory);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private static MediaSourceFactory newInstance(Class<? extends MediaSourceFactory> clazz) {
try {
return clazz.getConstructor().newInstance();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
} }