Skip to content

Commit

Permalink
Fix setDataSourceFactory handling in DefaultMediaSourceFactory
Browse files Browse the repository at this point in the history
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: #116

PiperOrigin-RevId: 460721541
(cherry picked from commit adc5051)
  • Loading branch information
tonihei authored and rohitjoins committed Jul 13, 2022
1 parent 3abffb7 commit dfe8bee
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 6 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
([#10361](https://github.com/google/ExoPlayer/issues/10361)).
* Allow custom logger for all ExoPlayer log output
([#9752](https://github.com/google/ExoPlayer/issues/9752)).
* 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)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ public DefaultMediaSourceFactory clearLocalAdInsertionComponents() {
*/
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
delegateFactoryLoader.setDataSourceFactory(dataSourceFactory);
return this;
}

Expand Down Expand Up @@ -594,6 +595,7 @@ public void setDataSourceFactory(DataSource.Factory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
// exists on the interface.
mediaSourceFactorySuppliers.clear();
mediaSourceFactories.clear();
}
}
Expand Down Expand Up @@ -627,26 +629,27 @@ private Supplier<MediaSource.Factory> maybeLoadSupplier(@C.ContentType int conte
}

@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
DataSource.Factory dataSourceFactory = checkNotNull(this.dataSourceFactory);
try {
Class<? extends MediaSource.Factory> clazz;
switch (contentType) {
case C.CONTENT_TYPE_DASH:
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 =
Expand All @@ -656,9 +659,7 @@ private Supplier<MediaSource.Factory> maybeLoadSupplier(@C.ContentType int conte
break;
case C.CONTENT_TYPE_OTHER:
mediaSourceFactorySupplier =
() ->
new ProgressiveMediaSource.Factory(
checkNotNull(dataSourceFactory), extractorsFactory);
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
break;
default:
// Do nothing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -82,4 +87,53 @@ public void getSupportedTypes_dashModule_containsTypeDash() {

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;
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -82,4 +87,53 @@ public void getSupportedTypes_hlsModule_containsTypeHls() {

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;
}
});
}
}
1 change: 1 addition & 0 deletions libraries/exoplayer_smoothstreaming/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -93,4 +98,53 @@ public void getSupportedTypes_smoothstreamingModule_containsTypeSS() {

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;
}
});
}
}

0 comments on commit dfe8bee

Please sign in to comment.