Commit adc50515 by tonihei Committed by Rohit Singh

Fix setDataSourceFactory handling in DefaultMediaSourceFactory

The call doesn't currently reset the already loaded suppliers and
factories. Also fix the supplier loading code to use a local copy
of the current dataSourceFactory to avoid leaking an updated
instance to a later invocation.

Issue: androidx/media#116

#minor-release

PiperOrigin-RevId: 460721541
parent 9a616c0c
......@@ -17,6 +17,9 @@
* Use `SingleThreadExecutor` for releasing `AudioTrack` instances to avoid
OutOfMemory errors when releasing multiple players at the same time
([#10057](https://github.com/google/ExoPlayer/issues/10057)).
* Fix implementation of `setDataSourceFactory` in
`DefaultMediaSourceFactory`, which was non-functional in some cases
([#116](https://github.com/androidx/media/issues/116)).
* Extractors:
* Add support for AVI
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
......
......@@ -282,6 +282,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
*/
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
delegateFactoryLoader.setDataSourceFactory(dataSourceFactory);
return this;
}
......@@ -594,6 +595,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
this.dataSourceFactory = dataSourceFactory;
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
// exists on the interface.
mediaSourceFactorySuppliers.clear();
mediaSourceFactories.clear();
}
}
......@@ -627,6 +629,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
}
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
DataSource.Factory dataSourceFactory = checkNotNull(this.dataSourceFactory);
try {
Class<? extends MediaSource.Factory> clazz;
switch (contentType) {
......@@ -634,19 +637,19 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
clazz =
Class.forName("androidx.media3.exoplayer.dash.DashMediaSource$Factory")
.asSubclass(MediaSource.Factory.class);
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
break;
case C.CONTENT_TYPE_SS:
clazz =
Class.forName("androidx.media3.exoplayer.smoothstreaming.SsMediaSource$Factory")
.asSubclass(MediaSource.Factory.class);
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
break;
case C.CONTENT_TYPE_HLS:
clazz =
Class.forName("androidx.media3.exoplayer.hls.HlsMediaSource$Factory")
.asSubclass(MediaSource.Factory.class);
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
break;
case C.CONTENT_TYPE_RTSP:
clazz =
......@@ -656,9 +659,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
break;
case C.CONTENT_TYPE_OTHER:
mediaSourceFactorySupplier =
() ->
new ProgressiveMediaSource.Factory(
checkNotNull(dataSourceFactory), extractorsFactory);
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
break;
default:
// Do nothing.
......
......@@ -15,16 +15,21 @@
*/
package androidx.media3.exoplayer.dash;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.test.utils.FakeDataSource;
import androidx.media3.test.utils.robolectric.RobolectricUtil;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_DASH);
}
@Test
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
.setDataSourceFactory(() -> fakeDataSource);
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
@Test
public void
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
// Use default DataSource.Factory first.
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
private static void prepareDashUrlAndWaitForPrepareError(
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
MediaSource mediaSource =
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.mpd"));
getInstrumentation()
.runOnMainSync(
() ->
mediaSource.prepareSource(
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
// We don't expect this to prepare successfully.
RobolectricUtil.runMainLooperUntil(
() -> {
try {
mediaSource.maybeThrowSourceInfoRefreshError();
return false;
} catch (IOException e) {
return true;
}
});
}
}
......@@ -15,16 +15,21 @@
*/
package androidx.media3.exoplayer.hls;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.test.utils.FakeDataSource;
import androidx.media3.test.utils.robolectric.RobolectricUtil;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_HLS);
}
@Test
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
.setDataSourceFactory(() -> fakeDataSource);
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
@Test
public void
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
// Use default DataSource.Factory first.
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
private static void prepareHlsUrlAndWaitForPrepareError(
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
MediaSource mediaSource =
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.m3u8"));
getInstrumentation()
.runOnMainSync(
() ->
mediaSource.prepareSource(
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
// We don't expect this to prepare successfully.
RobolectricUtil.runMainLooperUntil(
() -> {
try {
mediaSource.maybeThrowSourceInfoRefreshError();
return false;
} catch (IOException e) {
return true;
}
});
}
}
......@@ -29,6 +29,7 @@ dependencies {
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
testImplementation project(modulePrefix + 'test-utils-robolectric')
testImplementation project(modulePrefix + 'test-utils')
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
}
......
......@@ -15,16 +15,21 @@
*/
package androidx.media3.exoplayer.smoothstreaming;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.test.utils.FakeDataSource;
import androidx.media3.test.utils.robolectric.RobolectricUtil;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -93,4 +98,53 @@ public class DefaultMediaSourceFactoryTest {
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_SS);
}
@Test
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
.setDataSourceFactory(() -> fakeDataSource);
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
@Test
public void
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
throws Exception {
FakeDataSource fakeDataSource = new FakeDataSource();
DefaultMediaSourceFactory defaultMediaSourceFactory =
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
// Use default DataSource.Factory first.
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
}
private static void prepareSsUrlAndWaitForPrepareError(
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
MediaSource mediaSource =
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.ism"));
getInstrumentation()
.runOnMainSync(
() ->
mediaSource.prepareSource(
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
// We don't expect this to prepare successfully.
RobolectricUtil.runMainLooperUntil(
() -> {
try {
mediaSource.maybeThrowSourceInfoRefreshError();
return false;
} catch (IOException e) {
return true;
}
});
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment