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
This commit is contained in:
parent
9a616c0cee
commit
adc50515e9
@ -17,6 +17,9 @@
|
|||||||
* Use `SingleThreadExecutor` for releasing `AudioTrack` instances to avoid
|
* Use `SingleThreadExecutor` for releasing `AudioTrack` instances to avoid
|
||||||
OutOfMemory errors when releasing multiple players at the same time
|
OutOfMemory errors when releasing multiple players at the same time
|
||||||
([#10057](https://github.com/google/ExoPlayer/issues/10057)).
|
([#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:
|
* Extractors:
|
||||||
* Add support for AVI
|
* Add support for AVI
|
||||||
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
||||||
|
@ -282,6 +282,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
*/
|
*/
|
||||||
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
|
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
|
delegateFactoryLoader.setDataSourceFactory(dataSourceFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,6 +595,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
|
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
|
||||||
// exists on the interface.
|
// exists on the interface.
|
||||||
|
mediaSourceFactorySuppliers.clear();
|
||||||
mediaSourceFactories.clear();
|
mediaSourceFactories.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -627,6 +629,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
|
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
|
||||||
|
DataSource.Factory dataSourceFactory = checkNotNull(this.dataSourceFactory);
|
||||||
try {
|
try {
|
||||||
Class<? extends MediaSource.Factory> clazz;
|
Class<? extends MediaSource.Factory> clazz;
|
||||||
switch (contentType) {
|
switch (contentType) {
|
||||||
@ -634,19 +637,19 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
clazz =
|
clazz =
|
||||||
Class.forName("androidx.media3.exoplayer.dash.DashMediaSource$Factory")
|
Class.forName("androidx.media3.exoplayer.dash.DashMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_SS:
|
case C.CONTENT_TYPE_SS:
|
||||||
clazz =
|
clazz =
|
||||||
Class.forName("androidx.media3.exoplayer.smoothstreaming.SsMediaSource$Factory")
|
Class.forName("androidx.media3.exoplayer.smoothstreaming.SsMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_HLS:
|
case C.CONTENT_TYPE_HLS:
|
||||||
clazz =
|
clazz =
|
||||||
Class.forName("androidx.media3.exoplayer.hls.HlsMediaSource$Factory")
|
Class.forName("androidx.media3.exoplayer.hls.HlsMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_RTSP:
|
case C.CONTENT_TYPE_RTSP:
|
||||||
clazz =
|
clazz =
|
||||||
@ -656,9 +659,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_OTHER:
|
case C.CONTENT_TYPE_OTHER:
|
||||||
mediaSourceFactorySupplier =
|
mediaSourceFactorySupplier =
|
||||||
() ->
|
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
|
||||||
new ProgressiveMediaSource.Factory(
|
|
||||||
checkNotNull(dataSourceFactory), extractorsFactory);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
@ -15,16 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.dash;
|
package androidx.media3.exoplayer.dash;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
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.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_DASH);
|
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;
|
package androidx.media3.exoplayer.hls;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
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.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_HLS);
|
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.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||||
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
||||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||||
|
testImplementation project(modulePrefix + 'test-utils-robolectric')
|
||||||
testImplementation project(modulePrefix + 'test-utils')
|
testImplementation project(modulePrefix + 'test-utils')
|
||||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.smoothstreaming;
|
package androidx.media3.exoplayer.smoothstreaming;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
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.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -93,4 +98,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_SS);
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user