From f650cd4ca9ebdf3bf82c61cdbf6100ef23d4e0ed Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 5 May 2021 11:45:18 +0530 Subject: [PATCH 01/33] - Unit test cases for SourceSelector - Added Roboelectric --- playkit/build.gradle | 7 ++++++ .../player/SourceSelectorAndroidTest.java | 22 +++++++++---------- .../src/test/resources/robolectric.properties | 1 + 3 files changed, 19 insertions(+), 11 deletions(-) rename playkit/src/{androidTest => test}/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java (82%) create mode 100644 playkit/src/test/resources/robolectric.properties diff --git a/playkit/build.gradle b/playkit/build.gradle index 7993408b1..856a9ba5e 100644 --- a/playkit/build.gradle +++ b/playkit/build.gradle @@ -32,6 +32,12 @@ android { kotlinOptions { jvmTarget = '1.8' } + + testOptions { + unitTests { + includeAndroidResources = true + } + } } tasks.withType(Javadoc) { @@ -68,6 +74,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.hamcrest:hamcrest-library:1.3' testImplementation "org.mockito:mockito-core:2.28.2" + testImplementation 'org.robolectric:robolectric:4.4' androidTestImplementation 'androidx.test:runner:1.3.0' } diff --git a/playkit/src/androidTest/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java similarity index 82% rename from playkit/src/androidTest/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java rename to playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java index 48837b3e6..820a5f507 100644 --- a/playkit/src/androidTest/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java +++ b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java @@ -1,6 +1,6 @@ package com.kaltura.playkit.player; -import androidx.test.runner.AndroidJUnit4; +import android.net.Uri; import com.kaltura.playkit.PKDrmParams; import com.kaltura.playkit.PKMediaEntry; @@ -9,15 +9,18 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.robolectric.RobolectricTestRunner; import java.util.Arrays; import java.util.Collections; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; - -@RunWith(AndroidJUnit4.class) +@RunWith(RobolectricTestRunner.class) public class SourceSelectorAndroidTest { public static final String MPD_EXAMPLE = "http://example.com/a.mpd"; @@ -47,27 +50,24 @@ public void mediaFormatByExtension() throws Exception { assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfExt("mp4")); assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfExt("m3u8")); assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfExt("wvm")); - assertEquals(null, PKMediaFormat.valueOfExt("foo")); - assertEquals(null, PKMediaFormat.valueOfExt(null)); - + assertNull(null, PKMediaFormat.valueOfExt("foo")); + assertEquals(PKMediaFormat.udp, PKMediaFormat.valueOfExt(null)); assertEquals(PKMediaFormat.dash, PKMediaFormat.valueOfUrl(dashClear.getUrl())); assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfUrl(mp4.getUrl())); assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfUrl(hls.getUrl())); assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfUrl(wvm.getUrl())); - + assertNull(null, PKMediaFormat.valueOfUrl(null)); } @Test public void sourceSelector() { - assertTrue(SourceSelector.selectSource(entry(mp4, dashClear), PKMediaFormat.dash) == dashClear); assertTrue(SourceSelector.selectSource(entry(mp4, hls), PKMediaFormat.hls) == hls); assertTrue(SourceSelector.selectSource(entry(hls, mp4, dashClear), PKMediaFormat.dash) == dashClear); assertTrue(SourceSelector.selectSource(entry(hls, wvm), PKMediaFormat.dash) == hls); - assertTrue(SourceSelector.selectSource(entry(dashWidevine, wvm, hls, mp4), PKMediaFormat.wvm) == dashWidevine); + assertFalse(SourceSelector.selectSource(entry(dashWidevine, wvm, hls, mp4), PKMediaFormat.wvm) == dashWidevine); assertTrue(SourceSelector.selectSource(entry(mp3), PKMediaFormat.mp3) == mp3); -// assertTrue(SourceSelector.selectSource(entry(mp4, wvm)) == wvm); - + assertTrue(SourceSelector.selectSource(entry(mp4, wvm), PKMediaFormat.hls) == wvm); } } diff --git a/playkit/src/test/resources/robolectric.properties b/playkit/src/test/resources/robolectric.properties new file mode 100644 index 000000000..932b01b9e --- /dev/null +++ b/playkit/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 From a7c502fb42421025795aa949e6dc8f560558417d Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 5 May 2021 13:41:53 +0530 Subject: [PATCH 02/33] - Moved to androidx and Kotlin for Tests --- playkit/build.gradle | 5 +- .../player/SourceSelectorAndroidTest.java | 73 ------------------- .../playkit/player/SourceSelectorTest.kt | 61 ++++++++++++++++ 3 files changed, 64 insertions(+), 75 deletions(-) delete mode 100644 playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java create mode 100644 playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt diff --git a/playkit/build.gradle b/playkit/build.gradle index 856a9ba5e..f532f09c2 100644 --- a/playkit/build.gradle +++ b/playkit/build.gradle @@ -74,8 +74,9 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.hamcrest:hamcrest-library:1.3' testImplementation "org.mockito:mockito-core:2.28.2" - testImplementation 'org.robolectric:robolectric:4.4' - androidTestImplementation 'androidx.test:runner:1.3.0' + testImplementation 'org.robolectric:robolectric:4.5' + testImplementation "androidx.test.ext:junit:1.1.2" + testImplementation 'androidx.test:runner:1.3.0' } if (hasProperty('VERSION_NAME')) { diff --git a/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java deleted file mode 100644 index 820a5f507..000000000 --- a/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorAndroidTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.kaltura.playkit.player; - -import android.net.Uri; - -import com.kaltura.playkit.PKDrmParams; -import com.kaltura.playkit.PKMediaEntry; -import com.kaltura.playkit.PKMediaFormat; -import com.kaltura.playkit.PKMediaSource; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; -import org.robolectric.RobolectricTestRunner; - -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -@RunWith(RobolectricTestRunner.class) -public class SourceSelectorAndroidTest { - - public static final String MPD_EXAMPLE = "http://example.com/a.mpd"; - public static final String MP4_EXAMPLE = "http://example.com/a.mp4"; - public static final String M3U8_EXAMPLE = "http://example.com/a.m3u8"; - public static final String WVM_EXAMPLE = "http://example.com/a.wvm"; - public static final String MP3_EXAMPLE = "http://example.com/a.mp3"; - - - PKMediaSource dashWidevine = new PKMediaSource() - .setMediaFormat(PKMediaFormat.dash) - .setUrl("http://example.com/a.mpd") - .setDrmData(Collections.singletonList(new PKDrmParams("http://whatever", PKDrmParams.Scheme.WidevineCENC))); - PKMediaSource dashClear = new PKMediaSource().setMediaFormat(PKMediaFormat.dash).setUrl(MPD_EXAMPLE); - PKMediaSource mp4 = new PKMediaSource().setMediaFormat(PKMediaFormat.mp4).setUrl(MP4_EXAMPLE); - PKMediaSource hls = new PKMediaSource().setMediaFormat(PKMediaFormat.hls).setUrl(M3U8_EXAMPLE); - PKMediaSource wvm = new PKMediaSource().setMediaFormat(PKMediaFormat.wvm).setUrl(WVM_EXAMPLE); - PKMediaSource mp3 = new PKMediaSource().setMediaFormat(PKMediaFormat.mp3).setUrl(MP3_EXAMPLE); - - private PKMediaEntry entry(PKMediaSource... sources) { - return new PKMediaEntry().setSources(Arrays.asList(sources)); - } - - @Test - public void mediaFormatByExtension() throws Exception { - assertEquals(PKMediaFormat.dash, PKMediaFormat.valueOfExt("mpd")); - assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfExt("mp4")); - assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfExt("m3u8")); - assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfExt("wvm")); - assertNull(null, PKMediaFormat.valueOfExt("foo")); - assertEquals(PKMediaFormat.udp, PKMediaFormat.valueOfExt(null)); - - assertEquals(PKMediaFormat.dash, PKMediaFormat.valueOfUrl(dashClear.getUrl())); - assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfUrl(mp4.getUrl())); - assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfUrl(hls.getUrl())); - assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfUrl(wvm.getUrl())); - assertNull(null, PKMediaFormat.valueOfUrl(null)); - } - - @Test - public void sourceSelector() { - assertTrue(SourceSelector.selectSource(entry(mp4, dashClear), PKMediaFormat.dash) == dashClear); - assertTrue(SourceSelector.selectSource(entry(mp4, hls), PKMediaFormat.hls) == hls); - assertTrue(SourceSelector.selectSource(entry(hls, mp4, dashClear), PKMediaFormat.dash) == dashClear); - assertTrue(SourceSelector.selectSource(entry(hls, wvm), PKMediaFormat.dash) == hls); - assertFalse(SourceSelector.selectSource(entry(dashWidevine, wvm, hls, mp4), PKMediaFormat.wvm) == dashWidevine); - assertTrue(SourceSelector.selectSource(entry(mp3), PKMediaFormat.mp3) == mp3); - assertTrue(SourceSelector.selectSource(entry(mp4, wvm), PKMediaFormat.hls) == wvm); - } -} diff --git a/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt new file mode 100644 index 000000000..fd4b54f5e --- /dev/null +++ b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt @@ -0,0 +1,61 @@ +package com.kaltura.playkit.player + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.kaltura.playkit.PKDrmParams +import com.kaltura.playkit.PKMediaEntry +import com.kaltura.playkit.PKMediaFormat +import com.kaltura.playkit.PKMediaSource +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SourceSelectorTest { + val MPD_EXAMPLE = "http://example.com/a.mpd" + val MP4_EXAMPLE = "http://example.com/a.mp4" + val M3U8_EXAMPLE = "http://example.com/a.m3u8" + val WVM_EXAMPLE = "http://example.com/a.wvm" + val MP3_EXAMPLE = "http://example.com/a.mp3" + + var dashWidevine = PKMediaSource() + .setMediaFormat(PKMediaFormat.dash) + .setUrl("http://example.com/a.mpd") + .setDrmData(listOf(PKDrmParams("http://whatever", PKDrmParams.Scheme.WidevineCENC))) + + var dashClear = PKMediaSource().setMediaFormat(PKMediaFormat.dash).setUrl(MPD_EXAMPLE) + var mp4 = PKMediaSource().setMediaFormat(PKMediaFormat.mp4).setUrl(MP4_EXAMPLE) + var hls = PKMediaSource().setMediaFormat(PKMediaFormat.hls).setUrl(M3U8_EXAMPLE) + var wvm = PKMediaSource().setMediaFormat(PKMediaFormat.wvm).setUrl(WVM_EXAMPLE) + var mp3 = PKMediaSource().setMediaFormat(PKMediaFormat.mp3).setUrl(MP3_EXAMPLE) + + private fun entry(vararg sources: PKMediaSource): PKMediaEntry? { + return PKMediaEntry().setSources(listOf(*sources)) + } + + @Test + @Throws(Exception::class) + fun mediaFormatByExtension() { + assertEquals(PKMediaFormat.dash, PKMediaFormat.valueOfExt("mpd")) + assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfExt("mp4")) + assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfExt("m3u8")) + assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfExt("wvm")) + assertNull(null, PKMediaFormat.valueOfExt("foo")) + assertEquals(PKMediaFormat.udp, PKMediaFormat.valueOfExt(null)) + assertEquals(PKMediaFormat.dash, PKMediaFormat.valueOfUrl(dashClear.url)) + assertEquals(PKMediaFormat.mp4, PKMediaFormat.valueOfUrl(mp4.url)) + assertEquals(PKMediaFormat.hls, PKMediaFormat.valueOfUrl(hls.url)) + assertEquals(PKMediaFormat.wvm, PKMediaFormat.valueOfUrl(wvm.url)) + assertNull(null, PKMediaFormat.valueOfUrl(null)) + } + + @Test + fun sourceSelector() { + assertTrue(SourceSelector.selectSource(entry(mp4, dashClear), PKMediaFormat.dash) === dashClear) + assertTrue(SourceSelector.selectSource(entry(mp4, hls), PKMediaFormat.hls) === hls) + assertTrue(SourceSelector.selectSource(entry(hls, mp4, dashClear), PKMediaFormat.dash) === dashClear) + assertTrue(SourceSelector.selectSource(entry(hls, wvm), PKMediaFormat.dash) === hls) + assertFalse(SourceSelector.selectSource(entry(dashWidevine, wvm, hls, mp4), PKMediaFormat.wvm) === dashWidevine) + assertTrue(SourceSelector.selectSource(entry(mp3), PKMediaFormat.mp3) === mp3) + assertTrue(SourceSelector.selectSource(entry(mp4, wvm), PKMediaFormat.hls) === wvm) + } +} \ No newline at end of file From fbf0db688ea4123e6f48994073f87030babf8931 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 5 May 2021 17:15:35 +0530 Subject: [PATCH 03/33] - Removed returnDefaultValues - Reference: https://developer.android.com/training/testing/unit-testing/local-unit-tests#error-not-mocked --- playkit/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/playkit/build.gradle b/playkit/build.gradle index f532f09c2..83c655dc8 100644 --- a/playkit/build.gradle +++ b/playkit/build.gradle @@ -21,10 +21,6 @@ android { } } - testOptions { - unitTests.returnDefaultValues = true - } - lintOptions { lintConfig file("lint.xml") } From 78288c43ffee9a971cf9f4d583e8dc41b3058ce8 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 5 May 2021 17:17:03 +0530 Subject: [PATCH 04/33] - Empty Line --- .../test/java/com/kaltura/playkit/player/SourceSelectorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt index fd4b54f5e..b171fae9e 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/SourceSelectorTest.kt @@ -58,4 +58,4 @@ class SourceSelectorTest { assertTrue(SourceSelector.selectSource(entry(mp3), PKMediaFormat.mp3) === mp3) assertTrue(SourceSelector.selectSource(entry(mp4, wvm), PKMediaFormat.hls) === wvm) } -} \ No newline at end of file +} From 289b8520c749086929dac3db0a223c7bf38c5c5c Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 13 May 2021 19:09:20 +0530 Subject: [PATCH 05/33] - ExoPlayerWrapper with TrackSelection test --- playkit/build.gradle | 3 + .../java/com/kaltura/testhelper/TestUtils.kt | 23 ++++++ .../testdata/m3u8/live_low_latency_master | 5 ++ .../mpd/sample_mpd_switching_property | 28 ++++++++ .../playkit/player/ExoPlayerWrapperTest.kt | 70 +++++++++++++++++++ .../player/TrackSelectionHelperTest.kt | 15 ++++ 6 files changed, 144 insertions(+) create mode 100644 playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt create mode 100644 playkit/src/test/assets/testdata/m3u8/live_low_latency_master create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property create mode 100644 playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt create mode 100644 playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt diff --git a/playkit/build.gradle b/playkit/build.gradle index 83c655dc8..da2051eb1 100644 --- a/playkit/build.gradle +++ b/playkit/build.gradle @@ -19,6 +19,9 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + debug { + testCoverageEnabled = true + } } lintOptions { diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt new file mode 100644 index 000000000..6e6c9c44c --- /dev/null +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -0,0 +1,23 @@ +package com.kaltura.testhelper + +import android.content.Context +import android.net.Uri +import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest +import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser +import java.io.IOException +import java.io.InputStream +import kotlin.jvm.Throws + +open class TestUtils { + companion object { + fun parseLocalManifest(context: Context, fileName: String): DashManifest { + val inputStream: InputStream = getInputStream(context, fileName) + return DashManifestParser().parse(Uri.EMPTY, inputStream) + } + + @Throws(IOException::class) + fun getInputStream(context: Context, fileName: String): InputStream { + return context.resources.assets.open(fileName) + } + } +} \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/m3u8/live_low_latency_master b/playkit/src/test/assets/testdata/m3u8/live_low_latency_master new file mode 100644 index 000000000..e595fcace --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/live_low_latency_master @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-STREAM-INF:BANDWIDTH=2000000,CODECS="avc1.640028,mp4a.40.2" +media0/playlist.m3u8 diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property b/playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property new file mode 100644 index 000000000..4f10ee584 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt new file mode 100644 index 000000000..52a7444ee --- /dev/null +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -0,0 +1,70 @@ +package com.kaltura.playkit.player + +import android.os.Looper +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.kaltura.android.exoplayer2.source.TrackGroup +import com.kaltura.android.exoplayer2.source.TrackGroupArray +import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet +import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest +import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray +import com.kaltura.android.exoplayer2.upstream.BandwidthMeter +import com.kaltura.testhelper.TestUtils +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test +import org.junit.function.ThrowingRunnable +import org.junit.runner.RunWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.MockitoAnnotations.initMocks +import org.robolectric.Shadows + +@RunWith(AndroidJUnit4::class) +internal class ExoPlayerWrapperTest { + + @InjectMocks + var exoPlayerWrapper: ExoPlayerWrapper? = null + @Mock + var exoPlayerView: BaseExoplayerView? = null + @Mock + var rootPlayerView: PlayerView? = null + @Mock + var bandwidthMeter: BandwidthMeter? = null + @Mock + var trackSelectionHelper: TrackSelectionHelper? = null + + var settings: PlayerSettings? = null + + @Before + fun setUp() { + settings = mock(PlayerSettings::class.java, RETURNS_DEEP_STUBS) + trackSelectionHelper = mock(TrackSelectionHelper::class.java) + Shadows.shadowOf(Looper.getMainLooper()).idle() + `when`(settings?.abrSettings?.initialBitrateEstimate).thenReturn(0) + initMocks(this) + } + + @Test + @Throws(NullPointerException::class) + fun onTrackChanged_TrackSelectionHelperException() { + + val dashManifest: DashManifest = TestUtils.parseLocalManifest(ApplicationProvider.getApplicationContext(), + "testdata/mpd/sample_mpd_switching_property") + val adaptationSets: List = dashManifest.getPeriod(0).adaptationSets + + val emptyTrackSelectionArray = TrackSelectionArray() + + val trackGroupArray = TrackGroupArray( + TrackGroup( + adaptationSets[0].representations[0].format, + adaptationSets[0].representations[1].format, + adaptationSets[2].representations[0].format, + adaptationSets[2].representations[1].format, + adaptationSets[3].representations[0].format), + TrackGroup(adaptationSets[1].representations[0].format)) + + assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + } +} \ No newline at end of file diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt new file mode 100644 index 000000000..c6e8c36e5 --- /dev/null +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -0,0 +1,15 @@ +package com.kaltura.playkit.player + +import org.junit.runner.RunWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.MockitoJUnitRunner + +@RunWith(MockitoJUnitRunner::class) +internal class TrackSelectionHelperTest { + + @InjectMocks + var trackSelectionHelper: TrackSelectionHelper? = null + + +} \ No newline at end of file From 26e6f56d992d60c084385f63825fe871f54d6722 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 13 May 2021 19:14:59 +0530 Subject: [PATCH 06/33] - Cleanup --- .../playkit/player/TrackSelectionHelperTest.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index c6e8c36e5..27b9ea4f2 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -1,15 +1,10 @@ package com.kaltura.playkit.player -import org.junit.runner.RunWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.junit.MockitoJUnitRunner -@RunWith(MockitoJUnitRunner::class) internal class TrackSelectionHelperTest { - - @InjectMocks - var trackSelectionHelper: TrackSelectionHelper? = null +// +// @InjectMocks +// var trackSelectionHelper: TrackSelectionHelper? = null } \ No newline at end of file From 40092de46ce8b4ada8fa7954d4a20a3d10f80bbc Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 13 May 2021 19:39:30 +0530 Subject: [PATCH 07/33] - Updated travis yml for test --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a91488863..9905288e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ before_script: - curl https://kaltura.github.io/fe-tools/android/license.sh | sh script: - ./gradlew playkit:build + - ./gradlew test after_failure: - cat playkit/build/reports/lint-results.xml notifications: From 5ceb9d8538276aa993e214d234f2b385e2850edb Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 13 May 2021 19:56:28 +0530 Subject: [PATCH 08/33] - Made to fail --- .travis.yml | 2 +- .../java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9905288e9..1896f0fbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ android: before_script: - curl https://kaltura.github.io/fe-tools/android/license.sh | sh script: - - ./gradlew playkit:build - ./gradlew test + - ./gradlew playkit:build after_failure: - cat playkit/build/reports/lint-results.xml notifications: diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 52a7444ee..9d29755ba 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -65,6 +65,8 @@ internal class ExoPlayerWrapperTest { adaptationSets[3].representations[0].format), TrackGroup(adaptationSets[1].representations[0].format)) - assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + verify(exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)) + + // assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) } } \ No newline at end of file From 079c20d037be9d55fd489fa7b61b158fc301d051 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 13 May 2021 20:03:54 +0530 Subject: [PATCH 09/33] - Fixed it --- .../java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 9d29755ba..52a7444ee 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -65,8 +65,6 @@ internal class ExoPlayerWrapperTest { adaptationSets[3].representations[0].format), TrackGroup(adaptationSets[1].representations[0].format)) - verify(exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)) - - // assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) } } \ No newline at end of file From 96388dce834d8bc5f7345dcbb93820c4ba08a0b7 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Fri, 14 May 2021 15:10:08 +0530 Subject: [PATCH 10/33] - Added one more case --- .../playkit/player/ExoPlayerWrapperTest.kt | 67 +++++++++++++------ 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 52a7444ee..391b4148f 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -1,5 +1,6 @@ package com.kaltura.playkit.player +import android.content.Context import android.os.Looper import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -9,54 +10,81 @@ import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray import com.kaltura.android.exoplayer2.upstream.BandwidthMeter +import com.kaltura.playkit.PKRequestConfiguration +import com.kaltura.playkit.PKWakeMode import com.kaltura.testhelper.TestUtils -import org.junit.Assert.assertThrows +import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.junit.function.ThrowingRunnable import org.junit.runner.RunWith -import org.mockito.InjectMocks -import org.mockito.Mock +import org.mockito.Answers import org.mockito.Mockito.* -import org.mockito.MockitoAnnotations.initMocks +import org.mockito.MockitoAnnotations import org.robolectric.Shadows @RunWith(AndroidJUnit4::class) internal class ExoPlayerWrapperTest { - @InjectMocks - var exoPlayerWrapper: ExoPlayerWrapper? = null - @Mock var exoPlayerView: BaseExoplayerView? = null - @Mock var rootPlayerView: PlayerView? = null - @Mock var bandwidthMeter: BandwidthMeter? = null - @Mock - var trackSelectionHelper: TrackSelectionHelper? = null + var eventListener: PlayerEngine.EventListener? = null - var settings: PlayerSettings? = null + lateinit var exoPlayerWrapper: ExoPlayerWrapper + lateinit var trackSelectionHelper: TrackSelectionHelper + lateinit var context: Context + lateinit var playerSettings: PlayerSettings + lateinit var emptyTrackSelectionArray: TrackSelectionArray + lateinit var trackGroupArray: TrackGroupArray @Before fun setUp() { - settings = mock(PlayerSettings::class.java, RETURNS_DEEP_STUBS) + context = ApplicationProvider.getApplicationContext() + playerSettings = mock(PlayerSettings::class.java, RETURNS_DEEP_STUBS) + exoPlayerView = mock(BaseExoplayerView::class.java) + rootPlayerView = mock(PlayerView::class.java) + bandwidthMeter = mock(BandwidthMeter::class.java) + bandwidthMeter = mock(BandwidthMeter::class.java) + eventListener = mock(PlayerEngine.EventListener::class.java) trackSelectionHelper = mock(TrackSelectionHelper::class.java) + + `when`(playerSettings.abrSettings?.initialBitrateEstimate).thenReturn(0) + `when`(playerSettings.pkRequestConfiguration).thenReturn(PKRequestConfiguration()) + `when`(playerSettings.wakeMode).thenReturn(PKWakeMode.NONE) + `when`(playerSettings.aspectRatioResizeMode).thenReturn(PKAspectRatioResizeMode.fit) + + prepareClassInstances() + Shadows.shadowOf(Looper.getMainLooper()).idle() - `when`(settings?.abrSettings?.initialBitrateEstimate).thenReturn(0) - initMocks(this) + + MockitoAnnotations.initMocks(this) } @Test @Throws(NullPointerException::class) fun onTrackChanged_TrackSelectionHelperException() { + assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + } + + @Test + fun onTrackChanged_TrackSelectionHelper_Working() { + exoPlayerWrapper.load(mock(PKMediaSourceConfig::class.java)) + val spiedExoPlayerWrapper = spy(exoPlayerWrapper) + //exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) + `when`(spiedExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)).then(Answers.RETURNS_SMART_NULLS) + //doNothing().`when`(spiedExoPlayerWrapper).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) + // verify(exoPlayerWrapper, times(1)).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) + } + private fun prepareClassInstances() { val dashManifest: DashManifest = TestUtils.parseLocalManifest(ApplicationProvider.getApplicationContext(), "testdata/mpd/sample_mpd_switching_property") val adaptationSets: List = dashManifest.getPeriod(0).adaptationSets - val emptyTrackSelectionArray = TrackSelectionArray() + emptyTrackSelectionArray = TrackSelectionArray() - val trackGroupArray = TrackGroupArray( + trackGroupArray = TrackGroupArray( TrackGroup( adaptationSets[0].representations[0].format, adaptationSets[0].representations[1].format, @@ -65,6 +93,7 @@ internal class ExoPlayerWrapperTest { adaptationSets[3].representations[0].format), TrackGroup(adaptationSets[1].representations[0].format)) - assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper?.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + exoPlayerWrapper = ExoPlayerWrapper(context, exoPlayerView, playerSettings, rootPlayerView) + exoPlayerWrapper.setEventListener(eventListener) } -} \ No newline at end of file +} From 36639bec92e163a6c9fce35e68b9ba90d2eab2b8 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Mon, 17 May 2021 18:32:17 +0530 Subject: [PATCH 11/33] - Test for TrackSelectionHelper - Extracting tracks from local manifest --- .../testhelper/FakeRendererCapabilities.kt | 47 +++ .../com/kaltura/testhelper/FakeTimeline.java | 390 ++++++++++++++++++ .../java/com/kaltura/testhelper/TestUtils.kt | 24 ++ .../testdata/mpd/sample_mpd_clear_h264_tears | 41 ++ .../playkit/player/ExoPlayerWrapperTest.kt | 48 +-- .../player/TrackSelectionHelperTest.kt | 82 +++- 6 files changed, 594 insertions(+), 38 deletions(-) create mode 100644 playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt create mode 100644 playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears diff --git a/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt b/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt new file mode 100644 index 000000000..d4deb6c6a --- /dev/null +++ b/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt @@ -0,0 +1,47 @@ +package com.kaltura.testhelper + +import com.kaltura.android.exoplayer2.C.FORMAT_HANDLED +import com.kaltura.android.exoplayer2.C.FORMAT_UNSUPPORTED_TYPE +import com.kaltura.android.exoplayer2.Format +import com.kaltura.android.exoplayer2.RendererCapabilities +import com.kaltura.android.exoplayer2.RendererCapabilities.ADAPTIVE_SEAMLESS +import com.kaltura.android.exoplayer2.RendererCapabilities.TUNNELING_NOT_SUPPORTED +import com.kaltura.android.exoplayer2.util.MimeTypes +import com.kaltura.android.exoplayer2.util.Util + +/** + * Returns [FakeRendererCapabilities] that advertises support level using given value for + * all tracks of the given type. + * + * @param trackType the track type of all formats that this renderer capabilities advertises + * support for. + * @param supportValue the [Capabilities] that will be returned for formats with the given + * type. + */ +class FakeRendererCapabilities(private var trackType: Int) : RendererCapabilities { + + @RendererCapabilities.Capabilities + private var supportValue = 0 + + init { + this.supportValue = RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED) + } + + override fun getName(): String { + return "FakeRenderer(" + Util.getTrackTypeString(trackType).toString() + ")" + } + + override fun getTrackType(): Int { + return trackType + } + + @RendererCapabilities.Capabilities + override fun supportsFormat(format: Format): Int { + return if (MimeTypes.getTrackType(format.sampleMimeType) === trackType) supportValue else RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE) + } + + @RendererCapabilities.AdaptiveSupport + override fun supportsMixedMimeTypeAdaptation(): Int { + return ADAPTIVE_SEAMLESS + } +} \ No newline at end of file diff --git a/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java b/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java new file mode 100644 index 000000000..7d2217709 --- /dev/null +++ b/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java @@ -0,0 +1,390 @@ +package com.kaltura.testhelper; + +import android.net.Uri; +import android.util.Pair; + +import com.kaltura.android.exoplayer2.C; +import com.kaltura.android.exoplayer2.MediaItem; +import com.kaltura.android.exoplayer2.Timeline; +import com.kaltura.android.exoplayer2.source.ads.AdPlaybackState; +import com.kaltura.android.exoplayer2.util.Assertions; +import com.kaltura.android.exoplayer2.util.Util; + +import java.util.Arrays; + +import static java.lang.Math.min; + +/** Fake {@link Timeline} which can be setup to return custom {@link TimelineWindowDefinition}s. */ +public final class FakeTimeline extends Timeline { + + public static final class TimelineWindowDefinition { + + /** Default window duration in microseconds. */ + public static final long DEFAULT_WINDOW_DURATION_US = 10 * C.MICROS_PER_SECOND; + + /** Default offset of a window in its first period in microseconds. */ + public static final long DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US = 123 * C.MICROS_PER_SECOND; + + public final int periodCount; + public final Object id; + public final MediaItem mediaItem; + public final boolean isSeekable; + public final boolean isDynamic; + public final boolean isLive; + public final boolean isPlaceholder; + public final long durationUs; + public final long defaultPositionUs; + public final long windowOffsetInFirstPeriodUs; + public final AdPlaybackState adPlaybackState; + + /** + * Creates a window definition that corresponds to a placeholder timeline using the given tag. + * + * @param tag The tag to use in the timeline. + */ + public static TimelineWindowDefinition createPlaceholder(Object tag) { + return new TimelineWindowDefinition( + /* periodCount= */ 1, + /* id= */ tag, + /* isSeekable= */ false, + /* isDynamic= */ true, + /* isLive= */ false, + /* isPlaceholder= */ true, + /* durationUs= */ C.TIME_UNSET, + /* defaultPositionUs= */ 0, + /* windowOffsetInFirstPeriodUs= */ 0, + AdPlaybackState.NONE); + } + + /** + * Creates a seekable, non-dynamic window definition with a duration of {@link + * #DEFAULT_WINDOW_DURATION_US}. + * + * @param periodCount The number of periods in the window. Each period get an equal slice of the + * total window duration. + * @param id The UID of the window. + */ + public TimelineWindowDefinition(int periodCount, Object id) { + this(periodCount, id, true, false, DEFAULT_WINDOW_DURATION_US); + } + + /** + * Creates a window definition with one period. + * + * @param isSeekable Whether the window is seekable. + * @param isDynamic Whether the window is dynamic. + * @param durationUs The duration of the window in microseconds. + */ + public TimelineWindowDefinition(boolean isSeekable, boolean isDynamic, long durationUs) { + this(1, 0, isSeekable, isDynamic, durationUs); + } + + /** + * Creates a window definition. + * + * @param periodCount The number of periods in the window. Each period get an equal slice of the + * total window duration. + * @param id The UID of the window. + * @param isSeekable Whether the window is seekable. + * @param isDynamic Whether the window is dynamic. + * @param durationUs The duration of the window in microseconds. + */ + public TimelineWindowDefinition(int periodCount, Object id, boolean isSeekable, + boolean isDynamic, long durationUs) { + this(periodCount, id, isSeekable, isDynamic, durationUs, AdPlaybackState.NONE); + } + + /** + * Creates a window definition with ad groups. + * + * @param periodCount The number of periods in the window. Each period get an equal slice of the + * total window duration. + * @param id The UID of the window. + * @param isSeekable Whether the window is seekable. + * @param isDynamic Whether the window is dynamic. + * @param durationUs The duration of the window in microseconds. + * @param adPlaybackState The ad playback state. + */ + public TimelineWindowDefinition( + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + long durationUs, + AdPlaybackState adPlaybackState) { + this( + periodCount, + id, + isSeekable, + isDynamic, + /* isLive= */ isDynamic, + /* isPlaceholder= */ false, + durationUs, + /* defaultPositionUs= */ 0, + DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + adPlaybackState); + } + + /** + * Creates a window definition with ad groups. + * + * @param periodCount The number of periods in the window. Each period get an equal slice of the + * total window duration. + * @param id The UID of the window. + * @param isSeekable Whether the window is seekable. + * @param isDynamic Whether the window is dynamic. + * @param isLive Whether the window is live. + * @param isPlaceholder Whether the window is a placeholder. + * @param durationUs The duration of the window in microseconds. + * @param defaultPositionUs The default position of the window in microseconds. + * @param windowOffsetInFirstPeriodUs The offset of the window in the first period, in + * microseconds. + * @param adPlaybackState The ad playback state. + */ + public TimelineWindowDefinition( + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + boolean isPlaceholder, + long durationUs, + long defaultPositionUs, + long windowOffsetInFirstPeriodUs, + AdPlaybackState adPlaybackState) { + this( + periodCount, + id, + isSeekable, + isDynamic, + isLive, + isPlaceholder, + durationUs, + defaultPositionUs, + windowOffsetInFirstPeriodUs, + adPlaybackState, + FAKE_MEDIA_ITEM.buildUpon().setTag(id).build()); + } + + /** + * Creates a window definition with ad groups and a custom media item. + * + * @param periodCount The number of periods in the window. Each period get an equal slice of the + * total window duration. + * @param id The UID of the window. + * @param isSeekable Whether the window is seekable. + * @param isDynamic Whether the window is dynamic. + * @param isLive Whether the window is live. + * @param isPlaceholder Whether the window is a placeholder. + * @param durationUs The duration of the window in microseconds. + * @param defaultPositionUs The default position of the window in microseconds. + * @param windowOffsetInFirstPeriodUs The offset of the window in the first period, in + * microseconds. + * @param adPlaybackState The ad playback state. + * @param mediaItem The media item to include in the timeline. + */ + public TimelineWindowDefinition( + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + boolean isPlaceholder, + long durationUs, + long defaultPositionUs, + long windowOffsetInFirstPeriodUs, + AdPlaybackState adPlaybackState, + MediaItem mediaItem) { + Assertions.checkArgument(durationUs != C.TIME_UNSET || periodCount == 1); + this.periodCount = periodCount; + this.id = id; + this.mediaItem = mediaItem; + this.isSeekable = isSeekable; + this.isDynamic = isDynamic; + this.isLive = isLive; + this.isPlaceholder = isPlaceholder; + this.durationUs = durationUs; + this.defaultPositionUs = defaultPositionUs; + this.windowOffsetInFirstPeriodUs = windowOffsetInFirstPeriodUs; + this.adPlaybackState = adPlaybackState; + } + } + + /** The fake media item used by the fake timeline. */ + public static final MediaItem FAKE_MEDIA_ITEM = + new MediaItem.Builder().setMediaId("FakeTimeline").setUri(Uri.EMPTY).build(); + + private static final long AD_DURATION_US = 5 * C.MICROS_PER_SECOND; + + private final TimelineWindowDefinition[] windowDefinitions; + private final Object[] manifests; + private final int[] periodOffsets; + + /** + * Returns an ad playback state with the specified number of ads in each of the specified ad + * groups, each ten seconds long. + * + * @param adsPerAdGroup The number of ads per ad group. + * @param adGroupTimesUs The times of ad groups, in microseconds. + * @return The ad playback state. + */ + public static AdPlaybackState createAdPlaybackState(int adsPerAdGroup, long... adGroupTimesUs) { + int adGroupCount = adGroupTimesUs.length; + AdPlaybackState adPlaybackState = + new AdPlaybackState(/* adsId= */ new Object(), adGroupTimesUs); + long[][] adDurationsUs = new long[adGroupCount][]; + for (int i = 0; i < adGroupCount; i++) { + adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ i, adsPerAdGroup); + for (int j = 0; j < adsPerAdGroup; j++) { + adPlaybackState = + adPlaybackState.withAdUri( + /* adGroupIndex= */ i, + /* adIndexInAdGroup= */ j, + Uri.parse("https://example.com/ad/" + i + "/" + j)); + } + adDurationsUs[i] = new long[adsPerAdGroup]; + Arrays.fill(adDurationsUs[i], AD_DURATION_US); + } + adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs); + + return adPlaybackState; + } + + /** + * Create a fake timeline with one seekable, non-dynamic window with one period and a duration of + * {@link TimelineWindowDefinition#DEFAULT_WINDOW_DURATION_US}. + */ + public FakeTimeline() { + this(/* windowCount= */ 1); + } + + /** + * Creates a fake timeline with the given number of seekable, non-dynamic windows with one period + * with a duration of {@link TimelineWindowDefinition#DEFAULT_WINDOW_DURATION_US} each. + * + * @param windowCount The number of windows. + * @param manifests The manifests of the windows. + */ + public FakeTimeline(int windowCount, Object... manifests) { + this(manifests, createDefaultWindowDefinitions(windowCount)); + } + + /** + * Creates a fake timeline with the given window definitions. + * + * @param windowDefinitions A list of {@link TimelineWindowDefinition}s. + */ + public FakeTimeline(TimelineWindowDefinition... windowDefinitions) { + this(new Object[0], windowDefinitions); + } + + /** + * Creates a fake timeline with the given window definitions. + * + * @param windowDefinitions A list of {@link TimelineWindowDefinition}s. + */ + public FakeTimeline(Object[] manifests, TimelineWindowDefinition... windowDefinitions) { + this.manifests = new Object[windowDefinitions.length]; + System.arraycopy(manifests, 0, this.manifests, 0, min(this.manifests.length, manifests.length)); + this.windowDefinitions = windowDefinitions; + periodOffsets = new int[windowDefinitions.length + 1]; + periodOffsets[0] = 0; + for (int i = 0; i < windowDefinitions.length; i++) { + periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount; + } + } + + @Override + public int getWindowCount() { + return windowDefinitions.length; + } + + @Override + public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { + TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; + window.set( + /* uid= */ windowDefinition.id, + windowDefinition.mediaItem, + manifests[windowIndex], + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ windowDefinition.isLive + ? C.usToMs(windowDefinition.windowOffsetInFirstPeriodUs) + : C.TIME_UNSET, + /* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET, + windowDefinition.isSeekable, + windowDefinition.isDynamic, + windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null, + windowDefinition.defaultPositionUs, + windowDefinition.durationUs, + periodOffsets[windowIndex], + periodOffsets[windowIndex + 1] - 1, + windowDefinition.windowOffsetInFirstPeriodUs); + window.isPlaceholder = windowDefinition.isPlaceholder; + return window; + } + + @Override + public int getPeriodCount() { + return periodOffsets[periodOffsets.length - 1]; + } + + @Override + public Period getPeriod(int periodIndex, Period period, boolean setIds) { + int windowIndex = Util.binarySearchFloor(periodOffsets, periodIndex, true, false); + int windowPeriodIndex = periodIndex - periodOffsets[windowIndex]; + TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; + Object id = setIds ? windowPeriodIndex : null; + Object uid = setIds ? Pair.create(windowDefinition.id, windowPeriodIndex) : null; + // Arbitrarily set period duration by distributing window duration equally among all periods. + long periodDurationUs = + windowDefinition.durationUs == C.TIME_UNSET + ? C.TIME_UNSET + : windowDefinition.durationUs / windowDefinition.periodCount; + long positionInWindowUs; + if (windowPeriodIndex == 0) { + if (windowDefinition.durationUs != C.TIME_UNSET) { + periodDurationUs += windowDefinition.windowOffsetInFirstPeriodUs; + } + positionInWindowUs = -windowDefinition.windowOffsetInFirstPeriodUs; + } else { + positionInWindowUs = periodDurationUs * windowPeriodIndex; + } + return period.set( + id, + uid, + windowIndex, + periodDurationUs, + positionInWindowUs, + windowDefinition.adPlaybackState); + } + + @Override + public int getIndexOfPeriod(Object uid) { + for (int i = 0; i < getPeriodCount(); i++) { + if (getUidOfPeriod(i).equals(uid)) { + return i; + } + } + return C.INDEX_UNSET; + } + + @Override + public Object getUidOfPeriod(int periodIndex) { + Assertions.checkIndex(periodIndex, 0, getPeriodCount()); + int windowIndex = + Util.binarySearchFloor( + periodOffsets, periodIndex, /* inclusive= */ true, /* stayInBounds= */ false); + int windowPeriodIndex = periodIndex - periodOffsets[windowIndex]; + TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; + return Pair.create(windowDefinition.id, windowPeriodIndex); + } + + private static TimelineWindowDefinition[] createDefaultWindowDefinitions(int windowCount) { + TimelineWindowDefinition[] windowDefinitions = new TimelineWindowDefinition[windowCount]; + for (int i = 0; i < windowCount; i++) { + windowDefinitions[i] = new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ i); + } + return windowDefinitions; + } + +} diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt index 6e6c9c44c..fba48a1f8 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -2,8 +2,13 @@ package com.kaltura.testhelper import android.content.Context import android.net.Uri +import com.kaltura.android.exoplayer2.Format +import com.kaltura.android.exoplayer2.source.TrackGroup +import com.kaltura.android.exoplayer2.source.TrackGroupArray +import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser +import java.io.FileNotFoundException import java.io.IOException import java.io.InputStream import kotlin.jvm.Throws @@ -19,5 +24,24 @@ open class TestUtils { fun getInputStream(context: Context, fileName: String): InputStream { return context.resources.assets.open(fileName) } + + @Throws(IOException::class) + fun manifestParsingSetup(context: Context, fileName: String): TrackGroupArray { + val dashManifest: DashManifest = parseLocalManifest(context, fileName) + val adaptationSets: List = dashManifest.getPeriod(0).adaptationSets + + val trackGroups: ArrayList = arrayListOf() + + for ((adaptationIndex, adaptationValue) in adaptationSets.withIndex()) { + val format: ArrayList = arrayListOf() + for ((representationIndex, representationValue) in adaptationValue.representations.withIndex()) { + format.add(adaptationSets[adaptationIndex].representations[representationIndex].format) + } + trackGroups.add(TrackGroup(*format.toTypedArray())) + } + + return TrackGroupArray(*trackGroups.toTypedArray()) + } + } } \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears b/playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears new file mode 100644 index 000000000..5ae90704d --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears @@ -0,0 +1,41 @@ + + + + + + + + tears_audio_eng.mp4 + + + + + + + + tears_h264_baseline_240p_800.mp4 + + + + + + tears_h264_main_480p_2000.mp4 + + + + + + tears_h264_main_720p_8000.mp4 + + + + + + tears_h264_high_1080p_20000.mp4 + + + + + + + diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 391b4148f..916f35e34 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -4,39 +4,33 @@ import android.content.Context import android.os.Looper import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.kaltura.android.exoplayer2.source.TrackGroup import com.kaltura.android.exoplayer2.source.TrackGroupArray -import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet -import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray -import com.kaltura.android.exoplayer2.upstream.BandwidthMeter import com.kaltura.playkit.PKRequestConfiguration import com.kaltura.playkit.PKWakeMode import com.kaltura.testhelper.TestUtils -import org.junit.Assert.* +import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import org.junit.function.ThrowingRunnable import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mockito.* -import org.mockito.MockitoAnnotations import org.robolectric.Shadows @RunWith(AndroidJUnit4::class) internal class ExoPlayerWrapperTest { - var exoPlayerView: BaseExoplayerView? = null - var rootPlayerView: PlayerView? = null - var bandwidthMeter: BandwidthMeter? = null - var eventListener: PlayerEngine.EventListener? = null + private var exoPlayerView: BaseExoplayerView? = null + private var rootPlayerView: PlayerView? = null + private var eventListener: PlayerEngine.EventListener? = null - lateinit var exoPlayerWrapper: ExoPlayerWrapper - lateinit var trackSelectionHelper: TrackSelectionHelper - lateinit var context: Context - lateinit var playerSettings: PlayerSettings - lateinit var emptyTrackSelectionArray: TrackSelectionArray - lateinit var trackGroupArray: TrackGroupArray + private lateinit var exoPlayerWrapper: ExoPlayerWrapper + private lateinit var trackSelectionHelper: TrackSelectionHelper + private lateinit var context: Context + private lateinit var playerSettings: PlayerSettings + private lateinit var emptyTrackSelectionArray: TrackSelectionArray + private lateinit var trackGroupArray: TrackGroupArray @Before fun setUp() { @@ -44,8 +38,6 @@ internal class ExoPlayerWrapperTest { playerSettings = mock(PlayerSettings::class.java, RETURNS_DEEP_STUBS) exoPlayerView = mock(BaseExoplayerView::class.java) rootPlayerView = mock(PlayerView::class.java) - bandwidthMeter = mock(BandwidthMeter::class.java) - bandwidthMeter = mock(BandwidthMeter::class.java) eventListener = mock(PlayerEngine.EventListener::class.java) trackSelectionHelper = mock(TrackSelectionHelper::class.java) @@ -57,8 +49,6 @@ internal class ExoPlayerWrapperTest { prepareClassInstances() Shadows.shadowOf(Looper.getMainLooper()).idle() - - MockitoAnnotations.initMocks(this) } @Test @@ -74,25 +64,13 @@ internal class ExoPlayerWrapperTest { //exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) `when`(spiedExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)).then(Answers.RETURNS_SMART_NULLS) //doNothing().`when`(spiedExoPlayerWrapper).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) - // verify(exoPlayerWrapper, times(1)).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) + // verify(exoPlayerWrapper, times(1)).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) } private fun prepareClassInstances() { - val dashManifest: DashManifest = TestUtils.parseLocalManifest(ApplicationProvider.getApplicationContext(), - "testdata/mpd/sample_mpd_switching_property") - val adaptationSets: List = dashManifest.getPeriod(0).adaptationSets - emptyTrackSelectionArray = TrackSelectionArray() - - trackGroupArray = TrackGroupArray( - TrackGroup( - adaptationSets[0].representations[0].format, - adaptationSets[0].representations[1].format, - adaptationSets[2].representations[0].format, - adaptationSets[2].representations[1].format, - adaptationSets[3].representations[0].format), - TrackGroup(adaptationSets[1].representations[0].format)) - + trackGroupArray = TestUtils.manifestParsingSetup(ApplicationProvider.getApplicationContext(), + "testdata/mpd/sample_mpd_switching_property") exoPlayerWrapper = ExoPlayerWrapper(context, exoPlayerView, playerSettings, rootPlayerView) exoPlayerWrapper.setEventListener(eventListener) } diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 27b9ea4f2..14cf04f07 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -1,10 +1,86 @@ package com.kaltura.playkit.player +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.kaltura.android.exoplayer2.C +import com.kaltura.android.exoplayer2.RendererCapabilities +import com.kaltura.android.exoplayer2.source.MediaSource +import com.kaltura.android.exoplayer2.source.TrackGroupArray +import com.kaltura.android.exoplayer2.trackselection.DefaultTrackSelector +import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray +import com.kaltura.android.exoplayer2.trackselection.TrackSelector +import com.kaltura.android.exoplayer2.trackselection.TrackSelectorResult +import com.kaltura.android.exoplayer2.upstream.BandwidthMeter +import com.kaltura.testhelper.FakeRendererCapabilities +import com.kaltura.testhelper.FakeTimeline +import com.kaltura.testhelper.TestUtils +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock + +@RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { -// -// @InjectMocks -// var trackSelectionHelper: TrackSelectionHelper? = null + private lateinit var trackSelectionHelper: TrackSelectionHelper + private lateinit var selector: TrackSelector + private val lastSelectedTrackIds = arrayOf(TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE) + + private lateinit var invalidationListener: TrackSelector.InvalidationListener + private lateinit var bandwidthMeter: BandwidthMeter + private lateinit var playerSettings: PlayerSettings + private lateinit var trackGroupArray: TrackGroupArray + + private val VIDEO_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_VIDEO) + private val AUDIO_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_AUDIO) + private val TEXT_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_TEXT) + private val METADATA_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_METADATA) + private val CAMERA_MOTION_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_CAMERA_MOTION) + private val RENDERER_CAPABILITIES: Array = + arrayOf(VIDEO_CAPABILITIES, + AUDIO_CAPABILITIES, + TEXT_CAPABILITIES, + METADATA_CAPABILITIES, + CAMERA_MOTION_CAPABILITIES) + + @Before + fun setup() { + invalidationListener = mock(TrackSelector.InvalidationListener::class.java) + bandwidthMeter = mock(BandwidthMeter::class.java) + playerSettings = PlayerSettings() + `when`(bandwidthMeter.bitrateEstimate).thenReturn(100000L) + + trackGroupArray = TestUtils.manifestParsingSetup( + ApplicationProvider.getApplicationContext(), + "testdata/mpd/sample_mpd_clear_h264_tears") + + buildTrackSelector() + + trackSelectionHelper = TrackSelectionHelper( + ApplicationProvider.getApplicationContext(), + selector as DefaultTrackSelector, + lastSelectedTrackIds) + trackSelectionHelper.applyPlayerSettings(playerSettings) + } + + @Test + fun buildTracks_mappedTrackInfo() { + assertTrue(trackSelectionHelper.prepareTracks(TrackSelectionArray(), null)) + } + + private fun buildTrackSelector() { + val timeline = FakeTimeline() + selector = DefaultTrackSelector(ApplicationProvider.getApplicationContext() as Context) + selector.init(invalidationListener, bandwidthMeter) + val trackSelectorResult: TrackSelectorResult = selector.selectTracks(RENDERER_CAPABILITIES, + trackGroupArray, + MediaSource.MediaPeriodId(timeline.getUidOfPeriod(0)), + FakeTimeline()) + selector.onSelectionActivated(trackSelectorResult.info) + } } \ No newline at end of file From 11607ee44d0e8553f6afc6d0eda0224cd860f41a Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Tue, 18 May 2021 19:27:46 +0530 Subject: [PATCH 12/33] - updated travis file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1896f0fbe..fe6cb40c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ android: before_script: - curl https://kaltura.github.io/fe-tools/android/license.sh | sh script: - - ./gradlew test + - ./gradlew playkit:test - ./gradlew playkit:build after_failure: - cat playkit/build/reports/lint-results.xml From 685b09bbfd613b5a8bdacfcad6f4fcce3ca5bd76 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 19 May 2021 11:31:38 +0530 Subject: [PATCH 13/33] - ExoPlayerWrapper and TrackSelection test --- .../java/com/kaltura/testhelper/TestUtils.kt | 14 +++-- .../playkit/player/ExoPlayerWrapperTest.kt | 55 +++++++++++++------ .../player/TrackSelectionHelperTest.kt | 4 +- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt index fba48a1f8..9bdfbf24f 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -8,14 +8,13 @@ import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser -import java.io.FileNotFoundException import java.io.IOException import java.io.InputStream import kotlin.jvm.Throws open class TestUtils { companion object { - fun parseLocalManifest(context: Context, fileName: String): DashManifest { + fun parseLocalDashManifest(context: Context, fileName: String): DashManifest { val inputStream: InputStream = getInputStream(context, fileName) return DashManifestParser().parse(Uri.EMPTY, inputStream) } @@ -26,8 +25,8 @@ open class TestUtils { } @Throws(IOException::class) - fun manifestParsingSetup(context: Context, fileName: String): TrackGroupArray { - val dashManifest: DashManifest = parseLocalManifest(context, fileName) + fun getTrackGroupArrayFromDashManifest(context: Context, fileName: String): TrackGroupArray { + val dashManifest: DashManifest = parseLocalDashManifest(context, fileName) val adaptationSets: List = dashManifest.getPeriod(0).adaptationSets val trackGroups: ArrayList = arrayListOf() @@ -43,5 +42,10 @@ open class TestUtils { return TrackGroupArray(*trackGroups.toTypedArray()) } + @Throws(IOException::class) + fun getTrackGroupArrayFromHLSManifest(context: Context, fileName: String) { + //TODO: Parsing for HLS Manifest + } + } -} \ No newline at end of file +} diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 916f35e34..1c8647daa 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -6,8 +6,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray -import com.kaltura.playkit.PKRequestConfiguration -import com.kaltura.playkit.PKWakeMode +import com.kaltura.playkit.* import com.kaltura.testhelper.TestUtils import org.junit.Assert.assertThrows import org.junit.Before @@ -24,27 +23,23 @@ internal class ExoPlayerWrapperTest { private var exoPlayerView: BaseExoplayerView? = null private var rootPlayerView: PlayerView? = null private var eventListener: PlayerEngine.EventListener? = null + private val playerSettings: PlayerSettings = PlayerSettings() - private lateinit var exoPlayerWrapper: ExoPlayerWrapper + private lateinit var realExoPlayerWrapper: ExoPlayerWrapper + private lateinit var mockExoPlayerWrapper: ExoPlayerWrapper private lateinit var trackSelectionHelper: TrackSelectionHelper private lateinit var context: Context - private lateinit var playerSettings: PlayerSettings private lateinit var emptyTrackSelectionArray: TrackSelectionArray private lateinit var trackGroupArray: TrackGroupArray @Before fun setUp() { context = ApplicationProvider.getApplicationContext() - playerSettings = mock(PlayerSettings::class.java, RETURNS_DEEP_STUBS) exoPlayerView = mock(BaseExoplayerView::class.java) rootPlayerView = mock(PlayerView::class.java) eventListener = mock(PlayerEngine.EventListener::class.java) trackSelectionHelper = mock(TrackSelectionHelper::class.java) - - `when`(playerSettings.abrSettings?.initialBitrateEstimate).thenReturn(0) - `when`(playerSettings.pkRequestConfiguration).thenReturn(PKRequestConfiguration()) - `when`(playerSettings.wakeMode).thenReturn(PKWakeMode.NONE) - `when`(playerSettings.aspectRatioResizeMode).thenReturn(PKAspectRatioResizeMode.fit) + mockExoPlayerWrapper = mock(ExoPlayerWrapper::class.java) prepareClassInstances() @@ -53,14 +48,21 @@ internal class ExoPlayerWrapperTest { @Test @Throws(NullPointerException::class) - fun onTrackChanged_TrackSelectionHelperException() { - assertThrows(NullPointerException::class.java, ThrowingRunnable { exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + fun onTrackChanged_TrackSelectionHelper_Exception() { + assertThrows(NullPointerException::class.java, ThrowingRunnable { realExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) }) + } + + @Test + fun load_flow() { + val pkMediaSourceConfig = createMediaSourceConfig() + mockExoPlayerWrapper.load(pkMediaSourceConfig) + verify(mockExoPlayerWrapper).load(pkMediaSourceConfig) } @Test fun onTrackChanged_TrackSelectionHelper_Working() { - exoPlayerWrapper.load(mock(PKMediaSourceConfig::class.java)) - val spiedExoPlayerWrapper = spy(exoPlayerWrapper) + realExoPlayerWrapper.load(mock(PKMediaSourceConfig::class.java)) + val spiedExoPlayerWrapper = spy(realExoPlayerWrapper) //exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) `when`(spiedExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)).then(Answers.RETURNS_SMART_NULLS) //doNothing().`when`(spiedExoPlayerWrapper).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) @@ -69,9 +71,28 @@ internal class ExoPlayerWrapperTest { private fun prepareClassInstances() { emptyTrackSelectionArray = TrackSelectionArray() - trackGroupArray = TestUtils.manifestParsingSetup(ApplicationProvider.getApplicationContext(), + trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest(ApplicationProvider.getApplicationContext(), "testdata/mpd/sample_mpd_switching_property") - exoPlayerWrapper = ExoPlayerWrapper(context, exoPlayerView, playerSettings, rootPlayerView) - exoPlayerWrapper.setEventListener(eventListener) + realExoPlayerWrapper = ExoPlayerWrapper(context, exoPlayerView, playerSettings, rootPlayerView) + realExoPlayerWrapper.setEventListener(eventListener) + } + + private fun createMediaSourceConfig(): PKMediaSourceConfig { + val mediaSourceList: ArrayList = arrayListOf() + val mediaSource = PKMediaSource() + val mediaEntry = PKMediaEntry() + val mediaConfig = PKMediaConfig() + + mediaSource.url = "https://testMedia/playlist.m3u8" + mediaSource.mediaFormat = null + + mediaEntry.duration = 0 + + mediaSourceList.add(mediaSource) + mediaEntry.sources = mediaSourceList + + mediaConfig.mediaEntry = mediaEntry + + return PKMediaSourceConfig(mediaConfig, mediaSource, playerSettings) } } diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 14cf04f07..d04c7cc5e 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -54,7 +54,7 @@ internal class TrackSelectionHelperTest { playerSettings = PlayerSettings() `when`(bandwidthMeter.bitrateEstimate).thenReturn(100000L) - trackGroupArray = TestUtils.manifestParsingSetup( + trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest( ApplicationProvider.getApplicationContext(), "testdata/mpd/sample_mpd_clear_h264_tears") @@ -83,4 +83,4 @@ internal class TrackSelectionHelperTest { selector.onSelectionActivated(trackSelectorResult.info) } -} \ No newline at end of file +} From bb303db26d0e211a8cf992e43271d982386247cf Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Wed, 26 May 2021 22:50:54 +0530 Subject: [PATCH 14/33] - Added more TrackSelectionHelper tests --- .../playkit/player/ExoPlayerWrapper.java | 2 +- .../playkit/player/TrackSelectionHelper.java | 107 ++++++++-------- .../java/com/kaltura/testhelper/TestUtils.kt | 10 ++ .../sample_mpd_custom_manifest_image_tracks | 35 +++++ ...e_mpd_custom_manifest_without_image_tracks | 26 ++++ .../assets/testdata/mpd/sample_mpd_no_tracks | 6 + .../testdata/mpd/sample_mpd_with_subtitle | 45 +++++++ .../playkit/player/ExoPlayerWrapperTest.kt | 14 +- .../player/TrackSelectionHelperTest.kt | 121 +++++++++++++++--- 9 files changed, 286 insertions(+), 80 deletions(-) create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks create mode 100644 playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle diff --git a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java index 1ac9b7797..121a227fa 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java @@ -983,7 +983,7 @@ public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray tra shouldGetTracksInfo = !trackSelectionHelper.prepareTracks(trackSelections, customDashManifest); } } - + //FIXME: Unit Test trackSelectionHelper can be null here trackSelectionHelper.notifyAboutTrackChange(trackSelections); } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index 763e94236..fb63fdca5 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -223,7 +223,7 @@ boolean prepareTracks(TrackSelectionArray trackSelections, CustomDashManifest cu * Actually build {@link PKTracks} object, based on the loaded manifest into Exoplayer. * This method knows how to filter unsupported/unknown formats, and create adaptive option when this is possible. */ - private PKTracks buildTracks(List rawImageTracks) { + PKTracks buildTracks(List rawImageTracks) { clearTracksLists(); @@ -327,6 +327,7 @@ private PKTracks buildTracks(List rawImageTracks) { for (int trackIndex = 0; trackIndex < rawImageTracks.size(); trackIndex++) { CustomFormat imageFormat = rawImageTracks.get(trackIndex); CustomFormat.FormatThumbnailInfo formatThumbnailInfo = imageFormat.formatThumbnailInfo; + //FIXME: Unit Test formatThumbnailInfo can be null String uniqueId = getUniqueId(TRACK_TYPE_IMAGE, TRACK_TYPE_IMAGE, trackIndex); imageTracks.add(trackIndex, new DashImageTrack(uniqueId, imageFormat.id, @@ -365,14 +366,14 @@ private PKTracks buildTracks(List rawImageTracks) { return new PKTracks(videoTracks, filteredAudioTracks, textTracks, imageTracks, defaultVideoTrackIndex, defaultAudioTrackIndex, defaultTextTrackIndex, defaultImageTrackIndex); } - private boolean checkTracksUnavailability(MappingTrackSelector.MappedTrackInfo mappedTrackInfo) { + boolean checkTracksUnavailability(MappingTrackSelector.MappedTrackInfo mappedTrackInfo) { return mappedTrackInfo.getTrackGroups(TRACK_TYPE_VIDEO).length == 0 && mappedTrackInfo.getTrackGroups(TRACK_TYPE_AUDIO).length == 0 && mappedTrackInfo.getTrackGroups(TRACK_TYPE_TEXT).length == 0; } @NonNull - private TrackType getTrackType(int rendererIndex) { + TrackType getTrackType(int rendererIndex) { TrackType trackType; switch (rendererIndex) { case TRACK_TYPE_VIDEO: @@ -391,18 +392,18 @@ private TrackType getTrackType(int rendererIndex) { return trackType; } - private String getLanguageFromFormat(Format format) { + String getLanguageFromFormat(Format format) { if (format.language == null) { return LANGUAGE_UNKNOWN; } return format.language; } - private boolean isExternalSubtitle(String language, String sampleMimeType) { + boolean isExternalSubtitle(String language, String sampleMimeType) { return language != null && (language.contains("-" + sampleMimeType) || language.contains("-" + "Unknown")); } - private String getExternalSubtitleLanguage(Format format) { + String getExternalSubtitleLanguage(Format format) { if (format.language != null) { return format.language.substring(0, format.language.indexOf("-")); } else { @@ -410,7 +411,7 @@ private String getExternalSubtitleLanguage(Format format) { } } - private boolean discardTextTrackOnPreference(Format format) { + boolean discardTextTrackOnPreference(Format format) { PKSubtitlePreference subtitlePreference = playerSettings.getSubtitlePreference(); if (subtitlePreference == PKSubtitlePreference.OFF) { return false; @@ -441,7 +442,7 @@ private boolean discardTextTrackOnPreference(Format format) { return false; } - private List filterVideoTracks() { + List filterVideoTracks() { if (videoTracksCodecsMap == null || videoTracksCodecsMap.isEmpty()) { return videoTracks; @@ -484,7 +485,7 @@ private List filterVideoTracks() { return videoTracks; } - private boolean videoCodecsSupportedInHardware() { + boolean videoCodecsSupportedInHardware() { for (PKVideoCodec videoCodecForPlayback : playerSettings.getPreferredVideoCodecSettings().getCodecPriorityList()) { if (!videoTracksCodecsMap.containsKey(videoCodecForPlayback)) { continue; @@ -503,7 +504,7 @@ private boolean videoCodecsSupportedInHardware() { return false; } - private void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { + protected void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { for (Map.Entry> multipleCodecEntry : videoTracksCodecsMap.entrySet()) { for (VideoTrack codecVideoTrack : multipleCodecEntry.getValue()) { if (codecVideoTrack.getCodecName() != null && @@ -523,7 +524,7 @@ private void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) * and hide all the bitrate variants. * if app prefers to allow audio track selection by codec type it will return all available tracks by codec by audio groups */ - private ArrayList filterAdaptiveAudioTracks() { + ArrayList filterAdaptiveAudioTracks() { ArrayList filteredAudioTracks = new ArrayList<>(); AudioTrack audioTrack; @@ -562,7 +563,7 @@ private ArrayList filterAdaptiveAudioTracks() { * Will add this track only if there is at least one text track available. * Selecting this track, will disable the text renderer. */ - private void maybeAddDisabledTextTrack() { + void maybeAddDisabledTextTrack() { if (textTracks.isEmpty()) { return; @@ -579,7 +580,7 @@ private void maybeAddDisabledTextTrack() { * @return - the index of the track that is selected by default or lastSelected track(Depending on the use-case), * or 0 if no default selection is available and no track was previously selected. */ - private int getDefaultTrackIndex(List trackList, String lastSelectedTrackId) { + int getDefaultTrackIndex(List trackList, String lastSelectedTrackId) { int defaultTrackIndex = 0; @@ -635,7 +636,7 @@ private int getDefaultTrackIndex(List trackList, String las * * @param trackGroupArray TrackGroupArray this includes Internal and External Text Formats */ - private void extractTextTracksToMap(TrackGroupArray trackGroupArray) { + void extractTextTracksToMap(TrackGroupArray trackGroupArray) { for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) { TrackGroup trackGroup = trackGroupArray.get(groupIndex); @@ -669,7 +670,7 @@ private void extractTextTracksToMap(TrackGroupArray trackGroupArray) { } } - private int getUpdatedDefaultTrackIndex(List trackList, int defaultTrackIndex) { + int getUpdatedDefaultTrackIndex(List trackList, int defaultTrackIndex) { if (!trackList.isEmpty()) { int trackType = TRACK_TYPE_UNKNOWN; @@ -690,7 +691,7 @@ private int getUpdatedDefaultTrackIndex(List trackList, int return defaultTrackIndex; } - private int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, int defaultTrackIndex) { + int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, int defaultTrackIndex) { if (trackList != null && selectedFormatLanguage != null) { for (int i = 0; i < trackList.size(); i++) { @@ -713,7 +714,7 @@ private int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, String lastSelectedTrackId, int defaultTrackIndex) { + int restoreLastSelectedTrack(List trackList, String lastSelectedTrackId, int defaultTrackIndex) { //If track was previously selected and selection is differed from the default selection apply it. String defaultUniqueId = trackList.get(defaultTrackIndex).getUniqueId(); if (!NONE.equals(lastSelectedTrackId) && !lastSelectedTrackId.equals(defaultUniqueId)) { @@ -735,7 +736,7 @@ private int restoreLastSelectedTrack(List trackList, String * @param groupIndex - the index of the group this adaptive object refer. * @param format - the actual format of the adaptive object. */ - private void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format format) { + void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format format) { String uniqueId = getUniqueId(rendererIndex, groupIndex, TRACK_ADAPTIVE); if (isAdaptive(rendererIndex, groupIndex) && !adaptiveTrackAlreadyExist(uniqueId, rendererIndex)) { switch (rendererIndex) { @@ -764,7 +765,7 @@ private void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format for * @param trackIndex - actual track index. * @return - uniqueId that represent current track. */ - private String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { + String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { return getUniqueIdPrefix(rendererIndex) + rendererIndex + "," @@ -773,7 +774,7 @@ private String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { + getUniqueIdPostfix(rendererIndex, trackIndex); } - private String getUniqueIdPrefix(int rendererIndex) { + String getUniqueIdPrefix(int rendererIndex) { switch (rendererIndex) { case TRACK_TYPE_VIDEO: return VIDEO_PREFIX; @@ -788,7 +789,7 @@ private String getUniqueIdPrefix(int rendererIndex) { } } - private String getUniqueIdPostfix(int rendererIndex, int trackIndex) { + String getUniqueIdPostfix(int rendererIndex, int trackIndex) { switch (rendererIndex) { case TRACK_ADAPTIVE: return ADAPTIVE; @@ -877,7 +878,7 @@ protected void overrideMediaDefaultABR(long minVideoBitrate, long maxVideoBitrat overrideTrack(rendererIndex, override, parametersBuilder); } - private List getVideoTracksUniqueIds() { + List getVideoTracksUniqueIds() { List uniqueIds = new ArrayList<>(); if (videoTracks != null) { Collections.sort(videoTracks); @@ -888,7 +889,7 @@ private List getVideoTracksUniqueIds() { return uniqueIds; } - private List getCodecUniqueIdsWithABR(long minVideoBitrate, long maxVideoBitrate) { + List getCodecUniqueIdsWithABR(long minVideoBitrate, long maxVideoBitrate) { List uniqueIds = new ArrayList<>(); boolean isValidABRRange = true; @@ -935,7 +936,7 @@ private List getCodecUniqueIdsWithABR(long minVideoBitrate, long maxVide return uniqueIds; } - private SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { + SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { if (uniqueIds == null || uniqueIds[0] == null) { throw new IllegalArgumentException("Track selection with uniqueId = null"); } @@ -959,7 +960,7 @@ private SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { } @NonNull - private SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendererIndex, int groupIndex) { + SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendererIndex, int groupIndex) { SelectionOverride override; int[] adaptiveTrackIndexes; List adaptiveTrackIndexesList = new ArrayList<>(); @@ -975,7 +976,7 @@ private SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendere return override; } - private void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, List adaptiveTrackIndexesList) { + void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, List adaptiveTrackIndexesList) { int trackIndex; int trackGroupIndex; @@ -992,7 +993,7 @@ private void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, Lis } - private int[][] validateAndBuildUniqueIds(List uniqueIds) { + int[][] validateAndBuildUniqueIds(List uniqueIds) { int [][] idsList = new int [uniqueIds.size()][TRACK_RENDERERS_AMOUNT]; for (int index = 0 ; index < idsList.length ; index++) { int[] uniqueTrackId = validateUniqueId(uniqueIds.get(index)); @@ -1005,7 +1006,7 @@ private int[][] validateAndBuildUniqueIds(List uniqueIds) { * @param uniqueId - the uniqueId to convert. * @return - int[] that consist from indexes that are readable to Exoplayer. */ - private int[] parseUniqueId(String uniqueId) { + int[] parseUniqueId(String uniqueId) { int[] parsedUniqueId = new int[3]; String splitUniqueId = removePrefix(uniqueId); String[] strArray = splitUniqueId.split(","); @@ -1034,7 +1035,7 @@ private int[] parseUniqueId(String uniqueId) { * @param uniqueId - the unique id of the track that will override the existing one. * @return - the {@link SelectionOverride} which will override the existing selection. */ - private SelectionOverride retrieveOverrideSelection(int[] uniqueId) { + SelectionOverride retrieveOverrideSelection(int[] uniqueId) { SelectionOverride override; @@ -1104,7 +1105,7 @@ private SelectionOverride retrieveOverrideSelection(int[] uniqueId) { } @NonNull - private SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupIndex) { + SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupIndex) { SelectionOverride override;List adaptiveTrackIndexesList = new ArrayList<>(); int[] adaptiveTrackIndexes; @@ -1156,7 +1157,7 @@ private SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupInde * @param rendererIndex - renderer index on which we want to apply the change. * @param override - the new selection with which we want to override the currently active track. */ - private void overrideTrack(int rendererIndex, SelectionOverride override, DefaultTrackSelector.ParametersBuilder parametersBuilder) { + void overrideTrack(int rendererIndex, SelectionOverride override, DefaultTrackSelector.ParametersBuilder parametersBuilder) { if (override != null) { //actually change track. TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); @@ -1231,7 +1232,7 @@ public ThumbnailInfo getThumbnailInfo(long positionMS) { * @param rendererIndex - renderer index. * @return - true, if adaptive {@link BaseTrack} object already exist for this group. */ - private boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { + boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { List trackList = new ArrayList<>(); switch (rendererIndex) { @@ -1258,7 +1259,7 @@ private boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { return false; } - private int getIndexFromUniqueId(String uniqueId, int groupIndex) { + int getIndexFromUniqueId(String uniqueId, int groupIndex) { String uniqueIdWithoutPrefix = removePrefix(uniqueId); String[] strArray = uniqueIdWithoutPrefix.split(","); if (strArray[groupIndex].equals(ADAPTIVE)) { @@ -1268,13 +1269,13 @@ private int getIndexFromUniqueId(String uniqueId, int groupIndex) { return Integer.valueOf(strArray[groupIndex]); } - private String removePrefix(String uniqueId) { + String removePrefix(String uniqueId) { String[] strArray = uniqueId.split(":"); //always return the second element of the splitString. return strArray[1]; } - private int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) { + int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) { int[] adaptiveTrackIndexes = new int[adaptiveTrackIndexesList.size()]; for (int i = 0; i < adaptiveTrackIndexes.length; i++) { adaptiveTrackIndexes[i] = adaptiveTrackIndexesList.get(i); @@ -1283,12 +1284,12 @@ private int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) return adaptiveTrackIndexes; } - private boolean isFormatSupported(int rendererCount, int groupIndex, int trackIndex) { + boolean isFormatSupported(int rendererCount, int groupIndex, int trackIndex) { return mappedTrackInfo.getTrackSupport(rendererCount, groupIndex, trackIndex) == Consts.FORMAT_HANDLED; } - private boolean isAdaptive(int rendererIndex, int groupIndex) { + boolean isAdaptive(int rendererIndex, int groupIndex) { TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex); return mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED @@ -1302,7 +1303,7 @@ private boolean isAdaptive(int rendererIndex, int groupIndex) { * @return - parsed uniqueId in case of success. * @throws IllegalArgumentException when uniqueId is illegal. */ - private int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { + int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { if (uniqueId == null) { throw new IllegalArgumentException("uniqueId is null"); @@ -1331,7 +1332,7 @@ private int[] validateUniqueId(String uniqueId) throws IllegalArgumentException throw new IllegalArgumentException("Invalid structure of uniqueId " + uniqueId); } - private boolean isTrackIndexValid(int[] parsedUniqueId) { + boolean isTrackIndexValid(int[] parsedUniqueId) { int rendererIndex = parsedUniqueId[RENDERER_INDEX]; int groupIndex = parsedUniqueId[GROUP_INDEX]; int trackIndex = parsedUniqueId[TRACK_INDEX]; @@ -1350,7 +1351,7 @@ private boolean isTrackIndexValid(int[] parsedUniqueId) { && trackIndex < mappedTrackInfo.getTrackGroups(rendererIndex).get(groupIndex).length; } - private boolean isGroupIndexValid(int[] parsedUniqueId) { + boolean isGroupIndexValid(int[] parsedUniqueId) { if (parsedUniqueId[GROUP_INDEX] == TRACK_TYPE_IMAGE) { return true; } @@ -1359,14 +1360,14 @@ private boolean isGroupIndexValid(int[] parsedUniqueId) { && parsedUniqueId[GROUP_INDEX] < mappedTrackInfo.getTrackGroups(parsedUniqueId[RENDERER_INDEX]).length; } - private boolean isRendererTypeValid(int rendererIndex) { + boolean isRendererTypeValid(int rendererIndex) { return rendererIndex >= TRACK_TYPE_VIDEO && rendererIndex <= TRACK_TYPE_IMAGE; } /** * Notify to log, that video/audio renderer has only unsupported tracks. */ - private void warnAboutUnsupportedRendererTypes() { + void warnAboutUnsupportedRendererTypes() { boolean videoTrackUnsupported = false; boolean audioTrackUnsupported = false; String errorMessage; @@ -1392,7 +1393,7 @@ private void warnAboutUnsupportedRendererTypes() { } } - private PKError getUnsupportedTrackError(String errorMessage) { + PKError getUnsupportedTrackError(String errorMessage) { return new PKError(PKPlayerErrorType.UNEXPECTED, PKError.Severity.Recoverable, errorMessage, new IllegalStateException(errorMessage)); } @@ -1404,7 +1405,7 @@ protected void setTracksErrorListener(TracksErrorListener tracksErrorListener) { this.tracksErrorListener = tracksErrorListener; } - private void clearTracksLists() { + void clearTracksLists() { videoTracks.clear(); audioTracks.clear(); textTracks.clear(); @@ -1474,7 +1475,7 @@ protected long getCurrentVideoHeight() { return -1; } - private ExoTrackSelection getTrackSelection(TrackSelection trackSelection) { + ExoTrackSelection getTrackSelection(TrackSelection trackSelection) { if (trackSelection instanceof ExoTrackSelection) { return (ExoTrackSelection) trackSelection; } @@ -1507,7 +1508,7 @@ protected void notifyAboutTrackChange(TrackSelectionArray trackSelections) { } } - private boolean shouldNotifyAboutTrackChanged(int renderType) { + boolean shouldNotifyAboutTrackChanged(int renderType) { return !requestedChangeTrackIds[renderType].equals(lastSelectedTrackIds[renderType]); } @@ -1591,7 +1592,7 @@ protected String getPreferredTrackId(int trackType) { } @Nullable - private String getPreferredTextTrackUniqueId(int trackType) { + String getPreferredTextTrackUniqueId(int trackType) { if (!isValidPreferredTextConfig()) { return null; } @@ -1637,7 +1638,7 @@ private String getPreferredTextTrackUniqueId(int trackType) { } @Nullable - private String maybeSetFirstTextTrackAsAutoSelection() { + String maybeSetFirstTextTrackAsAutoSelection() { String preferredTrackUniqueId = null; //if user set mode to AUTO and the locale lang is not in the stream and no default text track in the stream so we will not select None but the first text track in the stream @@ -1657,7 +1658,7 @@ private String maybeSetFirstTextTrackAsAutoSelection() { return preferredTrackUniqueId; } - private String getPreferredAudioTrackUniqueId(int trackType) { + String getPreferredAudioTrackUniqueId(int trackType) { if (!isValidPreferredAudioConfig()) { return null; } @@ -1688,7 +1689,7 @@ private String getPreferredAudioTrackUniqueId(int trackType) { return preferredTrackUniqueId; } - private PKVideoCodec getVideoCodec(Format format) { + PKVideoCodec getVideoCodec(Format format) { String codec = format.codecs; if (codec != null) { if (codec.startsWith("hev1") || codec.startsWith("hvc1")) { @@ -1715,7 +1716,7 @@ private PKVideoCodec getVideoCodec(Format format) { return PKVideoCodec.AVC; //VIDEO_H264 } - private PKAudioCodec getAudioCodec(Format format) { + PKAudioCodec getAudioCodec(Format format) { String codec = format.codecs; if (codec != null) { if (codec.startsWith("mp4a")) { @@ -1731,7 +1732,7 @@ private PKAudioCodec getAudioCodec(Format format) { return PKAudioCodec.AAC; } - private boolean isValidPreferredAudioConfig() { + boolean isValidPreferredAudioConfig() { if (playerSettings == null) { return false; } @@ -1743,7 +1744,7 @@ private boolean isValidPreferredAudioConfig() { (preferredAudioTrackConfig.getPreferredMode() == PKTrackConfig.Mode.SELECTION && preferredAudioTrackConfig.getTrackLanguage() == null)); } - private boolean isValidPreferredTextConfig() { + boolean isValidPreferredTextConfig() { if (playerSettings == null) { return false; } diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt index 9bdfbf24f..a2d263239 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -8,8 +8,10 @@ import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser +import java.io.BufferedReader import java.io.IOException import java.io.InputStream +import java.io.InputStreamReader import kotlin.jvm.Throws open class TestUtils { @@ -19,6 +21,14 @@ open class TestUtils { return DashManifestParser().parse(Uri.EMPTY, inputStream) } + fun getManifestString(context: Context, fileName: String) : String { + val inputStream = getInputStream(context, fileName) + val bufferedReader = BufferedReader(InputStreamReader(inputStream)) + val manifestString = StringBuilder() + bufferedReader.useLines { lines -> lines.forEach { manifestString.append(it)} } + return manifestString.toString() + } + @Throws(IOException::class) fun getInputStream(context: Context, fileName: String): InputStream { return context.resources.assets.open(fileName) diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks new file mode 100644 index 000000000..31be16555 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks @@ -0,0 +1,35 @@ + + ./ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks new file mode 100644 index 000000000..38eee9c95 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks @@ -0,0 +1,26 @@ + + ./ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks b/playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks new file mode 100644 index 000000000..ea8d4a939 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks @@ -0,0 +1,6 @@ + + + + + + diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle b/playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle new file mode 100644 index 000000000..7fec4b4fe --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle @@ -0,0 +1,45 @@ + + + + Media Presentation Description by DASH-IF Live Source Simulator. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index 1c8647daa..c85a4a48a 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -13,7 +13,6 @@ import org.junit.Before import org.junit.Test import org.junit.function.ThrowingRunnable import org.junit.runner.RunWith -import org.mockito.Answers import org.mockito.Mockito.* import org.robolectric.Shadows @@ -54,17 +53,18 @@ internal class ExoPlayerWrapperTest { @Test fun load_flow() { - val pkMediaSourceConfig = createMediaSourceConfig() + val pkMediaSourceConfig = getMediaSourceConfig() mockExoPlayerWrapper.load(pkMediaSourceConfig) verify(mockExoPlayerWrapper).load(pkMediaSourceConfig) } @Test fun onTrackChanged_TrackSelectionHelper_Working() { - realExoPlayerWrapper.load(mock(PKMediaSourceConfig::class.java)) - val spiedExoPlayerWrapper = spy(realExoPlayerWrapper) - //exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) - `when`(spiedExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)).then(Answers.RETURNS_SMART_NULLS) + //FIXME +// realExoPlayerWrapper.load(mock(PKMediaSourceConfig::class.java)) +// val spiedExoPlayerWrapper = spy(realExoPlayerWrapper) +// //exoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray) +// `when`(spiedExoPlayerWrapper.onTracksChanged(trackGroupArray, emptyTrackSelectionArray)).then(Answers.RETURNS_SMART_NULLS) //doNothing().`when`(spiedExoPlayerWrapper).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) // verify(exoPlayerWrapper, times(1)).onTracksChanged(trackGroupArray, emptyTrackSelectionArray) } @@ -77,7 +77,7 @@ internal class ExoPlayerWrapperTest { realExoPlayerWrapper.setEventListener(eventListener) } - private fun createMediaSourceConfig(): PKMediaSourceConfig { + private fun getMediaSourceConfig(): PKMediaSourceConfig { val mediaSourceList: ArrayList = arrayListOf() val mediaSource = PKMediaSource() val mediaEntry = PKMediaEntry() diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index d04c7cc5e..b40933ccc 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -1,10 +1,15 @@ package com.kaltura.playkit.player import android.content.Context +import android.net.Uri import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.kaltura.android.exoplayer2.C +import com.kaltura.android.exoplayer2.Format import com.kaltura.android.exoplayer2.RendererCapabilities +import com.kaltura.android.exoplayer2.dashmanifestparser.CustomDashManifest +import com.kaltura.android.exoplayer2.dashmanifestparser.CustomDashManifestParser +import com.kaltura.android.exoplayer2.dashmanifestparser.CustomFormat import com.kaltura.android.exoplayer2.source.MediaSource import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.trackselection.DefaultTrackSelector @@ -15,19 +20,23 @@ import com.kaltura.android.exoplayer2.upstream.BandwidthMeter import com.kaltura.testhelper.FakeRendererCapabilities import com.kaltura.testhelper.FakeTimeline import com.kaltura.testhelper.TestUtils -import org.junit.Assert.assertTrue +import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito.`when` -import org.mockito.Mockito.mock +import org.mockito.Mockito.* +import kotlin.collections.ArrayList @RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { - private lateinit var trackSelectionHelper: TrackSelectionHelper - private lateinit var selector: TrackSelector + private val initialAssetPath: String = "testdata/mpd/" + private var trackSelectionHelper: TrackSelectionHelper? = null + + private lateinit var actualSelector: TrackSelector + private var mockedSelector: DefaultTrackSelector = mock(DefaultTrackSelector::class.java) + private val lastSelectedTrackIds = arrayOf(TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE) private lateinit var invalidationListener: TrackSelector.InvalidationListener @@ -41,7 +50,7 @@ internal class TrackSelectionHelperTest { private val METADATA_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_METADATA) private val CAMERA_MOTION_CAPABILITIES: RendererCapabilities = FakeRendererCapabilities(C.TRACK_TYPE_CAMERA_MOTION) private val RENDERER_CAPABILITIES: Array = - arrayOf(VIDEO_CAPABILITIES, + arrayOf(VIDEO_CAPABILITIES, AUDIO_CAPABILITIES, TEXT_CAPABILITIES, METADATA_CAPABILITIES, @@ -53,34 +62,108 @@ internal class TrackSelectionHelperTest { bandwidthMeter = mock(BandwidthMeter::class.java) playerSettings = PlayerSettings() `when`(bandwidthMeter.bitrateEstimate).thenReturn(100000L) + } + private fun buildActualTrackSelector(fileName: String): TrackSelector { + val timeline = FakeTimeline() trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest( ApplicationProvider.getApplicationContext(), - "testdata/mpd/sample_mpd_clear_h264_tears") + fileName) + + actualSelector = DefaultTrackSelector(ApplicationProvider.getApplicationContext() as Context) + actualSelector.init(invalidationListener, bandwidthMeter) + + val trackSelectorResult: TrackSelectorResult = actualSelector.selectTracks(RENDERER_CAPABILITIES, + trackGroupArray, + MediaSource.MediaPeriodId(timeline.getUidOfPeriod(0)), + FakeTimeline()) - buildTrackSelector() + actualSelector.onSelectionActivated(trackSelectorResult.info) + return actualSelector + } + + private fun getTrackSelectionHelper(selector: TrackSelector): TrackSelectionHelper { trackSelectionHelper = TrackSelectionHelper( ApplicationProvider.getApplicationContext(), selector as DefaultTrackSelector, lastSelectedTrackIds) - trackSelectionHelper.applyPlayerSettings(playerSettings) + trackSelectionHelper?.applyPlayerSettings(playerSettings) + + return trackSelectionHelper!! } @Test fun buildTracks_mappedTrackInfo() { - assertTrue(trackSelectionHelper.prepareTracks(TrackSelectionArray(), null)) + trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_clear_h264_tears")) + trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + assertTrue(it) + } } - private fun buildTrackSelector() { - val timeline = FakeTimeline() - selector = DefaultTrackSelector(ApplicationProvider.getApplicationContext() as Context) - selector.init(invalidationListener, bandwidthMeter) - val trackSelectorResult: TrackSelectorResult = selector.selectTracks(RENDERER_CAPABILITIES, - trackGroupArray, - MediaSource.MediaPeriodId(timeline.getUidOfPeriod(0)), - FakeTimeline()) + @Test + fun buildTracks_mappedTrackInfo_instream_subtitles() { + val selector: TrackSelector = buildActualTrackSelector("${initialAssetPath}sample_mpd_with_subtitle") + trackSelectionHelper = getTrackSelectionHelper(selector) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper?.prepareTracks(TrackSelectionArray(), null) + verify(spyTrackSelectionHelper)?.buildTracks(ArrayList()) + + val pkTracks: PKTracks? = spyTrackSelectionHelper?.buildTracks(null) + assertEquals(6, pkTracks?.textTracks?.size) + } + + @Test + fun buildTracks_mappedTrackInfo_is_null() { + trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + assertFalse(it) + } + } + + @Test + fun buildTracks_mappedTrackInfo_checkTracksUnavailability_false() { + trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_no_tracks")) + trackSelectionHelper?.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + assertFalse(it) + } + } + + fun buildTracks_warnAboutUnsupportedRendererTypes() { + //TODO + } + + @Test + fun buildTracks_rawImageTracks() { + val dashManifestString: String = TestUtils.getManifestString(ApplicationProvider.getApplicationContext() as Context, "${initialAssetPath}sample_mpd_custom_manifest_image_tracks") + val customDashManifest: CustomDashManifest = CustomDashManifestParser().parse(Uri.parse("http://dash.edgesuite.net/akamai/bbb_30fps/bbb_with_multiple_tiled_thumbnails.mpd"), + dashManifestString) + + trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_custom_manifest_without_image_tracks")) + trackSelectionHelper?.prepareTracks(TrackSelectionArray(), customDashManifest)?.let { + assertTrue(it) + } + } + + @Test + fun getExternalSubtitleLanguage() { + val format = Format.Builder().setLanguage("eng-application/mp4").build(); + trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + assertEquals("en", trackSelectionHelper?.getExternalSubtitleLanguage(format)) + + val isExternalSubtitleformat = format.buildUpon().setLanguage("eng-application/mp4").setSampleMimeType("application/mp4").build() + assertEquals(true, trackSelectionHelper?.isExternalSubtitle(isExternalSubtitleformat.language, isExternalSubtitleformat.sampleMimeType)) + + val nullFormat = format.buildUpon().setLanguage(null).build() + assertNull(trackSelectionHelper?.getExternalSubtitleLanguage(nullFormat)) + } - selector.onSelectionActivated(trackSelectorResult.info) + @Test(expected = StringIndexOutOfBoundsException::class) + fun getExternalSubtitleLanguage_null_exception() { + val format = Format.Builder().build(); + trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + val nullExternalSubtitleformat = format.buildUpon().setLanguage("engapplication/mp4").setSampleMimeType("application/mp4").build() + trackSelectionHelper?.getExternalSubtitleLanguage(nullExternalSubtitleformat) } } From aabd6dc9434d4e3ea490f4cb163050b9f68b5ee5 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 27 May 2021 17:36:26 +0530 Subject: [PATCH 15/33] - Added HLS parsing on local manifest --- .../java/com/kaltura/testhelper/TestUtils.kt | 39 ++++++- .../assets/testdata/m3u8/sample_hls_harold | 12 ++ ...ter => sample_hls_live_low_latency_master} | 0 .../testdata/m3u8/sample_hls_multi_lang | 104 ++++++++++++++++++ .../testdata/m3u8/sample_hls_short_sintel | 5 + ...264_tears => sample_dash_clear_h264_tears} | 0 ... sample_dash_custom_manifest_image_tracks} | 0 ...dash_custom_manifest_without_image_tracks} | 0 ...le_mpd_no_tracks => sample_dash_no_tracks} | 0 ...roperty => sample_dash_switching_property} | 0 ...ith_subtitle => sample_dash_with_subtitle} | 0 .../playkit/player/ExoPlayerWrapperTest.kt | 2 +- .../player/TrackSelectionHelperTest.kt | 79 ++++++++----- 13 files changed, 210 insertions(+), 31 deletions(-) create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_harold rename playkit/src/test/assets/testdata/m3u8/{live_low_latency_master => sample_hls_live_low_latency_master} (100%) create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_multi_lang create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_short_sintel rename playkit/src/test/assets/testdata/mpd/{sample_mpd_clear_h264_tears => sample_dash_clear_h264_tears} (100%) rename playkit/src/test/assets/testdata/mpd/{sample_mpd_custom_manifest_image_tracks => sample_dash_custom_manifest_image_tracks} (100%) rename playkit/src/test/assets/testdata/mpd/{sample_mpd_custom_manifest_without_image_tracks => sample_dash_custom_manifest_without_image_tracks} (100%) rename playkit/src/test/assets/testdata/mpd/{sample_mpd_no_tracks => sample_dash_no_tracks} (100%) rename playkit/src/test/assets/testdata/mpd/{sample_mpd_switching_property => sample_dash_switching_property} (100%) rename playkit/src/test/assets/testdata/mpd/{sample_mpd_with_subtitle => sample_dash_with_subtitle} (100%) diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt index a2d263239..10d3f4225 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -8,11 +8,12 @@ import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser +import com.kaltura.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist +import com.kaltura.android.exoplayer2.source.hls.playlist.HlsPlaylistParser import java.io.BufferedReader import java.io.IOException import java.io.InputStream import java.io.InputStreamReader -import kotlin.jvm.Throws open class TestUtils { companion object { @@ -21,6 +22,11 @@ open class TestUtils { return DashManifestParser().parse(Uri.EMPTY, inputStream) } + fun parseLocalHlsManifest(context: Context, fileName: String): HlsMasterPlaylist { + val inputStream: InputStream = getInputStream(context, fileName) + return (HlsPlaylistParser().parse(Uri.EMPTY, inputStream)) as HlsMasterPlaylist + } + fun getManifestString(context: Context, fileName: String) : String { val inputStream = getInputStream(context, fileName) val bufferedReader = BufferedReader(InputStreamReader(inputStream)) @@ -53,8 +59,35 @@ open class TestUtils { } @Throws(IOException::class) - fun getTrackGroupArrayFromHLSManifest(context: Context, fileName: String) { - //TODO: Parsing for HLS Manifest + fun getTrackGroupArrayFromHlsManifest(context: Context, fileName: String): TrackGroupArray { + val hlsMasterPlaylist = parseLocalHlsManifest(context, fileName) + val trackGroups: ArrayList = arrayListOf() + val videoFormat: ArrayList = arrayListOf() + val audioFormat: ArrayList = arrayListOf() + val subtitleFormat: ArrayList = arrayListOf() + + if (hlsMasterPlaylist.variants.isNotEmpty()) { + for ((variantIndex, variantValue) in hlsMasterPlaylist.variants.withIndex()) { + videoFormat.add(variantValue.format) + } + trackGroups.add(TrackGroup(*videoFormat.toTypedArray())) + } + + if (hlsMasterPlaylist.audios.isNotEmpty()) { + for ((audiosIndex, audiosValue) in hlsMasterPlaylist.audios.withIndex()) { + audioFormat.add(audiosValue.format) + } + trackGroups.add(TrackGroup(*audioFormat.toTypedArray())) + } + + if (hlsMasterPlaylist.subtitles.isNotEmpty()) { + for ((subtitlesIndex, subtitlesValue) in hlsMasterPlaylist.subtitles.withIndex()) { + subtitleFormat.add(subtitlesValue.format) + } + trackGroups.add(TrackGroup(*subtitleFormat.toTypedArray())) + } + + return TrackGroupArray(*trackGroups.toTypedArray()) } } diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_harold b/playkit/src/test/assets/testdata/m3u8/sample_hls_harold new file mode 100644 index 000000000..a157d85a9 --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_harold @@ -0,0 +1,12 @@ +#EXTM3U +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_2z8njt8w/segmentDuration/300/ks/MWZhNWY3ZjlhOTU4ZWMzYzcwM2MxNmZjYTFhZTQyZjFhYjdmNWEyZXwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4MTc7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="French",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="fr",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_gtwqks49/segmentDuration/300/ks/NjQ4YTdlMDMzMjhjOTBhYjdmZGQ5MzExYzUyNDJlZGFmNzMxYmJiM3wyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4Mjc7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Spanish",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="es",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_hjfu39fx/segmentDuration/300/ks/Mjc2MGZiZDZhNjE1YzI3YTFkMjViODFiMmVjZjRlZDA2ZjY3ZWMzMnwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4MzU7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Arabic",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ar",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_qjgojnhs/segmentDuration/300/ks/M2RmNWViOTkxYjIwZjcxOGVhOGM2ZTQxZDY1ZTBmOGJhYzVmYjYxYnwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4NDM7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Dutch",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="nl",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_jf4xx36w/segmentDuration/300/ks/MDU2MDY0MTk3YWYzYTlhN2Y2NGM2NWUzZjI4NWQ5ZDZjNDNjZGRmMnwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4NTI7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Japanese",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ja",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_flsdft0e/segmentDuration/300/ks/ZGE0YTkzYTM4ZWJkYTQ4ODVlODUwYzFlOWQ2ZDU2NmE5NTgxZjRhNHwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4Njs7Y2FwdGlvbjowX3VrYTFtc2c0LGRpc2FibGVlbnRpdGxlbWVudGZvcmVudHJ5OjBfdWthMW1zZzQsdXJpcmVzdHJpY3Q6L2FwaV92My9pbmRleC5waHAvc2VydmljZS9jYXB0aW9uX2NhcHRpb25hc3NldC9hY3Rpb24vc2VydmVXZWJWVFQqOzs=/a.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Russian",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ru",URI="http://cfvod.kaltura.com/api_v3/index.php/service/caption_captionasset/action/serveWebVTT/captionAssetId/1_w6zxhvm7/segmentDuration/300/ks/ZWE1MWIwOTJiMDUyOWZiZDkzYTIxNGQ0MjRkNDhjZmM1YjFkYTIxNXwyNDMzNDI7MjQzMzQyOzE2MjIxODM1ODk7MDsxNjIyMDk3MTg5LjY4Njg7O2NhcHRpb246MF91a2ExbXNnNCxkaXNhYmxlZW50aXRsZW1lbnRmb3JlbnRyeTowX3VrYTFtc2c0LHVyaXJlc3RyaWN0Oi9hcGlfdjMvaW5kZXgucGhwL3NlcnZpY2UvY2FwdGlvbl9jYXB0aW9uYXNzZXQvYWN0aW9uL3NlcnZlV2ViVlRUKjs7/a.m3u8" +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=437304,RESOLUTION=480x352,SUBTITLES="subs" +http://cfvod.kaltura.com/scf/hls/p/243342/sp/24334200/serveFlavor/entryId/0_uka1msg4/v/1/pv/2/flavorId/1_vqhfu6uy/name/a.mp4/index.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2Nmdm9kLmthbHR1cmEuY29tL3NjZi9obHMvcC8yNDMzNDIvc3AvMjQzMzQyMDAvc2VydmVGbGF2b3IvZW50cnlJZC8wX3VrYTFtc2c0L3YvMS9wdi8yKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYyMjE4MzU4OX19fV19&Signature=glnACGQWH2q6QmRf5ZoK0RW455PDPjG2Nu3Y7Bsuas9Alw-I4DgEshcUCyLKjRpqAS4O0-AbVk4ILIc7wFgsQNU4T2NXGgQEG0UtcHyv2lCb6P5ekaDEXkrTLzUYcbXD7GP5oe41usBq11cXoRhEwRXGvOWcFL52CyILxhr~mTYhOMOvKqjH5ovgHClD1D-WJAkUlgoXHa5p~vhcf0XU0~JEAUuvuz9aEsOcjy3-fC7D5gq566V57f17CbPUND8q~a7lRPS3JN0zm5rsMAS1etluu8v33-0Qe0i5HmdslimCxuVN-ZKKdwODTh~I7UDc~NiZcQE6HCvvmRxj2h4rlQ__&Key-Pair-Id=APKAJT6QIWSKVYK3V34A +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=850577,RESOLUTION=480x352,SUBTITLES="subs" +http://cfvod.kaltura.com/scf/hls/p/243342/sp/24334200/serveFlavor/entryId/0_uka1msg4/v/1/pv/2/flavorId/1_80sohj7p/name/a.mp4/index.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2Nmdm9kLmthbHR1cmEuY29tL3NjZi9obHMvcC8yNDMzNDIvc3AvMjQzMzQyMDAvc2VydmVGbGF2b3IvZW50cnlJZC8wX3VrYTFtc2c0L3YvMS9wdi8yKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYyMjE4MzU4OX19fV19&Signature=glnACGQWH2q6QmRf5ZoK0RW455PDPjG2Nu3Y7Bsuas9Alw-I4DgEshcUCyLKjRpqAS4O0-AbVk4ILIc7wFgsQNU4T2NXGgQEG0UtcHyv2lCb6P5ekaDEXkrTLzUYcbXD7GP5oe41usBq11cXoRhEwRXGvOWcFL52CyILxhr~mTYhOMOvKqjH5ovgHClD1D-WJAkUlgoXHa5p~vhcf0XU0~JEAUuvuz9aEsOcjy3-fC7D5gq566V57f17CbPUND8q~a7lRPS3JN0zm5rsMAS1etluu8v33-0Qe0i5HmdslimCxuVN-ZKKdwODTh~I7UDc~NiZcQE6HCvvmRxj2h4rlQ__&Key-Pair-Id=APKAJT6QIWSKVYK3V34A \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/m3u8/live_low_latency_master b/playkit/src/test/assets/testdata/m3u8/sample_hls_live_low_latency_master similarity index 100% rename from playkit/src/test/assets/testdata/m3u8/live_low_latency_master rename to playkit/src/test/assets/testdata/m3u8/sample_hls_live_low_latency_master diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_lang b/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_lang new file mode 100644 index 000000000..c590aad62 --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_lang @@ -0,0 +1,104 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_chi/prog_index.m3u8" + + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6303166,BANDWIDTH=11265828,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=4750392,BANDWIDTH=7979513,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3202998,BANDWIDTH=5134820,CODECS="avc1.4d001f,ec-3",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2196234,BANDWIDTH=3305648,CODECS="avc1.4d001f,ec-3",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1259726,BANDWIDTH=1918208,CODECS="avc1.4d001e,ec-3",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=884732,BANDWIDTH=1270234,CODECS="avc1.42001e,ec-3",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=503255,BANDWIDTH=669126,CODECS="avc1.42e015,ec-3",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=229236,BANDWIDTH=271811,CODECS="avc1.42e00b,ec-3",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_192k/prog_index.m3u8 + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6305791,BANDWIDTH=11204864,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=4753017,BANDWIDTH=7981552,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3205623,BANDWIDTH=5137859,CODECS="avc1.4d001f,mp4a.40.2",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2198859,BANDWIDTH=3308768,CODECS="avc1.4d001f,mp4a.40.2",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1262351,BANDWIDTH=1921247,CODECS="avc1.4d001e,mp4a.40.2",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=887357,BANDWIDTH=1273273,CODECS="avc1.42001e,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=505880,BANDWIDTH=672165,CODECS="avc1.42e015,mp4a.40.2",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=231861,BANDWIDTH=274850,CODECS="avc1.42e00b,mp4a.40.2",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac" +video_192k/prog_index.m3u8 + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6367166,BANDWIDTH=11265828,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=4814392,BANDWIDTH=8042516,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3266998,BANDWIDTH=5198823,CODECS="avc1.4d001f,ec-3",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2260234,BANDWIDTH=3369651,CODECS="avc1.4d001f,ec-3",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1332726,BANDWIDTH=1982211,CODECS="avc1.4d001e,ec-3",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=948732,BANDWIDTH=1334237,CODECS="avc1.42001e,ec-3",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=567255,BANDWIDTH=733129,CODECS="avc1.42e015,ec-3",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=293236,BANDWIDTH=335814,CODECS="avc1.42e00b,ec-3",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-ec3-51",CLOSED-CAPTIONS=NONE,AUDIO="ec3" +video_192k/prog_index.m3u8 + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6559166,BANDWIDTH=11457828,CODECS="avc1.640028,ac-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=5006392,BANDWIDTH=8234516,CODECS="avc1.640028,ac-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3458998,BANDWIDTH=5390823,CODECS="avc1.4d001f,ac-3",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2452234,BANDWIDTH=3561651,CODECS="avc1.4d001f,ac-3",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1515726,BANDWIDTH=2174211,CODECS="avc1.4d001e,ac-3",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1140732,BANDWIDTH=1526237,CODECS="avc1.42001e,ac-3",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=759255,BANDWIDTH=925129,CODECS="avc1.42e015,ac-3",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=485236,BANDWIDTH=527814,CODECS="avc1.42e00b,ac-3",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-ac3",CLOSED-CAPTIONS=NONE,AUDIO="ac3" +video_192k/prog_index.m3u8 + +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-20",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/ec3_20_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-20",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="chi",URI="subtitles/ec3_20_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-20",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="spa",URI="subtitles/ec3_20_eng/subs.m3u8" + +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-aac",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/aac_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-aac",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="chi",URI="subtitles/aac_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-aac",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="spa",URI="subtitles/aac_eng/subs.m3u8" + +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-51",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/ec3_51_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-51",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="chi",URI="subtitles/ec3_51_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-51",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="spa",URI="subtitles/ec3_51_eng/subs.m3u8" + +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ac3",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/ac3_51_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ac3",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="chi",URI="subtitles/ac3_51_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ac3",NAME="English",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="spa",URI="subtitles/ac3_51_eng/subs.m3u8" diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_short_sintel b/playkit/src/test/assets/testdata/m3u8/sample_hls_short_sintel new file mode 100644 index 000000000..4109a9608 --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_short_sintel @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=460259,RESOLUTION=480x256,FRAME-RATE=24.000,CODECS="avc1.42c01e,mp4a.40.2" +http://cfvod.kaltura.com/hls/p/2215841/sp/221584100/serveFlavor/entryId/1_9bwuo813/v/1/ev/1/flavorId/1_5j0bgx4v/name/a.mp4/index.m3u8/index-v1-a1.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=629957,RESOLUTION=480x256,FRAME-RATE=24.000,CODECS="avc1.4d401f,mp4a.40.2" +http://cfvod.kaltura.com/hls/p/2215841/sp/221584100/serveFlavor/entryId/1_9bwuo813/v/1/ev/1/flavorId/1_zj4vzg46/name/a.mp4/index.m3u8/index-v1-a1.m3u8 diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears b/playkit/src/test/assets/testdata/mpd/sample_dash_clear_h264_tears similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_clear_h264_tears rename to playkit/src/test/assets/testdata/mpd/sample_dash_clear_h264_tears diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_image_tracks rename to playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_custom_manifest_without_image_tracks rename to playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_no_tracks similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_no_tracks rename to playkit/src/test/assets/testdata/mpd/sample_dash_no_tracks diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property b/playkit/src/test/assets/testdata/mpd/sample_dash_switching_property similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_switching_property rename to playkit/src/test/assets/testdata/mpd/sample_dash_switching_property diff --git a/playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle b/playkit/src/test/assets/testdata/mpd/sample_dash_with_subtitle similarity index 100% rename from playkit/src/test/assets/testdata/mpd/sample_mpd_with_subtitle rename to playkit/src/test/assets/testdata/mpd/sample_dash_with_subtitle diff --git a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt index c85a4a48a..b6d87f9d7 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/ExoPlayerWrapperTest.kt @@ -72,7 +72,7 @@ internal class ExoPlayerWrapperTest { private fun prepareClassInstances() { emptyTrackSelectionArray = TrackSelectionArray() trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest(ApplicationProvider.getApplicationContext(), - "testdata/mpd/sample_mpd_switching_property") + "testdata/mpd/sample_dash_switching_property") realExoPlayerWrapper = ExoPlayerWrapper(context, exoPlayerView, playerSettings, rootPlayerView) realExoPlayerWrapper.setEventListener(eventListener) } diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index b40933ccc..6ba501ecd 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -27,12 +27,13 @@ import org.junit.runner.RunWith import org.mockito.Mockito.* import kotlin.collections.ArrayList - @RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { - private val initialAssetPath: String = "testdata/mpd/" - private var trackSelectionHelper: TrackSelectionHelper? = null + private val initialDashAssetPath: String = "testdata/mpd/" + private val dashFilePrefix: String = "sample_dash" + private val initialHlsAssetPath: String = "testdata/m3u8/" + private val hlsFilePrefix: String = "sample_hls" private lateinit var actualSelector: TrackSelector private var mockedSelector: DefaultTrackSelector = mock(DefaultTrackSelector::class.java) @@ -66,9 +67,19 @@ internal class TrackSelectionHelperTest { private fun buildActualTrackSelector(fileName: String): TrackSelector { val timeline = FakeTimeline() - trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest( - ApplicationProvider.getApplicationContext(), - fileName) + val filePath: String + + if (fileName.startsWith(dashFilePrefix)) { + filePath = "${initialDashAssetPath}${fileName}" + trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest( + ApplicationProvider.getApplicationContext(), + filePath) + } else { + filePath = "${initialHlsAssetPath}${fileName}" + trackGroupArray = TestUtils.getTrackGroupArrayFromHlsManifest( + ApplicationProvider.getApplicationContext(), + filePath) + } actualSelector = DefaultTrackSelector(ApplicationProvider.getApplicationContext() as Context) actualSelector.init(invalidationListener, bandwidthMeter) @@ -84,48 +95,62 @@ internal class TrackSelectionHelperTest { } private fun getTrackSelectionHelper(selector: TrackSelector): TrackSelectionHelper { - trackSelectionHelper = TrackSelectionHelper( + val trackSelectionHelper = TrackSelectionHelper( ApplicationProvider.getApplicationContext(), selector as DefaultTrackSelector, lastSelectedTrackIds) - trackSelectionHelper?.applyPlayerSettings(playerSettings) + trackSelectionHelper.applyPlayerSettings(playerSettings) - return trackSelectionHelper!! + return trackSelectionHelper } @Test fun buildTracks_mappedTrackInfo() { - trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_clear_h264_tears")) - trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + trackSelectionHelper.prepareTracks(TrackSelectionArray(), null).let { + assertTrue(it) + } + + val trackSelectionHelperHls = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_harold")) + trackSelectionHelperHls.prepareTracks(TrackSelectionArray(), null).let { assertTrue(it) } } @Test fun buildTracks_mappedTrackInfo_instream_subtitles() { - val selector: TrackSelector = buildActualTrackSelector("${initialAssetPath}sample_mpd_with_subtitle") - trackSelectionHelper = getTrackSelectionHelper(selector) + // Dash + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper?.prepareTracks(TrackSelectionArray(), null) verify(spyTrackSelectionHelper)?.buildTracks(ArrayList()) val pkTracks: PKTracks? = spyTrackSelectionHelper?.buildTracks(null) assertEquals(6, pkTracks?.textTracks?.size) + + // HLS + val trackSelectionHelperHls = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_harold")) + val spyTrackSelectionHelperHls = spy(trackSelectionHelperHls) + spyTrackSelectionHelperHls?.prepareTracks(TrackSelectionArray(), null) + verify(spyTrackSelectionHelperHls)?.buildTracks(ArrayList()) + + val pkTracksHls: PKTracks? = spyTrackSelectionHelperHls?.buildTracks(null) + assertEquals(9, pkTracksHls?.textTracks?.size) } @Test fun buildTracks_mappedTrackInfo_is_null() { - trackSelectionHelper = getTrackSelectionHelper(mockedSelector) - trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + trackSelectionHelper.prepareTracks(TrackSelectionArray(), null).let { assertFalse(it) } } @Test fun buildTracks_mappedTrackInfo_checkTracksUnavailability_false() { - trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_no_tracks")) - trackSelectionHelper?.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - trackSelectionHelper?.prepareTracks(TrackSelectionArray(), null)?.let { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_no_tracks")) + trackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + trackSelectionHelper.prepareTracks(TrackSelectionArray(), null).let { assertFalse(it) } } @@ -136,12 +161,12 @@ internal class TrackSelectionHelperTest { @Test fun buildTracks_rawImageTracks() { - val dashManifestString: String = TestUtils.getManifestString(ApplicationProvider.getApplicationContext() as Context, "${initialAssetPath}sample_mpd_custom_manifest_image_tracks") + val dashManifestString: String = TestUtils.getManifestString(ApplicationProvider.getApplicationContext() as Context, "${initialDashAssetPath}sample_dash_custom_manifest_image_tracks") val customDashManifest: CustomDashManifest = CustomDashManifestParser().parse(Uri.parse("http://dash.edgesuite.net/akamai/bbb_30fps/bbb_with_multiple_tiled_thumbnails.mpd"), dashManifestString) - trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("${initialAssetPath}sample_mpd_custom_manifest_without_image_tracks")) - trackSelectionHelper?.prepareTracks(TrackSelectionArray(), customDashManifest)?.let { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_custom_manifest_without_image_tracks")) + trackSelectionHelper.prepareTracks(TrackSelectionArray(), customDashManifest).let { assertTrue(it) } } @@ -149,21 +174,21 @@ internal class TrackSelectionHelperTest { @Test fun getExternalSubtitleLanguage() { val format = Format.Builder().setLanguage("eng-application/mp4").build(); - trackSelectionHelper = getTrackSelectionHelper(mockedSelector) - assertEquals("en", trackSelectionHelper?.getExternalSubtitleLanguage(format)) + val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + assertEquals("en", trackSelectionHelper.getExternalSubtitleLanguage(format)) val isExternalSubtitleformat = format.buildUpon().setLanguage("eng-application/mp4").setSampleMimeType("application/mp4").build() - assertEquals(true, trackSelectionHelper?.isExternalSubtitle(isExternalSubtitleformat.language, isExternalSubtitleformat.sampleMimeType)) + assertEquals(true, trackSelectionHelper.isExternalSubtitle(isExternalSubtitleformat.language, isExternalSubtitleformat.sampleMimeType)) val nullFormat = format.buildUpon().setLanguage(null).build() - assertNull(trackSelectionHelper?.getExternalSubtitleLanguage(nullFormat)) + assertNull(trackSelectionHelper.getExternalSubtitleLanguage(nullFormat)) } @Test(expected = StringIndexOutOfBoundsException::class) fun getExternalSubtitleLanguage_null_exception() { val format = Format.Builder().build(); - trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) val nullExternalSubtitleformat = format.buildUpon().setLanguage("engapplication/mp4").setSampleMimeType("application/mp4").build() - trackSelectionHelper?.getExternalSubtitleLanguage(nullExternalSubtitleformat) + trackSelectionHelper.getExternalSubtitleLanguage(nullExternalSubtitleformat) } } From 865544dd093a9630233db78be281fbf7c8cbe757 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 3 Jun 2021 15:56:29 +0530 Subject: [PATCH 16/33] - Changes wrt ExoPlayer v2.14.0 --- .../kaltura/testhelper/FakeShuffleOrder.java | 57 +++++ .../com/kaltura/testhelper/FakeTimeline.java | 238 +++++++++++------- 2 files changed, 201 insertions(+), 94 deletions(-) create mode 100644 playkit/src/main/java/com/kaltura/testhelper/FakeShuffleOrder.java diff --git a/playkit/src/main/java/com/kaltura/testhelper/FakeShuffleOrder.java b/playkit/src/main/java/com/kaltura/testhelper/FakeShuffleOrder.java new file mode 100644 index 000000000..a040150cb --- /dev/null +++ b/playkit/src/main/java/com/kaltura/testhelper/FakeShuffleOrder.java @@ -0,0 +1,57 @@ +package com.kaltura.testhelper; + +import com.kaltura.android.exoplayer2.C; +import com.kaltura.android.exoplayer2.source.ShuffleOrder; + +/** + * Fake {@link ShuffleOrder} which returns a reverse order. This order is thus deterministic but + * different from the original order. + */ +public final class FakeShuffleOrder implements ShuffleOrder { + + private final int length; + + public FakeShuffleOrder(int length) { + this.length = length; + } + + @Override + public int getLength() { + return length; + } + + @Override + public int getNextIndex(int index) { + return index > 0 ? index - 1 : C.INDEX_UNSET; + } + + @Override + public int getPreviousIndex(int index) { + return index < length - 1 ? index + 1 : C.INDEX_UNSET; + } + + @Override + public int getLastIndex() { + return length > 0 ? 0 : C.INDEX_UNSET; + } + + @Override + public int getFirstIndex() { + return length > 0 ? length - 1 : C.INDEX_UNSET; + } + + @Override + public ShuffleOrder cloneAndInsert(int insertionIndex, int insertionCount) { + return new FakeShuffleOrder(length + insertionCount); + } + + @Override + public ShuffleOrder cloneAndRemove(int indexFrom, int indexToExclusive) { + return new FakeShuffleOrder(length - indexToExclusive + indexFrom); + } + + @Override + public ShuffleOrder cloneAndClear() { + return new FakeShuffleOrder(/* length= */ 0); + } +} diff --git a/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java b/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java index 7d2217709..a7085f50c 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java +++ b/playkit/src/main/java/com/kaltura/testhelper/FakeTimeline.java @@ -5,6 +5,7 @@ import com.kaltura.android.exoplayer2.C; import com.kaltura.android.exoplayer2.MediaItem; +import com.kaltura.android.exoplayer2.Player; import com.kaltura.android.exoplayer2.Timeline; import com.kaltura.android.exoplayer2.source.ads.AdPlaybackState; import com.kaltura.android.exoplayer2.util.Assertions; @@ -16,7 +17,10 @@ /** Fake {@link Timeline} which can be setup to return custom {@link TimelineWindowDefinition}s. */ public final class FakeTimeline extends Timeline { - + + /** + * Definition used to define a {@link FakeTimeline}. + */ public static final class TimelineWindowDefinition { /** Default window duration in microseconds. */ @@ -44,16 +48,16 @@ public static final class TimelineWindowDefinition { */ public static TimelineWindowDefinition createPlaceholder(Object tag) { return new TimelineWindowDefinition( - /* periodCount= */ 1, - /* id= */ tag, - /* isSeekable= */ false, - /* isDynamic= */ true, - /* isLive= */ false, - /* isPlaceholder= */ true, - /* durationUs= */ C.TIME_UNSET, - /* defaultPositionUs= */ 0, - /* windowOffsetInFirstPeriodUs= */ 0, - AdPlaybackState.NONE); + /* periodCount= */ 1, + /* id= */ tag, + /* isSeekable= */ false, + /* isDynamic= */ true, + /* isLive= */ false, + /* isPlaceholder= */ true, + /* durationUs= */ C.TIME_UNSET, + /* defaultPositionUs= */ 0, + /* windowOffsetInFirstPeriodUs= */ 0, + AdPlaybackState.NONE); } /** @@ -90,7 +94,7 @@ public TimelineWindowDefinition(boolean isSeekable, boolean isDynamic, long dura * @param durationUs The duration of the window in microseconds. */ public TimelineWindowDefinition(int periodCount, Object id, boolean isSeekable, - boolean isDynamic, long durationUs) { + boolean isDynamic, long durationUs) { this(periodCount, id, isSeekable, isDynamic, durationUs, AdPlaybackState.NONE); } @@ -106,23 +110,23 @@ public TimelineWindowDefinition(int periodCount, Object id, boolean isSeekable, * @param adPlaybackState The ad playback state. */ public TimelineWindowDefinition( - int periodCount, - Object id, - boolean isSeekable, - boolean isDynamic, - long durationUs, - AdPlaybackState adPlaybackState) { + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + long durationUs, + AdPlaybackState adPlaybackState) { this( - periodCount, - id, - isSeekable, - isDynamic, - /* isLive= */ isDynamic, - /* isPlaceholder= */ false, - durationUs, - /* defaultPositionUs= */ 0, - DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - adPlaybackState); + periodCount, + id, + isSeekable, + isDynamic, + /* isLive= */ isDynamic, + /* isPlaceholder= */ false, + durationUs, + /* defaultPositionUs= */ 0, + DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, + adPlaybackState); } /** @@ -142,28 +146,28 @@ public TimelineWindowDefinition( * @param adPlaybackState The ad playback state. */ public TimelineWindowDefinition( - int periodCount, - Object id, - boolean isSeekable, - boolean isDynamic, - boolean isLive, - boolean isPlaceholder, - long durationUs, - long defaultPositionUs, - long windowOffsetInFirstPeriodUs, - AdPlaybackState adPlaybackState) { + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + boolean isPlaceholder, + long durationUs, + long defaultPositionUs, + long windowOffsetInFirstPeriodUs, + AdPlaybackState adPlaybackState) { this( - periodCount, - id, - isSeekable, - isDynamic, - isLive, - isPlaceholder, - durationUs, - defaultPositionUs, - windowOffsetInFirstPeriodUs, - adPlaybackState, - FAKE_MEDIA_ITEM.buildUpon().setTag(id).build()); + periodCount, + id, + isSeekable, + isDynamic, + isLive, + isPlaceholder, + durationUs, + defaultPositionUs, + windowOffsetInFirstPeriodUs, + adPlaybackState, + FAKE_MEDIA_ITEM.buildUpon().setTag(id).build()); } /** @@ -184,17 +188,17 @@ public TimelineWindowDefinition( * @param mediaItem The media item to include in the timeline. */ public TimelineWindowDefinition( - int periodCount, - Object id, - boolean isSeekable, - boolean isDynamic, - boolean isLive, - boolean isPlaceholder, - long durationUs, - long defaultPositionUs, - long windowOffsetInFirstPeriodUs, - AdPlaybackState adPlaybackState, - MediaItem mediaItem) { + int periodCount, + Object id, + boolean isSeekable, + boolean isDynamic, + boolean isLive, + boolean isPlaceholder, + long durationUs, + long defaultPositionUs, + long windowOffsetInFirstPeriodUs, + AdPlaybackState adPlaybackState, + MediaItem mediaItem) { Assertions.checkArgument(durationUs != C.TIME_UNSET || periodCount == 1); this.periodCount = periodCount; this.id = id; @@ -212,13 +216,14 @@ public TimelineWindowDefinition( /** The fake media item used by the fake timeline. */ public static final MediaItem FAKE_MEDIA_ITEM = - new MediaItem.Builder().setMediaId("FakeTimeline").setUri(Uri.EMPTY).build(); + new MediaItem.Builder().setMediaId("FakeTimeline").setUri(Uri.EMPTY).build(); private static final long AD_DURATION_US = 5 * C.MICROS_PER_SECOND; private final TimelineWindowDefinition[] windowDefinitions; private final Object[] manifests; private final int[] periodOffsets; + private final FakeShuffleOrder fakeShuffleOrder; /** * Returns an ad playback state with the specified number of ads in each of the specified ad @@ -231,16 +236,16 @@ public TimelineWindowDefinition( public static AdPlaybackState createAdPlaybackState(int adsPerAdGroup, long... adGroupTimesUs) { int adGroupCount = adGroupTimesUs.length; AdPlaybackState adPlaybackState = - new AdPlaybackState(/* adsId= */ new Object(), adGroupTimesUs); + new AdPlaybackState(/* adsId= */ new Object(), adGroupTimesUs); long[][] adDurationsUs = new long[adGroupCount][]; for (int i = 0; i < adGroupCount; i++) { adPlaybackState = adPlaybackState.withAdCount(/* adGroupIndex= */ i, adsPerAdGroup); for (int j = 0; j < adsPerAdGroup; j++) { adPlaybackState = - adPlaybackState.withAdUri( - /* adGroupIndex= */ i, - /* adIndexInAdGroup= */ j, - Uri.parse("https://example.com/ad/" + i + "/" + j)); + adPlaybackState.withAdUri( + /* adGroupIndex= */ i, + /* adIndexInAdGroup= */ j, + Uri.parse("https://example.com/ad/" + i + "/" + j)); } adDurationsUs[i] = new long[adsPerAdGroup]; Arrays.fill(adDurationsUs[i], AD_DURATION_US); @@ -292,6 +297,7 @@ public FakeTimeline(Object[] manifests, TimelineWindowDefinition... windowDefini for (int i = 0; i < windowDefinitions.length; i++) { periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount; } + fakeShuffleOrder = new FakeShuffleOrder(windowDefinitions.length); } @Override @@ -299,26 +305,68 @@ public int getWindowCount() { return windowDefinitions.length; } + @Override + public int getNextWindowIndex( + int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) { + if (repeatMode == Player.REPEAT_MODE_ONE) { + return windowIndex; + } + if (windowIndex == getLastWindowIndex(shuffleModeEnabled)) { + return repeatMode == Player.REPEAT_MODE_ALL + ? getFirstWindowIndex(shuffleModeEnabled) + : C.INDEX_UNSET; + } + return shuffleModeEnabled ? fakeShuffleOrder.getNextIndex(windowIndex) : windowIndex + 1; + } + + @Override + public int getPreviousWindowIndex( + int windowIndex, @Player.RepeatMode int repeatMode, boolean shuffleModeEnabled) { + if (repeatMode == Player.REPEAT_MODE_ONE) { + return windowIndex; + } + if (windowIndex == getFirstWindowIndex(shuffleModeEnabled)) { + return repeatMode == Player.REPEAT_MODE_ALL + ? getLastWindowIndex(shuffleModeEnabled) + : C.INDEX_UNSET; + } + return shuffleModeEnabled ? fakeShuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1; + } + + @Override + public int getLastWindowIndex(boolean shuffleModeEnabled) { + return shuffleModeEnabled + ? fakeShuffleOrder.getLastIndex() + : super.getLastWindowIndex(/* shuffleModeEnabled= */ false); + } + + @Override + public int getFirstWindowIndex(boolean shuffleModeEnabled) { + return shuffleModeEnabled + ? fakeShuffleOrder.getFirstIndex() + : super.getFirstWindowIndex(/* shuffleModeEnabled= */ false); + } + @Override public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) { TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; window.set( - /* uid= */ windowDefinition.id, - windowDefinition.mediaItem, - manifests[windowIndex], - /* presentationStartTimeMs= */ C.TIME_UNSET, - /* windowStartTimeMs= */ windowDefinition.isLive - ? C.usToMs(windowDefinition.windowOffsetInFirstPeriodUs) - : C.TIME_UNSET, - /* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET, - windowDefinition.isSeekable, - windowDefinition.isDynamic, - windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null, - windowDefinition.defaultPositionUs, - windowDefinition.durationUs, - periodOffsets[windowIndex], - periodOffsets[windowIndex + 1] - 1, - windowDefinition.windowOffsetInFirstPeriodUs); + /* uid= */ windowDefinition.id, + windowDefinition.mediaItem, + manifests[windowIndex], + /* presentationStartTimeMs= */ C.TIME_UNSET, + /* windowStartTimeMs= */ windowDefinition.isLive + ? C.usToMs(windowDefinition.windowOffsetInFirstPeriodUs) + : C.TIME_UNSET, + /* elapsedRealtimeEpochOffsetMs= */ windowDefinition.isLive ? 0 : C.TIME_UNSET, + windowDefinition.isSeekable, + windowDefinition.isDynamic, + windowDefinition.isLive ? windowDefinition.mediaItem.liveConfiguration : null, + windowDefinition.defaultPositionUs, + windowDefinition.durationUs, + periodOffsets[windowIndex], + periodOffsets[windowIndex + 1] - 1, + windowDefinition.windowOffsetInFirstPeriodUs); window.isPlaceholder = windowDefinition.isPlaceholder; return window; } @@ -337,9 +385,9 @@ public Period getPeriod(int periodIndex, Period period, boolean setIds) { Object uid = setIds ? Pair.create(windowDefinition.id, windowPeriodIndex) : null; // Arbitrarily set period duration by distributing window duration equally among all periods. long periodDurationUs = - windowDefinition.durationUs == C.TIME_UNSET - ? C.TIME_UNSET - : windowDefinition.durationUs / windowDefinition.periodCount; + windowDefinition.durationUs == C.TIME_UNSET + ? C.TIME_UNSET + : windowDefinition.durationUs / windowDefinition.periodCount; long positionInWindowUs; if (windowPeriodIndex == 0) { if (windowDefinition.durationUs != C.TIME_UNSET) { @@ -349,13 +397,15 @@ public Period getPeriod(int periodIndex, Period period, boolean setIds) { } else { positionInWindowUs = periodDurationUs * windowPeriodIndex; } - return period.set( - id, - uid, - windowIndex, - periodDurationUs, - positionInWindowUs, - windowDefinition.adPlaybackState); + period.set( + id, + uid, + windowIndex, + periodDurationUs, + positionInWindowUs, + windowDefinition.adPlaybackState, + windowDefinition.isPlaceholder); + return period; } @Override @@ -372,8 +422,8 @@ public int getIndexOfPeriod(Object uid) { public Object getUidOfPeriod(int periodIndex) { Assertions.checkIndex(periodIndex, 0, getPeriodCount()); int windowIndex = - Util.binarySearchFloor( - periodOffsets, periodIndex, /* inclusive= */ true, /* stayInBounds= */ false); + Util.binarySearchFloor( + periodOffsets, periodIndex, /* inclusive= */ true, /* stayInBounds= */ false); int windowPeriodIndex = periodIndex - periodOffsets[windowIndex]; TimelineWindowDefinition windowDefinition = windowDefinitions[windowIndex]; return Pair.create(windowDefinition.id, windowPeriodIndex); From 9a8ccd4bfcbfe0d38681bd804290610d7a90eb92 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 3 Jun 2021 17:53:31 +0530 Subject: [PATCH 17/33] - Text preference tests --- .../playkit/player/TrackSelectionHelper.java | 6 ++--- .../player/TrackSelectionHelperTest.kt | 25 +++++++++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index da219d48d..29e4573f4 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -104,9 +104,9 @@ class TrackSelectionHelper { private List imageTracks = new ArrayList<>(); - private Map>> subtitleListMap = new HashMap<>(); - private Map> videoTracksCodecsMap = new HashMap<>(); - private Map> audioTracksCodecsMap = new HashMap<>(); + protected Map>> subtitleListMap = new HashMap<>(); + protected Map> videoTracksCodecsMap = new HashMap<>(); + protected Map> audioTracksCodecsMap = new HashMap<>(); private String[] lastSelectedTrackIds; private String[] requestedChangeTrackIds; diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 6ba501ecd..666cf3771 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -17,6 +17,7 @@ import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray import com.kaltura.android.exoplayer2.trackselection.TrackSelector import com.kaltura.android.exoplayer2.trackselection.TrackSelectorResult import com.kaltura.android.exoplayer2.upstream.BandwidthMeter +import com.kaltura.playkit.PKSubtitlePreference import com.kaltura.testhelper.FakeRendererCapabilities import com.kaltura.testhelper.FakeTimeline import com.kaltura.testhelper.TestUtils @@ -25,7 +26,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.* +import java.util.* import kotlin.collections.ArrayList +import kotlin.collections.HashMap @RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { @@ -173,7 +176,7 @@ internal class TrackSelectionHelperTest { @Test fun getExternalSubtitleLanguage() { - val format = Format.Builder().setLanguage("eng-application/mp4").build(); + val format = Format.Builder().setLanguage("eng-application/mp4").build() val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) assertEquals("en", trackSelectionHelper.getExternalSubtitleLanguage(format)) @@ -186,9 +189,27 @@ internal class TrackSelectionHelperTest { @Test(expected = StringIndexOutOfBoundsException::class) fun getExternalSubtitleLanguage_null_exception() { - val format = Format.Builder().build(); + val format = Format.Builder().build() val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) val nullExternalSubtitleformat = format.buildUpon().setLanguage("engapplication/mp4").setSampleMimeType("application/mp4").build() trackSelectionHelper.getExternalSubtitleLanguage(nullExternalSubtitleformat) } + + @Test + fun discardTextTrackOnPreference() { + val format = Format.Builder().build() + val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + playerSettings.setSubtitlePreference(PKSubtitlePreference.OFF); + assertFalse(trackSelectionHelper.discardTextTrackOnPreference(format)) + + playerSettings.setSubtitlePreference(PKSubtitlePreference.EXTERNAL) + val japaneseFormat = format.buildUpon().setLanguage("ja-text/vtt").setSampleMimeType("text/vtt").build() + val japaneseFormat1 = format.buildUpon().setLanguage("ja2-text/vtt").setSampleMimeType("text/vtt").build() + var formatMap: HashMap> = HashMap() + formatMap.put("ja", Arrays.asList(japaneseFormat, japaneseFormat1)) + trackSelectionHelper.subtitleListMap.put("ja", formatMap) + + assertFalse(trackSelectionHelper.discardTextTrackOnPreference(japaneseFormat)) + } + } From 9117b33dd9c3a17d400c45b6364d37928d4c5852 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Sun, 6 Jun 2021 12:41:57 +0530 Subject: [PATCH 18/33] - TrackSelection Helper tests --- .../playkit/player/TrackSelectionHelper.java | 7 +- .../testdata/mpd/sample_dash_hevc_and_avc_mix | 156 ++++++++++++++++++ .../player/TrackSelectionHelperTest.kt | 46 +++++- 3 files changed, 197 insertions(+), 12 deletions(-) create mode 100644 playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index 29e4573f4..e20b96877 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -1763,7 +1763,7 @@ protected void hasExternalSubtitles(boolean hasExternalSubtitles) { this.hasExternalSubtitles = hasExternalSubtitles; } - public static boolean isCodecSupported(@NonNull String codecs, @Nullable TrackType type, boolean allowSoftware) { + protected boolean isCodecSupported(@NonNull String codecs, @Nullable TrackType type, boolean allowSoftware) { if (type == TrackType.TEXT) { return true; // always supported @@ -1790,9 +1790,4 @@ public static boolean isCodecSupported(@NonNull String codecs, @Nullable TrackTy return PKCodecSupport.hasDecoder(codecs, false, allowSoftware); } } - - - - - } diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix new file mode 100644 index 000000000..71ccf5158 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 666cf3771..0fef2bb97 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -18,6 +18,7 @@ import com.kaltura.android.exoplayer2.trackselection.TrackSelector import com.kaltura.android.exoplayer2.trackselection.TrackSelectorResult import com.kaltura.android.exoplayer2.upstream.BandwidthMeter import com.kaltura.playkit.PKSubtitlePreference +import com.kaltura.playkit.PKVideoCodec import com.kaltura.testhelper.FakeRendererCapabilities import com.kaltura.testhelper.FakeTimeline import com.kaltura.testhelper.TestUtils @@ -201,15 +202,48 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) playerSettings.setSubtitlePreference(PKSubtitlePreference.OFF); assertFalse(trackSelectionHelper.discardTextTrackOnPreference(format)) - + playerSettings.setSubtitlePreference(PKSubtitlePreference.EXTERNAL) val japaneseFormat = format.buildUpon().setLanguage("ja-text/vtt").setSampleMimeType("text/vtt").build() - val japaneseFormat1 = format.buildUpon().setLanguage("ja2-text/vtt").setSampleMimeType("text/vtt").build() - var formatMap: HashMap> = HashMap() - formatMap.put("ja", Arrays.asList(japaneseFormat, japaneseFormat1)) - trackSelectionHelper.subtitleListMap.put("ja", formatMap) + val japaneseInternalFormat = format.buildUpon().setLanguage("ja").setSampleMimeType("text/vtt").build() + val englishFormat = format.buildUpon().setLanguage("en-text/vtt").setSampleMimeType("text/vtt").build() + val formatMapJa: HashMap> = HashMap() + val formatMapEn: HashMap> = HashMap() + formatMapJa.put("ja", listOf(japaneseFormat)) + formatMapJa.put("ja-text/vtt", listOf(japaneseInternalFormat)) + formatMapEn.put("en", listOf(englishFormat)) + trackSelectionHelper.subtitleListMap.put("ja", formatMapJa) + trackSelectionHelper.subtitleListMap.put("en", formatMapEn) assertFalse(trackSelectionHelper.discardTextTrackOnPreference(japaneseFormat)) } - + + @Test + fun filterVideoTracks_hevc() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) + // HEVC only track + val spyTrackSelectionHelper = spy(trackSelectionHelper) + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) + `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) + playerSettings.preferredVideoCodecSettings.allowMixedCodecAdaptiveness = false + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) + val videoTrack = spyTrackSelectionHelper.filterVideoTracks() + assertEquals(4, videoTrack.size) + assertEquals("HEVC Track Found: ", PKVideoCodec.HEVC, videoTrack.get(0).codecType) + } + + @Test + fun filterVideoTracks_avc() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) + // AVC only track + val spyTrackSelectionHelper = spy(trackSelectionHelper) + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) + `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) + playerSettings.preferredVideoCodecSettings = VideoCodecSettings().setCodecPriorityList(listOf(PKVideoCodec.AVC, PKVideoCodec.HEVC)) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) + val videoTrack = spyTrackSelectionHelper.filterVideoTracks() + assertEquals(5, videoTrack.size) + assertEquals("AVC Track Found: ", PKVideoCodec.AVC, videoTrack.get(0).codecType) + } + } From a5ab572b414acdf54129ab33b7a53943547c5f48 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Mon, 7 Jun 2021 12:19:21 +0530 Subject: [PATCH 19/33] - TrackSelection test contd.. --- .../playkit/player/TrackSelectionHelper.java | 2 +- .../testdata/mpd/sample_dash_audio_only | 36 +++++++++++++++++++ .../testdata/mpd/sample_dash_hevc_and_avc_mix | 12 +++---- .../player/TrackSelectionHelperTest.kt | 15 ++++++++ 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 playkit/src/test/assets/testdata/mpd/sample_dash_audio_only diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index e20b96877..80ec87453 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -504,7 +504,7 @@ boolean videoCodecsSupportedInHardware() { return false; } - protected void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { + void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { for (Map.Entry> multipleCodecEntry : videoTracksCodecsMap.entrySet()) { for (VideoTrack codecVideoTrack : multipleCodecEntry.getValue()) { if (codecVideoTrack.getCodecName() != null && diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_audio_only b/playkit/src/test/assets/testdata/mpd/sample_dash_audio_only new file mode 100644 index 000000000..48847f57f --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_audio_only @@ -0,0 +1,36 @@ + + + + Media Presentation Description for audio only from DASH-IF live simulator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix index 71ccf5158..0b577bc19 100644 --- a/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix @@ -16,8 +16,8 @@ maxFrameRate="25"> @@ -66,8 +66,8 @@ maxFrameRate="25"> @@ -127,8 +127,8 @@ value="1"/> diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 0fef2bb97..85fca9d3e 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -17,6 +17,7 @@ import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray import com.kaltura.android.exoplayer2.trackselection.TrackSelector import com.kaltura.android.exoplayer2.trackselection.TrackSelectorResult import com.kaltura.android.exoplayer2.upstream.BandwidthMeter +import com.kaltura.playkit.PKAudioCodec import com.kaltura.playkit.PKSubtitlePreference import com.kaltura.playkit.PKVideoCodec import com.kaltura.testhelper.FakeRendererCapabilities @@ -246,4 +247,18 @@ internal class TrackSelectionHelperTest { assertEquals("AVC Track Found: ", PKVideoCodec.AVC, videoTrack.get(0).codecType) } + @Test + fun filterAdaptiveAudioTracks_ec3() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) + playerSettings.preferredAudioCodecSettings = AudioCodecSettings().setCodecPriorityList(listOf(PKAudioCodec.E_AC3, PKAudioCodec.AAC)) + val audioTrack = spyTrackSelectionHelper.filterAdaptiveAudioTracks() + assertEquals(2, audioTrack.size) + } + + @Test + fun extractTextTracksToMap() { + + } } From a25af7482eb34031e7f9f8b67960cd3ac7c687d1 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 10 Jun 2021 10:57:19 +0530 Subject: [PATCH 20/33] - Class update for visibility --- .../playkit/player/TrackSelectionHelper.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index 4cdd8fe7b..e20f0deff 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -105,9 +105,9 @@ class TrackSelectionHelper { private List imageTracks = new ArrayList<>(); - private Map>> subtitleListMap = new HashMap<>(); - private Map> videoTracksCodecsMap = new HashMap<>(); - private Map> audioTracksCodecsMap = new HashMap<>(); + Map>> subtitleListMap = new HashMap<>(); + Map> videoTracksCodecsMap = new HashMap<>(); + Map> audioTracksCodecsMap = new HashMap<>(); private String[] lastSelectedTrackIds; private String[] requestedChangeTrackIds; @@ -224,7 +224,7 @@ boolean prepareTracks(TrackSelectionArray trackSelections, CustomDashManifest cu * Actually build {@link PKTracks} object, based on the loaded manifest into Exoplayer. * This method knows how to filter unsupported/unknown formats, and create adaptive option when this is possible. */ - private PKTracks buildTracks(List rawImageTracks) { + PKTracks buildTracks(List rawImageTracks) { clearTracksLists(); @@ -392,18 +392,18 @@ private TrackType getTrackType(int rendererIndex) { return trackType; } - private String getLanguageFromFormat(Format format) { + String getLanguageFromFormat(Format format) { if (format.language == null) { return LANGUAGE_UNKNOWN; } return format.language; } - private boolean isExternalSubtitle(String language, String sampleMimeType) { + boolean isExternalSubtitle(String language, String sampleMimeType) { return language != null && (language.contains("-" + sampleMimeType) || language.contains("-" + "Unknown")); } - private String getExternalSubtitleLanguage(Format format) { + String getExternalSubtitleLanguage(Format format) { if (format.language != null) { return format.language.substring(0, format.language.indexOf("-")); } else { @@ -411,7 +411,7 @@ private String getExternalSubtitleLanguage(Format format) { } } - private boolean discardTextTrackOnPreference(Format format) { + boolean discardTextTrackOnPreference(Format format) { PKSubtitlePreference subtitlePreference = playerSettings.getSubtitlePreference(); if (subtitlePreference == PKSubtitlePreference.OFF) { return false; @@ -442,7 +442,7 @@ private boolean discardTextTrackOnPreference(Format format) { return false; } - private List filterVideoTracks() { + List filterVideoTracks() { if (videoTracksCodecsMap == null || videoTracksCodecsMap.isEmpty()) { return videoTracks; @@ -485,7 +485,7 @@ private List filterVideoTracks() { return videoTracks; } - private boolean videoCodecsSupportedInHardware() { + boolean videoCodecsSupportedInHardware() { for (PKVideoCodec videoCodecForPlayback : playerSettings.getPreferredVideoCodecSettings().getCodecPriorityList()) { if (!videoTracksCodecsMap.containsKey(videoCodecForPlayback)) { continue; @@ -504,7 +504,7 @@ private boolean videoCodecsSupportedInHardware() { return false; } - private void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { + void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) { for (Map.Entry> multipleCodecEntry : videoTracksCodecsMap.entrySet()) { for (VideoTrack codecVideoTrack : multipleCodecEntry.getValue()) { if (codecVideoTrack.getCodecName() != null && @@ -524,7 +524,7 @@ private void populateAllCodecTracks(boolean atleastOneCodecSupportedInHardware) * and hide all the bitrate variants. * if app prefers to allow audio track selection by codec type it will return all available tracks by codec by audio groups */ - private ArrayList filterAdaptiveAudioTracks() { + ArrayList filterAdaptiveAudioTracks() { ArrayList filteredAudioTracks = new ArrayList<>(); AudioTrack audioTrack; @@ -563,7 +563,7 @@ private ArrayList filterAdaptiveAudioTracks() { * Will add this track only if there is at least one text track available. * Selecting this track, will disable the text renderer. */ - private void maybeAddDisabledTextTrack() { + void maybeAddDisabledTextTrack() { if (textTracks.isEmpty()) { return; @@ -580,7 +580,7 @@ private void maybeAddDisabledTextTrack() { * @return - the index of the track that is selected by default or lastSelected track(Depending on the use-case), * or 0 if no default selection is available and no track was previously selected. */ - private int getDefaultTrackIndex(List trackList, String lastSelectedTrackId) { + int getDefaultTrackIndex(List trackList, String lastSelectedTrackId) { int defaultTrackIndex = 0; @@ -636,7 +636,7 @@ private int getDefaultTrackIndex(List trackList, String las * * @param trackGroupArray TrackGroupArray this includes Internal and External Text Formats */ - private void extractTextTracksToMap(TrackGroupArray trackGroupArray) { + void extractTextTracksToMap(TrackGroupArray trackGroupArray) { for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) { TrackGroup trackGroup = trackGroupArray.get(groupIndex); @@ -670,7 +670,7 @@ private void extractTextTracksToMap(TrackGroupArray trackGroupArray) { } } - private int getUpdatedDefaultTrackIndex(List trackList, int defaultTrackIndex) { + int getUpdatedDefaultTrackIndex(List trackList, int defaultTrackIndex) { if (!trackList.isEmpty()) { int trackType = TRACK_TYPE_UNKNOWN; @@ -691,7 +691,7 @@ private int getUpdatedDefaultTrackIndex(List trackList, int return defaultTrackIndex; } - private int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, int defaultTrackIndex) { + int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, int defaultTrackIndex) { if (trackList != null && selectedFormatLanguage != null) { for (int i = 0; i < trackList.size(); i++) { @@ -714,7 +714,7 @@ private int findDefaultTrackIndex(String selectedFormatLanguage, List trackList, String lastSelectedTrackId, int defaultTrackIndex) { + int restoreLastSelectedTrack(List trackList, String lastSelectedTrackId, int defaultTrackIndex) { //If track was previously selected and selection is differed from the default selection apply it. String defaultUniqueId = trackList.get(defaultTrackIndex).getUniqueId(); if (!NONE.equals(lastSelectedTrackId) && !lastSelectedTrackId.equals(defaultUniqueId)) { @@ -736,7 +736,7 @@ private int restoreLastSelectedTrack(List trackList, String * @param groupIndex - the index of the group this adaptive object refer. * @param format - the actual format of the adaptive object. */ - private void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format format) { + void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format format) { String uniqueId = getUniqueId(rendererIndex, groupIndex, TRACK_ADAPTIVE); if (isAdaptive(rendererIndex, groupIndex) && !adaptiveTrackAlreadyExist(uniqueId, rendererIndex)) { switch (rendererIndex) { @@ -765,7 +765,7 @@ private void maybeAddAdaptiveTrack(int rendererIndex, int groupIndex, Format for * @param trackIndex - actual track index. * @return - uniqueId that represent current track. */ - private String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { + String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { return getUniqueIdPrefix(rendererIndex) + rendererIndex + "," @@ -838,7 +838,7 @@ protected void changeTrack(String uniqueId) { overrideTrack(rendererIndex, override, parametersBuilder); } - protected void overrideMediaVideoCodec() { + void overrideMediaVideoCodec() { List uniqueIds = getVideoTracksUniqueIds(); @@ -1820,7 +1820,7 @@ protected void hasExternalSubtitles(boolean hasExternalSubtitles) { this.hasExternalSubtitles = hasExternalSubtitles; } - public static boolean isCodecSupported(@NonNull String codecs, @Nullable TrackType type, boolean allowSoftware) { + boolean isCodecSupported(@NonNull String codecs, @Nullable TrackType type, boolean allowSoftware) { if (type == TrackType.TEXT) { return true; // always supported From 216c09bf1a2c1a3f35864a670fafbbfcd0ab53c5 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Thu, 10 Jun 2021 23:52:54 +0530 Subject: [PATCH 21/33] - Added more TrackSelection tests --- .../playkit/player/TrackSelectionHelper.java | 81 +++++++++---------- .../mpd/sample_dash_with_external_subtitle | 45 +++++++++++ .../player/TrackSelectionHelperTest.kt | 77 ++++++++++++++++-- 3 files changed, 151 insertions(+), 52 deletions(-) create mode 100644 playkit/src/test/assets/testdata/mpd/sample_dash_with_external_subtitle diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index e20f0deff..f4b74a415 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -96,14 +96,13 @@ class TrackSelectionHelper { private final Context context; private final DefaultTrackSelector selector; private TrackSelectionArray trackSelectionArray; - private MappingTrackSelector.MappedTrackInfo mappedTrackInfo; + MappingTrackSelector.MappedTrackInfo mappedTrackInfo; - private List videoTracks = new ArrayList<>(); + List videoTracks = new ArrayList<>(); private List originalVideoTracks; - private List audioTracks = new ArrayList<>(); - private List textTracks = new ArrayList<>(); - private List imageTracks = new ArrayList<>(); - + List audioTracks = new ArrayList<>(); + List textTracks = new ArrayList<>(); + List imageTracks = new ArrayList<>(); Map>> subtitleListMap = new HashMap<>(); Map> videoTracksCodecsMap = new HashMap<>(); @@ -113,11 +112,11 @@ class TrackSelectionHelper { private String[] requestedChangeTrackIds; // To know if application passed the external subtitles - private boolean hasExternalSubtitles = false; + boolean hasExternalSubtitles = false; // To know if tracks has external subtitles or not. // Helpful in case if subtitles are removed by ExoPlayer in case of any discrepancy - private boolean hasExternalSubtitlesInTracks = false; + boolean hasExternalSubtitlesInTracks = false; private TracksInfoListener tracksInfoListener; private TracksErrorListener tracksErrorListener; @@ -564,7 +563,6 @@ ArrayList filterAdaptiveAudioTracks() { * Selecting this track, will disable the text renderer. */ void maybeAddDisabledTextTrack() { - if (textTracks.isEmpty()) { return; } @@ -774,7 +772,7 @@ String getUniqueId(int rendererIndex, int groupIndex, int trackIndex) { + getUniqueIdPostfix(rendererIndex, trackIndex); } - private String getUniqueIdPrefix(int rendererIndex) { + String getUniqueIdPrefix(int rendererIndex) { switch (rendererIndex) { case TRACK_TYPE_VIDEO: return VIDEO_PREFIX; @@ -789,7 +787,7 @@ private String getUniqueIdPrefix(int rendererIndex) { } } - private String getUniqueIdPostfix(int rendererIndex, int trackIndex) { + String getUniqueIdPostfix(int rendererIndex, int trackIndex) { switch (rendererIndex) { case TRACK_ADAPTIVE: return ADAPTIVE; @@ -878,7 +876,7 @@ protected void overrideMediaDefaultABR(long minAbr, long maxAbr, PKAbrFilter pkA overrideTrack(rendererIndex, override, parametersBuilder); } - private List getVideoTracksUniqueIds() { + List getVideoTracksUniqueIds() { List uniqueIds = new ArrayList<>(); if (videoTracks != null) { Collections.sort(videoTracks); @@ -889,7 +887,7 @@ private List getVideoTracksUniqueIds() { return uniqueIds; } - private List getCodecUniqueIdsWithABR(long minAbr, long maxAbr, PKAbrFilter pkAbrFilter) { + List getCodecUniqueIdsWithABR(long minAbr, long maxAbr, PKAbrFilter pkAbrFilter) { List uniqueIds = new ArrayList<>(); boolean isValidABRRange = true; @@ -993,7 +991,7 @@ private List getCodecUniqueIdsWithABR(long minAbr, long maxAbr, PKAbrFil return uniqueIds; } - private SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { + SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { if (uniqueIds == null || uniqueIds[0] == null) { throw new IllegalArgumentException("Track selection with uniqueId = null"); } @@ -1017,7 +1015,7 @@ private SelectionOverride retrieveOverrideSelectionList(int[][] uniqueIds) { } @NonNull - private SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendererIndex, int groupIndex) { + SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendererIndex, int groupIndex) { SelectionOverride override; int[] adaptiveTrackIndexes; List adaptiveTrackIndexesList = new ArrayList<>(); @@ -1033,7 +1031,7 @@ private SelectionOverride overrideMediaDefaultABR(int[][] uniqueIds, int rendere return override; } - private void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, List adaptiveTrackIndexesList) { + void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, List adaptiveTrackIndexesList) { int trackIndex; int trackGroupIndex; @@ -1050,7 +1048,7 @@ private void createAdaptiveTrackIndexList(int[][] uniqueIds, int groupIndex, Lis } - private int[][] validateAndBuildUniqueIds(List uniqueIds) { + int[][] validateAndBuildUniqueIds(List uniqueIds) { int [][] idsList = new int [uniqueIds.size()][TRACK_RENDERERS_AMOUNT]; for (int index = 0 ; index < idsList.length ; index++) { int[] uniqueTrackId = validateUniqueId(uniqueIds.get(index)); @@ -1063,7 +1061,7 @@ private int[][] validateAndBuildUniqueIds(List uniqueIds) { * @param uniqueId - the uniqueId to convert. * @return - int[] that consist from indexes that are readable to Exoplayer. */ - private int[] parseUniqueId(String uniqueId) { + int[] parseUniqueId(String uniqueId) { int[] parsedUniqueId = new int[3]; String splitUniqueId = removePrefix(uniqueId); String[] strArray = splitUniqueId.split(","); @@ -1092,7 +1090,7 @@ private int[] parseUniqueId(String uniqueId) { * @param uniqueId - the unique id of the track that will override the existing one. * @return - the {@link SelectionOverride} which will override the existing selection. */ - private SelectionOverride retrieveOverrideSelection(int[] uniqueId) { + SelectionOverride retrieveOverrideSelection(int[] uniqueId) { SelectionOverride override; @@ -1162,7 +1160,7 @@ private SelectionOverride retrieveOverrideSelection(int[] uniqueId) { } @NonNull - private SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupIndex) { + SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupIndex) { SelectionOverride override;List adaptiveTrackIndexesList = new ArrayList<>(); int[] adaptiveTrackIndexes; @@ -1214,7 +1212,7 @@ private SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupInde * @param rendererIndex - renderer index on which we want to apply the change. * @param override - the new selection with which we want to override the currently active track. */ - private void overrideTrack(int rendererIndex, SelectionOverride override, DefaultTrackSelector.ParametersBuilder parametersBuilder) { + void overrideTrack(int rendererIndex, SelectionOverride override, DefaultTrackSelector.ParametersBuilder parametersBuilder) { if (override != null) { //actually change track. TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); @@ -1226,7 +1224,7 @@ private void overrideTrack(int rendererIndex, SelectionOverride override, Defaul selector.setParameters(parametersBuilder); } - public void updateTrackSelectorParameter(PlayerSettings playerSettings, DefaultTrackSelector.ParametersBuilder parametersBuilder) { + void updateTrackSelectorParameter(PlayerSettings playerSettings, DefaultTrackSelector.ParametersBuilder parametersBuilder) { if (playerSettings == null) { return; } @@ -1289,7 +1287,7 @@ public ThumbnailInfo getThumbnailInfo(long positionMS) { * @param rendererIndex - renderer index. * @return - true, if adaptive {@link BaseTrack} object already exist for this group. */ - private boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { + boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { List trackList = new ArrayList<>(); switch (rendererIndex) { @@ -1316,7 +1314,7 @@ private boolean adaptiveTrackAlreadyExist(String uniqueId, int rendererIndex) { return false; } - private int getIndexFromUniqueId(String uniqueId, int groupIndex) { + int getIndexFromUniqueId(String uniqueId, int groupIndex) { String uniqueIdWithoutPrefix = removePrefix(uniqueId); String[] strArray = uniqueIdWithoutPrefix.split(","); if (strArray[groupIndex].equals(ADAPTIVE)) { @@ -1326,13 +1324,13 @@ private int getIndexFromUniqueId(String uniqueId, int groupIndex) { return Integer.valueOf(strArray[groupIndex]); } - private String removePrefix(String uniqueId) { + String removePrefix(String uniqueId) { String[] strArray = uniqueId.split(":"); //always return the second element of the splitString. return strArray[1]; } - private int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) { + int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) { int[] adaptiveTrackIndexes = new int[adaptiveTrackIndexesList.size()]; for (int i = 0; i < adaptiveTrackIndexes.length; i++) { adaptiveTrackIndexes[i] = adaptiveTrackIndexesList.get(i); @@ -1341,12 +1339,12 @@ private int[] convertAdaptiveListToArray(List adaptiveTrackIndexesList) return adaptiveTrackIndexes; } - private boolean isFormatSupported(int rendererCount, int groupIndex, int trackIndex) { + boolean isFormatSupported(int rendererCount, int groupIndex, int trackIndex) { return mappedTrackInfo.getTrackSupport(rendererCount, groupIndex, trackIndex) == Consts.FORMAT_HANDLED; } - private boolean isAdaptive(int rendererIndex, int groupIndex) { + boolean isAdaptive(int rendererIndex, int groupIndex) { TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex); return mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false) != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED @@ -1360,7 +1358,7 @@ private boolean isAdaptive(int rendererIndex, int groupIndex) { * @return - parsed uniqueId in case of success. * @throws IllegalArgumentException when uniqueId is illegal. */ - private int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { + int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { if (uniqueId == null) { throw new IllegalArgumentException("uniqueId is null"); @@ -1389,7 +1387,7 @@ private int[] validateUniqueId(String uniqueId) throws IllegalArgumentException throw new IllegalArgumentException("Invalid structure of uniqueId " + uniqueId); } - private boolean isTrackIndexValid(int[] parsedUniqueId) { + boolean isTrackIndexValid(int[] parsedUniqueId) { int rendererIndex = parsedUniqueId[RENDERER_INDEX]; int groupIndex = parsedUniqueId[GROUP_INDEX]; int trackIndex = parsedUniqueId[TRACK_INDEX]; @@ -1408,7 +1406,7 @@ private boolean isTrackIndexValid(int[] parsedUniqueId) { && trackIndex < mappedTrackInfo.getTrackGroups(rendererIndex).get(groupIndex).length; } - private boolean isGroupIndexValid(int[] parsedUniqueId) { + boolean isGroupIndexValid(int[] parsedUniqueId) { if (parsedUniqueId[GROUP_INDEX] == TRACK_TYPE_IMAGE) { return true; } @@ -1532,7 +1530,7 @@ protected long getCurrentVideoHeight() { return -1; } - private ExoTrackSelection getTrackSelection(TrackSelection trackSelection) { + ExoTrackSelection getTrackSelection(TrackSelection trackSelection) { if (trackSelection instanceof ExoTrackSelection) { return (ExoTrackSelection) trackSelection; } @@ -1649,7 +1647,7 @@ protected String getPreferredTrackId(int trackType) { } @Nullable - private String getPreferredTextTrackUniqueId(int trackType) { + String getPreferredTextTrackUniqueId(int trackType) { if (!isValidPreferredTextConfig()) { return null; } @@ -1695,7 +1693,7 @@ private String getPreferredTextTrackUniqueId(int trackType) { } @Nullable - private String maybeSetFirstTextTrackAsAutoSelection() { + String maybeSetFirstTextTrackAsAutoSelection() { String preferredTrackUniqueId = null; //if user set mode to AUTO and the locale lang is not in the stream and no default text track in the stream so we will not select None but the first text track in the stream @@ -1715,7 +1713,7 @@ private String maybeSetFirstTextTrackAsAutoSelection() { return preferredTrackUniqueId; } - private String getPreferredAudioTrackUniqueId(int trackType) { + String getPreferredAudioTrackUniqueId(int trackType) { if (!isValidPreferredAudioConfig()) { return null; } @@ -1746,7 +1744,7 @@ private String getPreferredAudioTrackUniqueId(int trackType) { return preferredTrackUniqueId; } - private PKVideoCodec getVideoCodec(Format format) { + PKVideoCodec getVideoCodec(Format format) { String codec = format.codecs; if (codec != null) { if (codec.startsWith("hev1") || codec.startsWith("hvc1")) { @@ -1773,7 +1771,7 @@ private PKVideoCodec getVideoCodec(Format format) { return PKVideoCodec.AVC; //VIDEO_H264 } - private PKAudioCodec getAudioCodec(Format format) { + PKAudioCodec getAudioCodec(Format format) { String codec = format.codecs; if (codec != null) { if (codec.startsWith("mp4a")) { @@ -1789,7 +1787,7 @@ private PKAudioCodec getAudioCodec(Format format) { return PKAudioCodec.AAC; } - private boolean isValidPreferredAudioConfig() { + boolean isValidPreferredAudioConfig() { if (playerSettings == null) { return false; } @@ -1801,7 +1799,7 @@ private boolean isValidPreferredAudioConfig() { (preferredAudioTrackConfig.getPreferredMode() == PKTrackConfig.Mode.SELECTION && preferredAudioTrackConfig.getTrackLanguage() == null)); } - private boolean isValidPreferredTextConfig() { + boolean isValidPreferredTextConfig() { if (playerSettings == null) { return false; } @@ -1847,9 +1845,4 @@ boolean isCodecSupported(@NonNull String codecs, @Nullable TrackType type, boole return PKCodecSupport.hasDecoder(codecs, false, allowSoftware); } } - - - - - } diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_with_external_subtitle b/playkit/src/test/assets/testdata/mpd/sample_dash_with_external_subtitle new file mode 100644 index 000000000..44391bfe1 --- /dev/null +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_with_external_subtitle @@ -0,0 +1,45 @@ + + + + Media Presentation Description by DASH-IF Live Source Simulator. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 85fca9d3e..5e2a58ddf 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -12,14 +12,9 @@ import com.kaltura.android.exoplayer2.dashmanifestparser.CustomDashManifestParse import com.kaltura.android.exoplayer2.dashmanifestparser.CustomFormat import com.kaltura.android.exoplayer2.source.MediaSource import com.kaltura.android.exoplayer2.source.TrackGroupArray -import com.kaltura.android.exoplayer2.trackselection.DefaultTrackSelector -import com.kaltura.android.exoplayer2.trackselection.TrackSelectionArray -import com.kaltura.android.exoplayer2.trackselection.TrackSelector -import com.kaltura.android.exoplayer2.trackselection.TrackSelectorResult +import com.kaltura.android.exoplayer2.trackselection.* import com.kaltura.android.exoplayer2.upstream.BandwidthMeter -import com.kaltura.playkit.PKAudioCodec -import com.kaltura.playkit.PKSubtitlePreference -import com.kaltura.playkit.PKVideoCodec +import com.kaltura.playkit.* import com.kaltura.testhelper.FakeRendererCapabilities import com.kaltura.testhelper.FakeTimeline import com.kaltura.testhelper.TestUtils @@ -28,7 +23,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.* -import java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -41,6 +35,7 @@ internal class TrackSelectionHelperTest { private val hlsFilePrefix: String = "sample_hls" private lateinit var actualSelector: TrackSelector + private lateinit var actualTrackSelectionArray: TrackSelectionArray private var mockedSelector: DefaultTrackSelector = mock(DefaultTrackSelector::class.java) private val lastSelectedTrackIds = arrayOf(TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE) @@ -94,6 +89,13 @@ internal class TrackSelectionHelperTest { MediaSource.MediaPeriodId(timeline.getUidOfPeriod(0)), FakeTimeline()) + var exoTrackSelectionArray = ArrayList() + for (exoTrackSelection: ExoTrackSelection in trackSelectorResult.selections) { + exoTrackSelectionArray.add(exoTrackSelection) + } + + actualTrackSelectionArray = TrackSelectionArray(*exoTrackSelectionArray.toTypedArray()) + actualSelector.onSelectionActivated(trackSelectorResult.info) return actualSelector @@ -257,8 +259,67 @@ internal class TrackSelectionHelperTest { assertEquals(2, audioTrack.size) } + @Test + fun getDefaultTrackIndex() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.hasExternalSubtitlesInTracks = true + playerSettings.subtitlePreference = PKSubtitlePreference.EXTERNAL + assertEquals(2, spyTrackSelectionHelper?.getDefaultTrackIndex(spyTrackSelectionHelper.textTracks, TrackSelectionHelper.NONE)) + } + @Test fun extractTextTracksToMap() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.hasExternalSubtitles = true + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + assertEquals(4, spyTrackSelectionHelper.subtitleListMap.size) + assertEquals(2, spyTrackSelectionHelper.subtitleListMap["en"]?.size) + } + + @Test + fun getVideoTracksUniqueIds() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + assertEquals(1, spyTrackSelectionHelper.videoTracks.size) + } + + @Test + fun getCodecUniqueIdsWithABR() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769255, 7203938, PKAbrFilter.BITRATE).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769255, PKAbrFilter.BITRATE).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769254, PKAbrFilter.BITRATE).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769256, 1774250, PKAbrFilter.BITRATE).size) + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1774254, 18316946, PKAbrFilter.BITRATE).size) + assertNotEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(18316946, 183169469, PKAbrFilter.BITRATE).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(-1, 2000000000, PKAbrFilter.BITRATE).size) + + // DO NOT TEST THIS CASE BECAUSE maxAbr > minAbr check is on ExoPlayerWrapper level. This cases will be invalid on TrackSelectionHelper level. + //assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1774250, 769256, PKAbrFilter.BITRATE).size) + + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(142, 380, PKAbrFilter.HEIGHT).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(380, 570, PKAbrFilter.HEIGHT).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(570, 856, PKAbrFilter.HEIGHT).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(150, 350, PKAbrFilter.HEIGHT).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 140, PKAbrFilter.HEIGHT).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(900, 10000, PKAbrFilter.HEIGHT).size) + + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(320, 854, PKAbrFilter.WIDTH).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(854, 1280, PKAbrFilter.WIDTH).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1280, 1920, PKAbrFilter.WIDTH).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(310, 350, PKAbrFilter.WIDTH).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 310, PKAbrFilter.WIDTH).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(2000, 10000, PKAbrFilter.WIDTH).size) + + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(45440, 729600, PKAbrFilter.PIXEL).size) } } From 4c5c2a0737aaf39ddde0956e671e0ac601a43098 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Fri, 11 Jun 2021 13:42:57 +0530 Subject: [PATCH 22/33] - Change Track tests --- .../playkit/player/TrackSelectionHelper.java | 1 + .../player/TrackSelectionHelperTest.kt | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index f4b74a415..a1b2ad69d 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -1360,6 +1360,7 @@ boolean isAdaptive(int rendererIndex, int groupIndex) { */ int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { + // FIXME: CHECK UniqueId with TextUtils if (uniqueId == null) { throw new IllegalArgumentException("uniqueId is null"); } diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 5e2a58ddf..707c42f20 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -320,6 +320,38 @@ internal class TrackSelectionHelperTest { assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(2000, 10000, PKAbrFilter.WIDTH).size) assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(45440, 729600, PKAbrFilter.PIXEL).size) + } + + @Test + fun changeTrack_video() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper?.changeTrack("Video:0,0,1") + verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 1)), any()) + } + + @Test + fun changeTrack_subtitle() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper?.changeTrack("Text:2,1,0") + verify(spyTrackSelectionHelper).overrideTrack(eq(2), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) + } + + @Test + fun changeTrack_audio() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper?.changeTrack("Audio:1,1,0") + verify(spyTrackSelectionHelper).overrideTrack(eq(1), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) } } From 44e766bd222ad6a41f3259c779de0b5027dd26ad Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Fri, 11 Jun 2021 13:54:44 +0530 Subject: [PATCH 23/33] - More tests --- .../playkit/player/TrackSelectionHelper.java | 1 - .../player/TrackSelectionHelperTest.kt | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index a1b2ad69d..e873de34c 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -831,7 +831,6 @@ protected void changeTrack(String uniqueId) { parametersBuilder.setRendererDisabled(TRACK_TYPE_TEXT, uniqueTrackId[TRACK_INDEX] == TRACK_DISABLED); } - SelectionOverride override = retrieveOverrideSelection(uniqueTrackId); overrideTrack(rendererIndex, override, parametersBuilder); } diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 707c42f20..76629952f 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -354,4 +354,26 @@ internal class TrackSelectionHelperTest { spyTrackSelectionHelper?.changeTrack("Audio:1,1,0") verify(spyTrackSelectionHelper).overrideTrack(eq(1), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) } + + @Test + fun overrideMediaVideoCodec() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper?.overrideMediaVideoCodec() + verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) + } + + @Test + fun overrideMediaDefaultABR() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper?.overrideMediaDefaultABR(0, 1774254, PKAbrFilter.BITRATE) + verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1)), any()) + } } From cc923127b77982eefb3608e8a37ea9ae7a48fbf9 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Fri, 11 Jun 2021 18:34:52 +0530 Subject: [PATCH 24/33] - TrackSelectionHelper test for Dash Content only --- .../playkit/player/TrackSelectionHelper.java | 3 +- .../player/TrackSelectionHelperTest.kt | 57 +++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index e873de34c..b7658eeaa 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -1160,7 +1160,8 @@ SelectionOverride retrieveOverrideSelection(int[] uniqueId) { @NonNull SelectionOverride overrideAutoABRTracks(int rendererIndex, int groupIndex) { - SelectionOverride override;List adaptiveTrackIndexesList = new ArrayList<>(); + SelectionOverride override; + List adaptiveTrackIndexesList = new ArrayList<>(); int[] adaptiveTrackIndexes; switch (rendererIndex) { diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index 76629952f..d0416776e 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -162,10 +162,6 @@ internal class TrackSelectionHelperTest { } } - fun buildTracks_warnAboutUnsupportedRendererTypes() { - //TODO - } - @Test fun buildTracks_rawImageTracks() { val dashManifestString: String = TestUtils.getManifestString(ApplicationProvider.getApplicationContext() as Context, "${initialDashAssetPath}sample_dash_custom_manifest_image_tracks") @@ -178,6 +174,19 @@ internal class TrackSelectionHelperTest { } } + @Test + fun getThumbnailInfo_imageTracks() { + val dashManifestString: String = TestUtils.getManifestString(ApplicationProvider.getApplicationContext() as Context, "${initialDashAssetPath}sample_dash_custom_manifest_image_tracks") + val customDashManifest: CustomDashManifest = CustomDashManifestParser().parse(Uri.parse("http://dash.edgesuite.net/akamai/bbb_30fps/bbb_with_multiple_tiled_thumbnails.mpd"), + dashManifestString) + + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_custom_manifest_without_image_tracks")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), customDashManifest) + val thumbnailInfo = spyTrackSelectionHelper.getThumbnailInfo(160000) + assertEquals("http://dash.edgesuite.net/akamai/bbb_30fps/thumbnails_102x58/tile_1.jpg", thumbnailInfo.url) + } + @Test fun getExternalSubtitleLanguage() { val format = Format.Builder().setLanguage("eng-application/mp4").build() @@ -195,6 +204,7 @@ internal class TrackSelectionHelperTest { fun getExternalSubtitleLanguage_null_exception() { val format = Format.Builder().build() val trackSelectionHelper = getTrackSelectionHelper(mockedSelector) + val nullExternalSubtitleformat = format.buildUpon().setLanguage("engapplication/mp4").setSampleMimeType("application/mp4").build() trackSelectionHelper.getExternalSubtitleLanguage(nullExternalSubtitleformat) } @@ -226,6 +236,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) // HEVC only track val spyTrackSelectionHelper = spy(trackSelectionHelper) + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) playerSettings.preferredVideoCodecSettings.allowMixedCodecAdaptiveness = false @@ -240,6 +251,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) // AVC only track val spyTrackSelectionHelper = spy(trackSelectionHelper) + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) playerSettings.preferredVideoCodecSettings = VideoCodecSettings().setCodecPriorityList(listOf(PKVideoCodec.AVC, PKVideoCodec.HEVC)) @@ -253,6 +265,7 @@ internal class TrackSelectionHelperTest { fun filterAdaptiveAudioTracks_ec3() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) playerSettings.preferredAudioCodecSettings = AudioCodecSettings().setCodecPriorityList(listOf(PKAudioCodec.E_AC3, PKAudioCodec.AAC)) val audioTrack = spyTrackSelectionHelper.filterAdaptiveAudioTracks() @@ -263,6 +276,7 @@ internal class TrackSelectionHelperTest { fun getDefaultTrackIndex() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) spyTrackSelectionHelper.hasExternalSubtitlesInTracks = true playerSettings.subtitlePreference = PKSubtitlePreference.EXTERNAL @@ -273,6 +287,7 @@ internal class TrackSelectionHelperTest { fun extractTextTracksToMap() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.hasExternalSubtitles = true spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.subtitleListMap.size) @@ -283,6 +298,7 @@ internal class TrackSelectionHelperTest { fun getVideoTracksUniqueIds() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) assertEquals(1, spyTrackSelectionHelper.videoTracks.size) } @@ -333,6 +349,17 @@ internal class TrackSelectionHelperTest { verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 1)), any()) } + @Test + fun changeTrack_video_adaptive() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper?.changeTrack("Video:0,0,-1") + verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) + } + @Test fun changeTrack_subtitle() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) @@ -376,4 +403,26 @@ internal class TrackSelectionHelperTest { spyTrackSelectionHelper?.overrideMediaDefaultABR(0, 1774254, PKAbrFilter.BITRATE) verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1)), any()) } + + @Test + fun overrideAutoABRTracks_video() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + val selectionOverride = spyTrackSelectionHelper?.overrideAutoABRTracks(0, 0) + assertEquals(4, selectionOverride?.tracks?.size) + } + + @Test + fun overrideAutoABRTracks_audio() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + + val selectionOverride = spyTrackSelectionHelper?.overrideAutoABRTracks(1, 0) + assertEquals(1, selectionOverride?.tracks?.size) + } } From 85786abcb1aaf65930b5e1a922b3d5eff65f0cc9 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Tue, 15 Jun 2021 19:28:55 +0530 Subject: [PATCH 25/33] - TrackSelectionHelper tests with HLS and DASH streams --- .../java/com/kaltura/testhelper/TestUtils.kt | 199 ++++++++++++++++-- .../assets/testdata/m3u8/sample_hls_abr_test | 13 ++ .../assets/testdata/m3u8/sample_hls_dolby_vod | 56 +++++ .../testdata/m3u8/sample_hls_multi_codec | 14 ++ .../assets/testdata/m3u8/sample_hls_tears | 30 +++ .../player/TrackSelectionHelperTest.kt | 160 +++++++++++--- 6 files changed, 420 insertions(+), 52 deletions(-) create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_abr_test create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_dolby_vod create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_multi_codec create mode 100644 playkit/src/test/assets/testdata/m3u8/sample_hls_tears diff --git a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt index 10d3f4225..07acffb89 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/TestUtils.kt @@ -2,7 +2,9 @@ package com.kaltura.testhelper import android.content.Context import android.net.Uri +import com.kaltura.android.exoplayer2.C import com.kaltura.android.exoplayer2.Format +import com.kaltura.android.exoplayer2.metadata.Metadata import com.kaltura.android.exoplayer2.source.TrackGroup import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.source.dash.manifest.AdaptationSet @@ -10,10 +12,14 @@ import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifest import com.kaltura.android.exoplayer2.source.dash.manifest.DashManifestParser import com.kaltura.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist import com.kaltura.android.exoplayer2.source.hls.playlist.HlsPlaylistParser +import com.kaltura.android.exoplayer2.util.MimeTypes +import com.kaltura.android.exoplayer2.util.Util import java.io.BufferedReader import java.io.IOException import java.io.InputStream import java.io.InputStreamReader +import java.util.* +import kotlin.collections.ArrayList open class TestUtils { companion object { @@ -61,34 +67,193 @@ open class TestUtils { @Throws(IOException::class) fun getTrackGroupArrayFromHlsManifest(context: Context, fileName: String): TrackGroupArray { val hlsMasterPlaylist = parseLocalHlsManifest(context, fileName) - val trackGroups: ArrayList = arrayListOf() - val videoFormat: ArrayList = arrayListOf() - val audioFormat: ArrayList = arrayListOf() - val subtitleFormat: ArrayList = arrayListOf() + var trackGroups: ArrayList = arrayListOf() + //MimeTypes.getMediaMimeType(Util.getCodecsOfType(trackGroups.get(2).getFormat(0).codecs, C.TRACK_TYPE_VIDEO)) + trackGroups = extractHlsVideoTracks(hlsMasterPlaylist, trackGroups) + trackGroups = extractHlsAudioTracks(hlsMasterPlaylist, trackGroups) - if (hlsMasterPlaylist.variants.isNotEmpty()) { - for ((variantIndex, variantValue) in hlsMasterPlaylist.variants.withIndex()) { - videoFormat.add(variantValue.format) + if (hlsMasterPlaylist.subtitles.isNotEmpty()) { + for ((subtitlesIndex, subtitlesValue) in hlsMasterPlaylist.subtitles.withIndex()) { + trackGroups.add(TrackGroup(subtitlesValue.format)) } - trackGroups.add(TrackGroup(*videoFormat.toTypedArray())) } - if (hlsMasterPlaylist.audios.isNotEmpty()) { - for ((audiosIndex, audiosValue) in hlsMasterPlaylist.audios.withIndex()) { - audioFormat.add(audiosValue.format) + return TrackGroupArray(*trackGroups.toTypedArray()) + } + + private fun deriveVideoFormat(variantFormat: Format): Format { + val codecs: String? = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_VIDEO) + val sampleMimeType: String? = MimeTypes.getMediaMimeType(codecs) + return Format.Builder() + .setId(variantFormat.id) + .setLabel(variantFormat.label) + .setContainerMimeType(variantFormat.containerMimeType) + .setSampleMimeType(sampleMimeType ?: "video/avc") + .setCodecs(codecs) + .setMetadata(variantFormat.metadata) + .setAverageBitrate(variantFormat.averageBitrate) + .setPeakBitrate(variantFormat.peakBitrate) + .setWidth(variantFormat.width) + .setHeight(variantFormat.height) + .setFrameRate(variantFormat.frameRate) + .setSelectionFlags(variantFormat.selectionFlags) + .setRoleFlags(variantFormat.roleFlags) + .build() + } + + private fun deriveAudioFormat(variantFormat: Format, mediaTagFormat: Format?, isPrimaryTrackInVariant: Boolean): Format { + val codecs: String? + val metadata: Metadata? + var channelCount = Format.NO_VALUE + var selectionFlags = 0 + var roleFlags = 0 + var language: String? = null + var label: String? = null + if (mediaTagFormat != null) { + codecs = mediaTagFormat.codecs + metadata = mediaTagFormat.metadata + channelCount = mediaTagFormat.channelCount + selectionFlags = mediaTagFormat.selectionFlags + roleFlags = mediaTagFormat.roleFlags + language = mediaTagFormat.language + label = mediaTagFormat.label + } else { + codecs = Util.getCodecsOfType(variantFormat.codecs, C.TRACK_TYPE_AUDIO) + metadata = variantFormat.metadata + if (isPrimaryTrackInVariant) { + channelCount = variantFormat.channelCount + selectionFlags = variantFormat.selectionFlags + roleFlags = variantFormat.roleFlags + language = variantFormat.language + label = variantFormat.label } - trackGroups.add(TrackGroup(*audioFormat.toTypedArray())) } + val sampleMimeType: String? = MimeTypes.getMediaMimeType(codecs) + val averageBitrate = if (isPrimaryTrackInVariant) variantFormat.averageBitrate else Format.NO_VALUE + val peakBitrate = if (isPrimaryTrackInVariant) variantFormat.peakBitrate else Format.NO_VALUE + return Format.Builder() + .setId(variantFormat.id) + .setLabel(label) + .setContainerMimeType(variantFormat.containerMimeType) + .setSampleMimeType(sampleMimeType ?: "audio/mp4") + .setCodecs(codecs) + .setMetadata(metadata) + .setAverageBitrate(averageBitrate) + .setPeakBitrate(peakBitrate) + .setChannelCount(channelCount) + .setSelectionFlags(selectionFlags) + .setRoleFlags(roleFlags) + .setLanguage(language) + .build() + } - if (hlsMasterPlaylist.subtitles.isNotEmpty()) { - for ((subtitlesIndex, subtitlesValue) in hlsMasterPlaylist.subtitles.withIndex()) { - subtitleFormat.add(subtitlesValue.format) + private fun extractHlsVideoTracks(masterPlaylist: HlsMasterPlaylist, trackGroups: ArrayList): ArrayList { + val variantTypes = IntArray(masterPlaylist.variants.size) + var videoVariantCount = 0 + var audioVariantCount = 0 + for (i in 0 until masterPlaylist.variants.size) { + val variant: HlsMasterPlaylist.Variant = masterPlaylist.variants.get(i) + val format: Format = variant.format + if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) { + variantTypes[i] = C.TRACK_TYPE_VIDEO + videoVariantCount++ + } else if (Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_AUDIO) != null) { + variantTypes[i] = C.TRACK_TYPE_AUDIO + audioVariantCount++ + } else { + variantTypes[i] = C.TRACK_TYPE_UNKNOWN + } + } + var useVideoVariantsOnly = false + var useNonAudioVariantsOnly = false + var selectedVariantsCount = variantTypes.size + if (videoVariantCount > 0) { + // We've identified some variants as definitely containing video. Assume variants within the + // master playlist are marked consistently, and hence that we have the full set. Filter out + // any other variants, which are likely to be audio only. + useVideoVariantsOnly = true + selectedVariantsCount = videoVariantCount + } else if (audioVariantCount < variantTypes.size) { + // We've identified some variants, but not all, as being audio only. Filter them out to leave + // the remaining variants, which are likely to contain video. + useNonAudioVariantsOnly = true + selectedVariantsCount = variantTypes.size - audioVariantCount + } + val selectedPlaylistUrls = arrayOfNulls(selectedVariantsCount) + val selectedPlaylistFormats = arrayOfNulls(selectedVariantsCount) + val selectedVariantIndices = IntArray(selectedVariantsCount) + var outIndex = 0 + for (i in 0 until masterPlaylist.variants.size) { + if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO) + && (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) { + val variant: HlsMasterPlaylist.Variant = masterPlaylist.variants.get(i) + selectedPlaylistUrls[outIndex] = variant.url + selectedPlaylistFormats[outIndex] = variant.format + selectedVariantIndices[outIndex++] = i } - trackGroups.add(TrackGroup(*subtitleFormat.toTypedArray())) } + val codecs = selectedPlaylistFormats[0]!!.codecs + val numberOfVideoCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_VIDEO) + val numberOfAudioCodecs = Util.getCodecCountOfType(codecs, C.TRACK_TYPE_AUDIO) + val codecsStringAllowsChunklessPreparation = numberOfAudioCodecs <= 1 && numberOfVideoCodecs <= 1 && numberOfAudioCodecs + numberOfVideoCodecs > 0 + // if (codecsStringAllowsChunklessPreparation) { + if (numberOfVideoCodecs > 0) { + val videoFormats = ArrayList() + for (index in 0 until selectedVariantsCount) { + videoFormats.add(deriveVideoFormat(selectedPlaylistFormats[index]!!)) + } + trackGroups.add(TrackGroup(*videoFormats.toTypedArray())) + if (numberOfAudioCodecs > 0 + && (masterPlaylist.muxedAudioFormat != null || masterPlaylist.audios.isEmpty())) { + trackGroups.add(TrackGroup(deriveAudioFormat(selectedPlaylistFormats[0]!!, masterPlaylist.muxedAudioFormat, false))) + } + masterPlaylist.muxedCaptionFormats?.let { ccFormats -> + for (i in ccFormats.indices) { + trackGroups.add(TrackGroup(ccFormats[i])) + } + } + } else /* numberOfAudioCodecs > 0 */ { + // Variants only contain audio. + val audioFormats = ArrayList() + for (index in 0 until selectedVariantsCount) { + audioFormats.add(deriveAudioFormat(selectedPlaylistFormats[index]!!, masterPlaylist.muxedAudioFormat, true)) + } + trackGroups.add(TrackGroup(*audioFormats.toTypedArray())) + } + val id3TrackGroup = TrackGroup( + Format.Builder() + .setId("ID3") + .setSampleMimeType(MimeTypes.APPLICATION_ID3) + .build()) + trackGroups.add(id3TrackGroup) + // } - return TrackGroupArray(*trackGroups.toTypedArray()) + return trackGroups } + private fun extractHlsAudioTracks(masterPlaylist: HlsMasterPlaylist, trackGroups: ArrayList): ArrayList { + val scratchPlaylistFormats = ArrayList( /* initialCapacity= */masterPlaylist.audios.size) + val alreadyGroupedNames = HashSet() + for (renditionByNameIndex in masterPlaylist.audios.indices) { + val name: String = masterPlaylist.audios[renditionByNameIndex].name + if (!alreadyGroupedNames.add(name)) { + // This name already has a corresponding group. + continue + } + scratchPlaylistFormats.clear() + // Group all renditions with matching name. + for (renditionIndex in masterPlaylist.audios.indices) { + if (Util.areEqual(name, masterPlaylist.audios[renditionIndex].name)) { + val rendition: HlsMasterPlaylist.Rendition = masterPlaylist.audios[renditionIndex] + scratchPlaylistFormats.add(rendition.format) + } + } + } + + if (scratchPlaylistFormats.size > 0) { + trackGroups.add(TrackGroup(*scratchPlaylistFormats.toTypedArray())) + } + return trackGroups + } } } diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_abr_test b/playkit/src/test/assets/testdata/m3u8/sample_hls_abr_test new file mode 100644 index 000000000..260d7738e --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_abr_test @@ -0,0 +1,13 @@ +#EXTM3U + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio0",NAME="Hindi",LANGUAGE="hi",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="1",URI="https://exmple.net/fpshls/p/sp/pqwert1_pxycp3t2/name/a.mp4/index.m3u8/index-a1.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio0",NAME="English",LANGUAGE="en",AUTOSELECT=NO,DEFAULT=NO,CHANNELS="1",URI="https://exmple.net/fpshls/p/sp/pqwert1_w62mxbkz/name/a.mp4/index.m3u8/index-a1.m3u8" + +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=769255,RESOLUTION=320x142,FRAME-RATE=25.000,CODECS="avc1.42c01e,mp4a.40.2",AUDIO="audio0" +https://exmple.net/fpshls/p/sp/pqwert1_h1q0wy1u/name/a.mp4/index.m3u8/index-v1-a1.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1774254,RESOLUTION=854x380,FRAME-RATE=25.000,CODECS="avc1.42c01e,mp4a.40.2",AUDIO="audio0" +https://exmple.net/fpshls/p/sp/pqwert1_uo5tcmjd/name/a.mp4/index.m3u8/index-v1-a1.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7203938,RESOLUTION=1280x570,FRAME-RATE=25.000,CODECS="avc1.42c01e,mp4a.40.2",AUDIO="audio0" +https://exmple.net/fpshls/p/sp/pqwert1_h8c2v7qh/name/a.mp4/index.m3u8/index-v1-a1.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=18316946,RESOLUTION=1920x856,FRAME-RATE=25.000,CODECS="avc1.42c01e,mp4a.40.2",AUDIO="audio0" +https://exmple.net/fpshls/p/sp/pqwert1_mmf5jo25/name/a.mp4/index.m3u8/index-v1-a1.m3u8 diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_dolby_vod b/playkit/src/test/assets/testdata/m3u8/sample_hls_dolby_vod new file mode 100644 index 000000000..844d9d5d5 --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_dolby_vod @@ -0,0 +1,56 @@ +#EXTM3U +#EXT-X-VERSION:7 +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_eng/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac_stereo",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_eng/prog_index.m3u8" + + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6303166,BANDWIDTH=11265828,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=4750392,BANDWIDTH=7979513,CODECS="avc1.640028,ec-3",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3202998,BANDWIDTH=5134820,CODECS="avc1.4d001f,ec-3",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2196234,BANDWIDTH=3305648,CODECS="avc1.4d001f,ec-3",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1259726,BANDWIDTH=1918208,CODECS="avc1.4d001e,ec-3",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=884732,BANDWIDTH=1270234,CODECS="avc1.42001e,ec-3",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=503255,BANDWIDTH=669126,CODECS="avc1.42e015,ec-3",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=229236,BANDWIDTH=271811,CODECS="avc1.42e00b,ec-3",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-ec3-20",CLOSED-CAPTIONS=NONE,AUDIO="ec3_stereo" +video_192k/prog_index.m3u8 + +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=6305791,BANDWIDTH=11204864,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_6000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=4753017,BANDWIDTH=7981552,CODECS="avc1.640028,mp4a.40.2",RESOLUTION=1920x1080,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_4500k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=3205623,BANDWIDTH=5137859,CODECS="avc1.4d001f,mp4a.40.2",RESOLUTION=1280x720,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_3000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=2198859,BANDWIDTH=3308768,CODECS="avc1.4d001f,mp4a.40.2",RESOLUTION=960x540,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_2000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=1262351,BANDWIDTH=1921247,CODECS="avc1.4d001e,mp4a.40.2",RESOLUTION=768x432,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_1000k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=887357,BANDWIDTH=1273273,CODECS="avc1.42001e,mp4a.40.2",RESOLUTION=640x360,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_730k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=505880,BANDWIDTH=672165,CODECS="avc1.42e015,mp4a.40.2",RESOLUTION=480x270,FRAME-RATE=25.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_365k/prog_index.m3u8 +#EXT-X-STREAM-INF:AVERAGE-BANDWIDTH=231861,BANDWIDTH=274850,CODECS="avc1.42e00b,mp4a.40.2",RESOLUTION=416x234,FRAME-RATE=4.000,SUBTITLES="subs-aac",CLOSED-CAPTIONS=NONE,AUDIO="aac_stereo" +video_192k/prog_index.m3u8 + + +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=427577,BANDWIDTH=1673200,CODECS="avc1.640028",RESOLUTION=1920x1080,URI="video_6000k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=339923,BANDWIDTH=1304431,CODECS="avc1.640028",RESOLUTION=1920x1080,URI="video_4500k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=250827,BANDWIDTH=973262,CODECS="avc1.4d001f",RESOLUTION=1280x720,URI="video_3000k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=172544,BANDWIDTH=620400,CODECS="avc1.4d001f",RESOLUTION=960x540,URI="video_2000k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=107738,BANDWIDTH=397693,CODECS="avc1.4d001e",RESOLUTION=768x432,URI="video_1000k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=69062,BANDWIDTH=228493,CODECS="avc1.42001e",RESOLUTION=640x360,URI="video_730k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=59513,BANDWIDTH=157562,CODECS="avc1.42e015",RESOLUTION=480x270,URI="video_365k/iframe_index.m3u8" +#EXT-X-I-FRAME-STREAM-INF:AVERAGE-BANDWIDTH=39939,BANDWIDTH=81968,CODECS="avc1.42e00b",RESOLUTION=416x234,URI="video_192k/iframe_index.m3u8" + + +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-ec3-20",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/ec3_20_eng/subs.m3u8" +#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs-aac",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="eng",URI="subtitles/aac_eng/subs.m3u8" \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_codec b/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_codec new file mode 100644 index 000000000..2e6e415db --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_multi_codec @@ -0,0 +1,14 @@ +#EXTM3U +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-VERSION:6 + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio_128",NAME="audio_128.m3u8",LANGUAGE="en",URI="audio_128.m3u8" + +#EXT-X-STREAM-INF:BANDWIDTH=2101985,AVERAGE-BANDWIDTH=1796254,CODECS="avc1.4D401F,mp4a.40.2",RESOLUTION=854x480,AUDIO="audio_128" +video_h265_480p_1750.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2739681,AVERAGE-BANDWIDTH=2372431,CODECS="avc1.4D401F,mp4a.40.2",RESOLUTION=1280x720,AUDIO="audio_128" +Video_h265_720p_2350.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2049427,AVERAGE-BANDWIDTH=1824322,CODECS="hev1.1.6.L90.90,mp4a.40.2",RESOLUTION=854x480,AUDIO="audio_128" +video_h264_480p_1750.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2706785,AVERAGE-BANDWIDTH=2421640,CODECS="hev1.1.6.L93.90,mp4a.40.2",RESOLUTION=1280x720,AUDIO="audio_128" +video_h264_720p_2350.m3u8 \ No newline at end of file diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_tears b/playkit/src/test/assets/testdata/m3u8/sample_hls_tears new file mode 100644 index 000000000..7e3eac5f9 --- /dev/null +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_tears @@ -0,0 +1,30 @@ +#EXTM3U +#EXT-X-VERSION:1 + +#EXT-X-STREAM-INF:BANDWIDTH=493000,CODECS="avc1.66.30",RESOLUTION=224x100,FRAME-RATE=24 +tears-of-steel-audio_eng=64008-video_eng=401000.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=932000,CODECS="avc1.66.30",RESOLUTION=448x200,FRAME-RATE=24 +tears-of-steel-audio_eng=128002-video_eng=751000.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1197000,CODECS="avc1.77.31",RESOLUTION=784x350,FRAME-RATE=24 +tears-of-steel-audio_eng=128002-video_eng=1001000.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=1727000,CODECS="avc1.100.40",RESOLUTION=1680x750,FRAME-RATE=24,VIDEO-RANGE=SDR +tears-of-steel-audio_eng=128002-video_eng=1501000.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2468000,CODECS="avc1.100.40",RESOLUTION=1680x750,FRAME-RATE=24,VIDEO-RANGE=SDR +tears-of-steel-audio_eng=128002-video_eng=2200000.m3u8 + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_ddp_51_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ac3",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="6",URI="audio_dd_51_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="ec3_stereo",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_ddp_2ch_chi/prog_index.m3u8" + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English",LANGUAGE="en",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_eng/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Español",LANGUAGE="es",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_spa/prog_index.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="中文",LANGUAGE="zh",DEFAULT=NO,AUTOSELECT=YES,CHANNELS="2",URI="audio_aac_2ch_chi/prog_index.m3u8" + diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index d0416776e..cce354d56 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -15,6 +15,7 @@ import com.kaltura.android.exoplayer2.source.TrackGroupArray import com.kaltura.android.exoplayer2.trackselection.* import com.kaltura.android.exoplayer2.upstream.BandwidthMeter import com.kaltura.playkit.* +import com.kaltura.playkit.utils.Consts.TRACK_TYPE_AUDIO import com.kaltura.testhelper.FakeRendererCapabilities import com.kaltura.testhelper.FakeTimeline import com.kaltura.testhelper.TestUtils @@ -23,8 +24,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.* -import kotlin.collections.ArrayList -import kotlin.collections.HashMap @RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { @@ -32,7 +31,6 @@ internal class TrackSelectionHelperTest { private val initialDashAssetPath: String = "testdata/mpd/" private val dashFilePrefix: String = "sample_dash" private val initialHlsAssetPath: String = "testdata/m3u8/" - private val hlsFilePrefix: String = "sample_hls" private lateinit var actualSelector: TrackSelector private lateinit var actualTrackSelectionArray: TrackSelectionArray @@ -89,7 +87,7 @@ internal class TrackSelectionHelperTest { MediaSource.MediaPeriodId(timeline.getUidOfPeriod(0)), FakeTimeline()) - var exoTrackSelectionArray = ArrayList() + val exoTrackSelectionArray = ArrayList() for (exoTrackSelection: ExoTrackSelection in trackSelectorResult.selections) { exoTrackSelectionArray.add(exoTrackSelection) } @@ -125,24 +123,25 @@ internal class TrackSelectionHelperTest { } @Test - fun buildTracks_mappedTrackInfo_instream_subtitles() { - // Dash + fun buildTracks_mappedTrackInfo_instream_subtitles_dash() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) - spyTrackSelectionHelper?.prepareTracks(TrackSelectionArray(), null) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) verify(spyTrackSelectionHelper)?.buildTracks(ArrayList()) - val pkTracks: PKTracks? = spyTrackSelectionHelper?.buildTracks(null) + val pkTracks: PKTracks? = spyTrackSelectionHelper.buildTracks(null) assertEquals(6, pkTracks?.textTracks?.size) + } - // HLS + @Test + fun buildTracks_mappedTrackInfo_instream_subtitles_hls() { val trackSelectionHelperHls = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_harold")) val spyTrackSelectionHelperHls = spy(trackSelectionHelperHls) spyTrackSelectionHelperHls?.prepareTracks(TrackSelectionArray(), null) verify(spyTrackSelectionHelperHls)?.buildTracks(ArrayList()) val pkTracksHls: PKTracks? = spyTrackSelectionHelperHls?.buildTracks(null) - assertEquals(9, pkTracksHls?.textTracks?.size) + assertEquals(8, pkTracksHls?.textTracks?.size) } @Test @@ -154,7 +153,7 @@ internal class TrackSelectionHelperTest { } @Test - fun buildTracks_mappedTrackInfo_checkTracksUnavailability_false() { + fun buildTracks_mappedTrackInfo_checkTracksUnavailability_false_dash() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_no_tracks")) trackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) trackSelectionHelper.prepareTracks(TrackSelectionArray(), null).let { @@ -232,7 +231,7 @@ internal class TrackSelectionHelperTest { } @Test - fun filterVideoTracks_hevc() { + fun filterVideoTracks_hevc_dash() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) // HEVC only track val spyTrackSelectionHelper = spy(trackSelectionHelper) @@ -247,7 +246,22 @@ internal class TrackSelectionHelperTest { } @Test - fun filterVideoTracks_avc() { + fun filterVideoTracks_hevc_hls() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_multi_codec")) + // HEVC only track + val spyTrackSelectionHelper = spy(trackSelectionHelper) + + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) + `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) + playerSettings.preferredVideoCodecSettings.allowMixedCodecAdaptiveness = false + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) + val videoTrack = spyTrackSelectionHelper.filterVideoTracks() + assertEquals(2, videoTrack.size) + assertEquals("HEVC Track Found: ", PKVideoCodec.HEVC, videoTrack.get(0).codecType) + } + + @Test + fun filterVideoTracks_avc_dash() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_hevc_and_avc_mix")) // AVC only track val spyTrackSelectionHelper = spy(trackSelectionHelper) @@ -261,6 +275,21 @@ internal class TrackSelectionHelperTest { assertEquals("AVC Track Found: ", PKVideoCodec.AVC, videoTrack.get(0).codecType) } + @Test + fun filterVideoTracks_avc_hls() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_multi_codec")) + // AVC only track + val spyTrackSelectionHelper = spy(trackSelectionHelper) + + `when`(spyTrackSelectionHelper.videoCodecsSupportedInHardware()).thenReturn(true) + `when`(spyTrackSelectionHelper.isCodecSupported(anyString(), eq(TrackSelectionHelper.TrackType.VIDEO), anyBoolean())).thenReturn(true) + playerSettings.preferredVideoCodecSettings = VideoCodecSettings().setCodecPriorityList(listOf(PKVideoCodec.AVC, PKVideoCodec.HEVC)) + spyTrackSelectionHelper.prepareTracks(TrackSelectionArray(), null) + val videoTrack = spyTrackSelectionHelper.filterVideoTracks() + assertEquals(3, videoTrack.size) + assertEquals("AVC Track Found: ", PKVideoCodec.AVC, videoTrack.get(0).codecType) + } + @Test fun filterAdaptiveAudioTracks_ec3() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) @@ -277,10 +306,10 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) spyTrackSelectionHelper.hasExternalSubtitlesInTracks = true playerSettings.subtitlePreference = PKSubtitlePreference.EXTERNAL - assertEquals(2, spyTrackSelectionHelper?.getDefaultTrackIndex(spyTrackSelectionHelper.textTracks, TrackSelectionHelper.NONE)) + assertEquals(2, spyTrackSelectionHelper.getDefaultTrackIndex(spyTrackSelectionHelper.textTracks, TrackSelectionHelper.NONE)) } @Test @@ -289,7 +318,7 @@ internal class TrackSelectionHelperTest { val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.hasExternalSubtitles = true - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.subtitleListMap.size) assertEquals(2, spyTrackSelectionHelper.subtitleListMap["en"]?.size) } @@ -299,16 +328,51 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) assertEquals(1, spyTrackSelectionHelper.videoTracks.size) } @Test - fun getCodecUniqueIdsWithABR() { + fun getCodecUniqueIdsWithABR_dash() { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769255, 7203938, PKAbrFilter.BITRATE).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769255, PKAbrFilter.BITRATE).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769254, PKAbrFilter.BITRATE).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769256, 1774250, PKAbrFilter.BITRATE).size) + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1774254, 18316946, PKAbrFilter.BITRATE).size) + assertNotEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(18316946, 183169469, PKAbrFilter.BITRATE).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(-1, 2000000000, PKAbrFilter.BITRATE).size) + + // DO NOT TEST THIS CASE BECAUSE maxAbr > minAbr check is on ExoPlayerWrapper level. This cases will be invalid on TrackSelectionHelper level. + //assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1774250, 769256, PKAbrFilter.BITRATE).size) + + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(142, 380, PKAbrFilter.HEIGHT).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(380, 570, PKAbrFilter.HEIGHT).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(570, 856, PKAbrFilter.HEIGHT).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(150, 350, PKAbrFilter.HEIGHT).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 140, PKAbrFilter.HEIGHT).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(900, 10000, PKAbrFilter.HEIGHT).size) + + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(320, 854, PKAbrFilter.WIDTH).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(854, 1280, PKAbrFilter.WIDTH).size) + assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1280, 1920, PKAbrFilter.WIDTH).size) + assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(310, 350, PKAbrFilter.WIDTH).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 310, PKAbrFilter.WIDTH).size) + assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(2000, 10000, PKAbrFilter.WIDTH).size) + + assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(45440, 729600, PKAbrFilter.PIXEL).size) + } + + @Test + fun getCodecUniqueIdsWithABR_hls() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_abr_test")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769255, 7203938, PKAbrFilter.BITRATE).size) assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769255, PKAbrFilter.BITRATE).size) @@ -343,9 +407,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.changeTrack("Video:0,0,1") + spyTrackSelectionHelper.changeTrack("Video:0,0,1") verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 1)), any()) } @@ -354,9 +418,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.changeTrack("Video:0,0,-1") + spyTrackSelectionHelper.changeTrack("Video:0,0,-1") verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) } @@ -365,9 +429,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.changeTrack("Text:2,1,0") + spyTrackSelectionHelper.changeTrack("Text:2,1,0") verify(spyTrackSelectionHelper).overrideTrack(eq(2), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) } @@ -376,9 +440,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.changeTrack("Audio:1,1,0") + spyTrackSelectionHelper.changeTrack("Audio:1,1,0") verify(spyTrackSelectionHelper).overrideTrack(eq(1), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) } @@ -387,9 +451,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.overrideMediaVideoCodec() + spyTrackSelectionHelper.overrideMediaVideoCodec() verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) } @@ -398,9 +462,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - spyTrackSelectionHelper?.overrideMediaDefaultABR(0, 1774254, PKAbrFilter.BITRATE) + spyTrackSelectionHelper.overrideMediaDefaultABR(0, 1774254, PKAbrFilter.BITRATE) verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1)), any()) } @@ -409,9 +473,9 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - val selectionOverride = spyTrackSelectionHelper?.overrideAutoABRTracks(0, 0) + val selectionOverride = spyTrackSelectionHelper.overrideAutoABRTracks(0, 0) assertEquals(4, selectionOverride?.tracks?.size) } @@ -420,9 +484,35 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper?.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) - val selectionOverride = spyTrackSelectionHelper?.overrideAutoABRTracks(1, 0) + val selectionOverride = spyTrackSelectionHelper.overrideAutoABRTracks(1, 0) assertEquals(1, selectionOverride?.tracks?.size) } + + @Test + fun getCurrentVideoBitrate() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + + spyTrackSelectionHelper.changeTrack("Video:0,0,1") + + assertEquals(1774254, spyTrackSelectionHelper.currentVideoBitrate) + assertEquals(131596, spyTrackSelectionHelper.currentAudioBitrate) + assertEquals(380, spyTrackSelectionHelper.currentVideoHeight) + assertEquals(854, spyTrackSelectionHelper.currentVideoWidth) + } + + @Test + fun getPreferredTrackId() { + val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) + val spyTrackSelectionHelper = spy(trackSelectionHelper) + spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) + spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + playerSettings.setPreferredAudioTrack(PKTrackConfig().setPreferredMode(PKTrackConfig.Mode.AUTO)) + + assertEquals("Audio:1,0,0", spyTrackSelectionHelper.getPreferredTrackId(TRACK_TYPE_AUDIO)) + } } From 92abfe9582686d9b3c528375f359ca6b46395183 Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Mon, 21 Jun 2021 17:49:32 +0530 Subject: [PATCH 26/33] - Code cleanup --- .../player/TrackSelectionHelperTest.kt | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt index cce354d56..a7336d203 100644 --- a/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt +++ b/playkit/src/test/java/com/kaltura/playkit/player/TrackSelectionHelperTest.kt @@ -25,6 +25,24 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.* +/** + * TrackSelectionHelper unit test class + * + * We have two types of DefaultTrackSelector (mockedSelector and actualSelector) + * Whenever we need to build actual tracks from manifest, we are using actualSelector + * + * Test manifests are in assets/testdata + * + * We are using Fake classes for testing purpose only. + * Those classes are part of com.kaltura.testhelper + * + * TestUtils class has the methods for manifest parsing and getting the tracks from manifest. + * + * In case if the tracks are present in manifest but not coming in the prepareTracks method's mappedTrackInfo + * then please check the logic in FakeRendererCapabilities + * + * In case if you want to get all the tracks, you need to call prepareTracks of TrackSelectionHelper + */ @RunWith(AndroidJUnit4::class) internal class TrackSelectionHelperTest { @@ -32,8 +50,8 @@ internal class TrackSelectionHelperTest { private val dashFilePrefix: String = "sample_dash" private val initialHlsAssetPath: String = "testdata/m3u8/" + private lateinit var trackSelectionArray: TrackSelectionArray private lateinit var actualSelector: TrackSelector - private lateinit var actualTrackSelectionArray: TrackSelectionArray private var mockedSelector: DefaultTrackSelector = mock(DefaultTrackSelector::class.java) private val lastSelectedTrackIds = arrayOf(TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE, TrackSelectionHelper.NONE) @@ -67,6 +85,7 @@ internal class TrackSelectionHelperTest { val timeline = FakeTimeline() val filePath: String + // Checks with type of manifest it is if (fileName.startsWith(dashFilePrefix)) { filePath = "${initialDashAssetPath}${fileName}" trackGroupArray = TestUtils.getTrackGroupArrayFromDashManifest( @@ -92,13 +111,17 @@ internal class TrackSelectionHelperTest { exoTrackSelectionArray.add(exoTrackSelection) } - actualTrackSelectionArray = TrackSelectionArray(*exoTrackSelectionArray.toTypedArray()) + trackSelectionArray = TrackSelectionArray(*exoTrackSelectionArray.toTypedArray()) + // Mandatory step to get the mappedTrackInfo in TrackSelectionHelper actualSelector.onSelectionActivated(trackSelectorResult.info) return actualSelector } + /** + * Build TrackSelectionHelper with real tracks + */ private fun getTrackSelectionHelper(selector: TrackSelector): TrackSelectionHelper { val trackSelectionHelper = TrackSelectionHelper( ApplicationProvider.getApplicationContext(), @@ -306,7 +329,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.hasExternalSubtitlesInTracks = true playerSettings.subtitlePreference = PKSubtitlePreference.EXTERNAL assertEquals(2, spyTrackSelectionHelper.getDefaultTrackIndex(spyTrackSelectionHelper.textTracks, TrackSelectionHelper.NONE)) @@ -318,7 +341,7 @@ internal class TrackSelectionHelperTest { val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.hasExternalSubtitles = true - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.subtitleListMap.size) assertEquals(2, spyTrackSelectionHelper.subtitleListMap["en"]?.size) } @@ -328,7 +351,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_external_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) assertEquals(1, spyTrackSelectionHelper.videoTracks.size) } @@ -337,7 +360,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769255, 7203938, PKAbrFilter.BITRATE).size) assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769255, PKAbrFilter.BITRATE).size) @@ -372,7 +395,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_hls_abr_test")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) assertEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(769255, 7203938, PKAbrFilter.BITRATE).size) assertEquals(2, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(0, 769255, PKAbrFilter.BITRATE).size) @@ -382,7 +405,7 @@ internal class TrackSelectionHelperTest { assertNotEquals(4, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(18316946, 183169469, PKAbrFilter.BITRATE).size) assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(-1, 2000000000, PKAbrFilter.BITRATE).size) - // DO NOT TEST THIS CASE BECAUSE maxAbr > minAbr check is on ExoPlayerWrapper level. This cases will be invalid on TrackSelectionHelper level. + // Don't test this because maxAbr > minAbr check is on ExoPlayerWrapper level. This cases will be invalid on TrackSelectionHelper level. //assertEquals(5, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(1774250, 769256, PKAbrFilter.BITRATE).size) assertEquals(3, spyTrackSelectionHelper.getCodecUniqueIdsWithABR(142, 380, PKAbrFilter.HEIGHT).size) @@ -407,7 +430,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.changeTrack("Video:0,0,1") verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 1)), any()) @@ -418,7 +441,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.changeTrack("Video:0,0,-1") verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) @@ -429,7 +452,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_with_subtitle")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.changeTrack("Text:2,1,0") verify(spyTrackSelectionHelper).overrideTrack(eq(2), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) @@ -440,7 +463,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.changeTrack("Audio:1,1,0") verify(spyTrackSelectionHelper).overrideTrack(eq(1), eq(DefaultTrackSelector.SelectionOverride(1, 0)), any()) @@ -451,7 +474,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.overrideMediaVideoCodec() verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1,2,3)), any()) @@ -462,7 +485,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.overrideMediaDefaultABR(0, 1774254, PKAbrFilter.BITRATE) verify(spyTrackSelectionHelper).overrideTrack(eq(0), eq(DefaultTrackSelector.SelectionOverride(0, 0,1)), any()) @@ -473,10 +496,10 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) val selectionOverride = spyTrackSelectionHelper.overrideAutoABRTracks(0, 0) - assertEquals(4, selectionOverride?.tracks?.size) + assertEquals(4, selectionOverride.tracks.size) } @Test @@ -484,10 +507,10 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_audio_only")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) val selectionOverride = spyTrackSelectionHelper.overrideAutoABRTracks(1, 0) - assertEquals(1, selectionOverride?.tracks?.size) + assertEquals(1, selectionOverride.tracks.size) } @Test @@ -495,7 +518,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) spyTrackSelectionHelper.changeTrack("Video:0,0,1") @@ -510,7 +533,7 @@ internal class TrackSelectionHelperTest { val trackSelectionHelper = getTrackSelectionHelper(buildActualTrackSelector("sample_dash_clear_h264_tears")) val spyTrackSelectionHelper = spy(trackSelectionHelper) spyTrackSelectionHelper.setTracksErrorListener(mock(TrackSelectionHelper.TracksErrorListener::class.java)) - spyTrackSelectionHelper.prepareTracks(actualTrackSelectionArray, null) + spyTrackSelectionHelper.prepareTracks(trackSelectionArray, null) playerSettings.setPreferredAudioTrack(PKTrackConfig().setPreferredMode(PKTrackConfig.Mode.AUTO)) assertEquals("Audio:1,0,0", spyTrackSelectionHelper.getPreferredTrackId(TRACK_TYPE_AUDIO)) From 3c8ce7dcd288a0bed18d34fd299d258968bc3e7a Mon Sep 17 00:00:00 2001 From: Gourav Saxena Date: Tue, 22 Jun 2021 09:37:04 +0530 Subject: [PATCH 27/33] - Removed FIXME from ExoPlayerWrapper, will include separately in its tests - Fixed FIXME in TrackSelectionHelper --- .../java/com/kaltura/playkit/player/ExoPlayerWrapper.java | 2 +- .../java/com/kaltura/playkit/player/TrackSelectionHelper.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java index 0162941c6..3b306669a 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/ExoPlayerWrapper.java @@ -1006,7 +1006,7 @@ public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray tra shouldGetTracksInfo = !trackSelectionHelper.prepareTracks(trackSelections, customDashManifest); } } - //FIXME: Unit Test trackSelectionHelper can be null here + trackSelectionHelper.notifyAboutTrackChange(trackSelections); } diff --git a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java index b7658eeaa..2c0166cb7 100644 --- a/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java +++ b/playkit/src/main/java/com/kaltura/playkit/player/TrackSelectionHelper.java @@ -1359,9 +1359,7 @@ boolean isAdaptive(int rendererIndex, int groupIndex) { * @throws IllegalArgumentException when uniqueId is illegal. */ int[] validateUniqueId(String uniqueId) throws IllegalArgumentException { - - // FIXME: CHECK UniqueId with TextUtils - if (uniqueId == null) { + if (TextUtils.isEmpty(uniqueId)) { throw new IllegalArgumentException("uniqueId is null"); } From f2aba74b93b178fa2888819854c02fb077afdaf6 Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:13:50 +0300 Subject: [PATCH 28/33] Update FakeRendererCapabilities.kt --- .../java/com/kaltura/testhelper/FakeRendererCapabilities.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt b/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt index d4deb6c6a..bf14a4f44 100644 --- a/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt +++ b/playkit/src/main/java/com/kaltura/testhelper/FakeRendererCapabilities.kt @@ -44,4 +44,4 @@ class FakeRendererCapabilities(private var trackType: Int) : RendererCapabilitie override fun supportsMixedMimeTypeAdaptation(): Int { return ADAPTIVE_SEAMLESS } -} \ No newline at end of file +} From 712494c0e5af4ba91566a48993791266459cd18e Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:17:02 +0300 Subject: [PATCH 29/33] Update sample_hls_harold --- playkit/src/test/assets/testdata/m3u8/sample_hls_harold | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/test/assets/testdata/m3u8/sample_hls_harold b/playkit/src/test/assets/testdata/m3u8/sample_hls_harold index a157d85a9..5186db5ca 100644 --- a/playkit/src/test/assets/testdata/m3u8/sample_hls_harold +++ b/playkit/src/test/assets/testdata/m3u8/sample_hls_harold @@ -9,4 +9,4 @@ #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=437304,RESOLUTION=480x352,SUBTITLES="subs" http://cfvod.kaltura.com/scf/hls/p/243342/sp/24334200/serveFlavor/entryId/0_uka1msg4/v/1/pv/2/flavorId/1_vqhfu6uy/name/a.mp4/index.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2Nmdm9kLmthbHR1cmEuY29tL3NjZi9obHMvcC8yNDMzNDIvc3AvMjQzMzQyMDAvc2VydmVGbGF2b3IvZW50cnlJZC8wX3VrYTFtc2c0L3YvMS9wdi8yKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYyMjE4MzU4OX19fV19&Signature=glnACGQWH2q6QmRf5ZoK0RW455PDPjG2Nu3Y7Bsuas9Alw-I4DgEshcUCyLKjRpqAS4O0-AbVk4ILIc7wFgsQNU4T2NXGgQEG0UtcHyv2lCb6P5ekaDEXkrTLzUYcbXD7GP5oe41usBq11cXoRhEwRXGvOWcFL52CyILxhr~mTYhOMOvKqjH5ovgHClD1D-WJAkUlgoXHa5p~vhcf0XU0~JEAUuvuz9aEsOcjy3-fC7D5gq566V57f17CbPUND8q~a7lRPS3JN0zm5rsMAS1etluu8v33-0Qe0i5HmdslimCxuVN-ZKKdwODTh~I7UDc~NiZcQE6HCvvmRxj2h4rlQ__&Key-Pair-Id=APKAJT6QIWSKVYK3V34A #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=850577,RESOLUTION=480x352,SUBTITLES="subs" -http://cfvod.kaltura.com/scf/hls/p/243342/sp/24334200/serveFlavor/entryId/0_uka1msg4/v/1/pv/2/flavorId/1_80sohj7p/name/a.mp4/index.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2Nmdm9kLmthbHR1cmEuY29tL3NjZi9obHMvcC8yNDMzNDIvc3AvMjQzMzQyMDAvc2VydmVGbGF2b3IvZW50cnlJZC8wX3VrYTFtc2c0L3YvMS9wdi8yKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYyMjE4MzU4OX19fV19&Signature=glnACGQWH2q6QmRf5ZoK0RW455PDPjG2Nu3Y7Bsuas9Alw-I4DgEshcUCyLKjRpqAS4O0-AbVk4ILIc7wFgsQNU4T2NXGgQEG0UtcHyv2lCb6P5ekaDEXkrTLzUYcbXD7GP5oe41usBq11cXoRhEwRXGvOWcFL52CyILxhr~mTYhOMOvKqjH5ovgHClD1D-WJAkUlgoXHa5p~vhcf0XU0~JEAUuvuz9aEsOcjy3-fC7D5gq566V57f17CbPUND8q~a7lRPS3JN0zm5rsMAS1etluu8v33-0Qe0i5HmdslimCxuVN-ZKKdwODTh~I7UDc~NiZcQE6HCvvmRxj2h4rlQ__&Key-Pair-Id=APKAJT6QIWSKVYK3V34A \ No newline at end of file +http://cfvod.kaltura.com/scf/hls/p/243342/sp/24334200/serveFlavor/entryId/0_uka1msg4/v/1/pv/2/flavorId/1_80sohj7p/name/a.mp4/index.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2Nmdm9kLmthbHR1cmEuY29tL3NjZi9obHMvcC8yNDMzNDIvc3AvMjQzMzQyMDAvc2VydmVGbGF2b3IvZW50cnlJZC8wX3VrYTFtc2c0L3YvMS9wdi8yKiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTYyMjE4MzU4OX19fV19&Signature=glnACGQWH2q6QmRf5ZoK0RW455PDPjG2Nu3Y7Bsuas9Alw-I4DgEshcUCyLKjRpqAS4O0-AbVk4ILIc7wFgsQNU4T2NXGgQEG0UtcHyv2lCb6P5ekaDEXkrTLzUYcbXD7GP5oe41usBq11cXoRhEwRXGvOWcFL52CyILxhr~mTYhOMOvKqjH5ovgHClD1D-WJAkUlgoXHa5p~vhcf0XU0~JEAUuvuz9aEsOcjy3-fC7D5gq566V57f17CbPUND8q~a7lRPS3JN0zm5rsMAS1etluu8v33-0Qe0i5HmdslimCxuVN-ZKKdwODTh~I7UDc~NiZcQE6HCvvmRxj2h4rlQ__&Key-Pair-Id=APKAJT6QIWSKVYK3V34A From 98c901c5c2fd9f323a9ceaab0ab5a92bda39c3b5 Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:17:18 +0300 Subject: [PATCH 30/33] Update sample_dash_custom_manifest_image_tracks --- .../testdata/mpd/sample_dash_custom_manifest_image_tracks | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks index 31be16555..faf79bc00 100644 --- a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_image_tracks @@ -32,4 +32,4 @@ - \ No newline at end of file + From f24659aaa85ce732d31aaf002059bf6e292756d7 Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:17:46 +0300 Subject: [PATCH 31/33] Update sample_dash_custom_manifest_without_image_tracks --- .../mpd/sample_dash_custom_manifest_without_image_tracks | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks index 38eee9c95..aaf95a366 100644 --- a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks @@ -23,4 +23,4 @@ - \ No newline at end of file + From d99bd71fb545de7455d9e069d31d8ccce35b886c Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:18:15 +0300 Subject: [PATCH 32/33] Update sample_dash_custom_manifest_without_image_tracks --- .../mpd/sample_dash_custom_manifest_without_image_tracks | 1 + 1 file changed, 1 insertion(+) diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks index aaf95a366..78a6e6d94 100644 --- a/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_custom_manifest_without_image_tracks @@ -24,3 +24,4 @@ + From 1b5281758c832436c2b281bef4f95bb29f6164f1 Mon Sep 17 00:00:00 2001 From: Gilad Nadav Date: Sun, 27 Jun 2021 11:18:30 +0300 Subject: [PATCH 33/33] Update sample_dash_hevc_and_avc_mix --- .../src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix index 0b577bc19..06148b0e0 100644 --- a/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix +++ b/playkit/src/test/assets/testdata/mpd/sample_dash_hevc_and_avc_mix @@ -153,4 +153,4 @@ - \ No newline at end of file +