From b50e63077b0b97c4f468214794e285fb7d7603b9 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 11 Sep 2024 20:28:08 +0200 Subject: [PATCH] introduce datasource --- gradle/libs.versions.toml | 2 + new-player/build.gradle.kts | 1 + new-player/src/main/AndroidManifest.xml | 1 + .../net/newpipe/newplayer/MediaRepository.kt | 24 ++++++- .../java/net/newpipe/newplayer/NewPlayer.kt | 4 +- .../net/newpipe/newplayer/NewPlayerImpl.kt | 72 ++++++++++++------- .../newplayer/testapp/TestMediaRepository.kt | 46 ++++++++++-- 7 files changed, 114 insertions(+), 36 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6075a8a..b7e6dc3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ okhttpAndroid = "5.0.0-alpha.14" coil = "2.7.0" reorderable = "2.4.0-alpha02" media3Session = "1.4.1" +media3ExoplayerDash = "1.4.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -81,6 +82,7 @@ okhttp-android = { group = "com.squareup.okhttp3", name = "okhttp-android", vers coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } reorderable = { group = "sh.calvin.reorderable", name = "reorderable", version.ref = "reorderable" } androidx-media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3Session" } +androidx-media3-exoplayer-dash = { group = "androidx.media3", name = "media3-exoplayer-dash", version.ref = "media3ExoplayerDash" } diff --git a/new-player/build.gradle.kts b/new-player/build.gradle.kts index 20aa232..e73a7a5 100644 --- a/new-player/build.gradle.kts +++ b/new-player/build.gradle.kts @@ -69,6 +69,7 @@ dependencies { implementation(libs.coil.compose) implementation(libs.reorderable) implementation(libs.androidx.media3.session) + implementation(libs.androidx.media3.exoplayer.dash) ksp(libs.hilt.android.compiler) ksp(libs.androidx.hilt.compiler) diff --git a/new-player/src/main/AndroidManifest.xml b/new-player/src/main/AndroidManifest.xml index fec1d41..f04e26a 100644 --- a/new-player/src/main/AndroidManifest.xml +++ b/new-player/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + - suspend fun getStream(item: String, streamSelector: String): Uri + suspend fun getAvailableStreamVariants(item: String): List + suspend fun getStream(item: String, streamVariantSelector: StreamVariant): Uri suspend fun getAvailableSubtitleVariants(item: String): List suspend fun getSubtitle(item: String, variant: String): Uri diff --git a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt index 01893aa..ea7e76f 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt @@ -25,6 +25,7 @@ import androidx.media3.common.Player import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import java.util.stream.Stream import kotlin.Exception enum class PlayMode { @@ -45,6 +46,7 @@ enum class RepeatMode { interface NewPlayer { // preferences val preferredStreamVariants: List + val preferredStreamLanguage: List val exoPlayer: StateFlow var playWhenReady: Boolean @@ -76,7 +78,7 @@ interface NewPlayer { fun removePlaylistItem(uniqueId: Long) fun playStream(item: String, playMode: PlayMode) fun selectChapter(index: Int) - fun playStream(item: String, streamVariant: String, playMode: PlayMode) + fun playStream(item: String, streamVariant: StreamVariant, playMode: PlayMode) fun release() fun getItemLinkOfMediaItem(mediaItem: MediaItem) : String } diff --git a/new-player/src/main/java/net/newpipe/newplayer/NewPlayerImpl.kt b/new-player/src/main/java/net/newpipe/newplayer/NewPlayerImpl.kt index 21f5f7e..99bf7c5 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/NewPlayerImpl.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayerImpl.kt @@ -23,11 +23,18 @@ package net.newpipe.newplayer import android.app.Application import android.content.ComponentName import android.util.Log +import androidx.annotation.OptIn +import androidx.media3.common.AudioAttributes +import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.common.Timeline +import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import com.google.common.util.concurrent.MoreExecutors @@ -50,7 +57,8 @@ private const val TAG = "NewPlayerImpl" class NewPlayerImpl( val app: Application, private val repository: MediaRepository, - override val preferredStreamVariants: List = emptyList() + override val preferredStreamVariants: List = emptyList(), + override val preferredStreamLanguage: List = emptyList() ) : NewPlayer { private val mutableExoPlayer = MutableStateFlow(null) @@ -134,7 +142,11 @@ class NewPlayerImpl( } private fun setupNewExoplayer() { - val newExoPlayer = ExoPlayer.Builder(app).build() + val newExoPlayer = ExoPlayer.Builder(app) + .setAudioAttributes(AudioAttributes.DEFAULT, true) + .setHandleAudioBecomingNoisy(true) + .setWakeMode(if (repository.getRepoInfo().pullsDataFromNetwrok) C.WAKE_MODE_NETWORK else C.WAKE_MODE_LOCAL) + .build() newExoPlayer.addListener(object : Player.Listener { override fun onPlayerError(error: PlaybackException) { launchJobAndCollectError { @@ -190,7 +202,7 @@ class NewPlayerImpl( } override fun prepare() { - if(exoPlayer.value == null) { + if (exoPlayer.value == null) { setupNewExoplayer() } exoPlayer.value?.prepare() @@ -219,13 +231,14 @@ class NewPlayerImpl( exoPlayer.value?.pause() } + @OptIn(UnstableApi::class) override fun addToPlaylist(item: String) { if (exoPlayer.value == null) { prepare() } launchJobAndCollectError { - val mediaItem = toMediaItem(item) - exoPlayer.value?.addMediaItem(mediaItem) + val mediaSource = toMediaSource(item, playBackMode.value) + exoPlayer.value?.addMediaSource(mediaSource) } } @@ -234,33 +247,34 @@ class NewPlayerImpl( } override fun removePlaylistItem(uniqueId: Long) { - for (i in 0..<(exoPlayer.value?.mediaItemCount ?: 0)) { - val id = exoPlayer.value?.getMediaItemAt(i)?.mediaId?.toLong() ?: 0 - if (id == uniqueId) { - exoPlayer.value?.removeMediaItem(i) - break - } + for (i in 0..<(exoPlayer.value?.mediaItemCount ?: 0)) { + val id = exoPlayer.value?.getMediaItemAt(i)?.mediaId?.toLong() ?: 0 + if (id == uniqueId) { + exoPlayer.value?.removeMediaItem(i) + break } + } } override fun playStream(item: String, playMode: PlayMode) { launchJobAndCollectError { - val mediaItem = toMediaItem(item) + val mediaItem = toMediaSource(item, playMode) internalPlayStream(mediaItem, playMode) } } override fun playStream( item: String, - streamVariant: String, + streamVariant: StreamVariant, playMode: PlayMode ) { launchJobAndCollectError { - val stream = toMediaItem(item, streamVariant) + val stream = toMediaSource(item, streamVariant) internalPlayStream(stream, playMode) } } + @OptIn(UnstableApi::class) override fun selectChapter(index: Int) { val chapters = currentChapters.value assert(index in 0.. Unit) = diff --git a/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt b/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt index e1b15f9..3090053 100644 --- a/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt +++ b/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt @@ -8,6 +8,9 @@ import androidx.media3.common.PlaybackException import androidx.media3.common.util.UnstableApi import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.MediaRepository +import net.newpipe.newplayer.RepoMetaInfo +import net.newpipe.newplayer.StreamType +import net.newpipe.newplayer.StreamVariant import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -22,6 +25,9 @@ class TestMediaRepository(val context: Context) : MediaRepository { return client.newCall(request).execute() } + override fun getRepoInfo() = + RepoMetaInfo(canHandleTimestampedLinks = true, pullsDataFromNetwrok = true) + @OptIn(UnstableApi::class) override suspend fun getMetaInfo(item: String): MediaMetadata = when (item) { @@ -55,11 +61,37 @@ class TestMediaRepository(val context: Context) : MediaRepository { else -> throw Exception("Unknown stream: $item") } - override suspend fun getAvailableStreamVariants(item: String): List = + override suspend fun getAvailableStreamVariants(item: String): List = when (item) { - "6502" -> listOf("576p") - "portrait" -> listOf("720p") - "imu" -> listOf("1080p", "576p") + "6502" -> listOf( + StreamVariant( + streamType = StreamType.AUDIO_AND_VIDEO, + language = "Deutsch", + streamVariantIdentifier = "576p", + ), + ) + + "portrait" -> listOf( + StreamVariant( + streamType = StreamType.AUDIO_AND_VIDEO, + language = null, + streamVariantIdentifier = "720p", + ), + ) + + "imu" -> listOf( + StreamVariant( + streamType = StreamType.AUDIO_AND_VIDEO, + language = "Deutsch", + streamVariantIdentifier = "1080p", + ), + StreamVariant( + streamType = StreamType.AUDIO_AND_VIDEO, + language = "Deutsch", + streamVariantIdentifier = "576p", + ) + ) + else -> throw Exception("Unknown stream: $item") } @@ -68,15 +100,15 @@ class TestMediaRepository(val context: Context) : MediaRepository { } - override suspend fun getStream(item: String, streamSelector: String) = + override suspend fun getStream(item: String, streamVariantSelector: StreamVariant) = Uri.parse( when (item) { "6502" -> context.getString(R.string.ccc_6502_video) "portrait" -> context.getString(R.string.portrait_video_example) - "imu" -> when (streamSelector) { + "imu" -> when (streamVariantSelector.streamVariantIdentifier) { "1080p" -> context.getString(R.string.ccc_imu_1080_mp4) "576p" -> context.getString(R.string.ccc_imu_576_mp4) - else -> throw Exception("Unknown stream selector for $item: $streamSelector") + else -> throw Exception("Unknown stream selector for $item: $streamVariantSelector") } else -> throw Exception("Unknown stream: $item")