diff --git a/app/src/main/java/com/github/libretube/helpers/DashHelper.kt b/app/src/main/java/com/github/libretube/helpers/DashHelper.kt index 2cbe21006..23e706025 100644 --- a/app/src/main/java/com/github/libretube/helpers/DashHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/DashHelper.kt @@ -24,8 +24,8 @@ object DashHelper { val formats: MutableList = mutableListOf(), ) - fun createManifest(streams: Streams, supportsHdr: Boolean): String { - val builder: DocumentBuilder = builderFactory.newDocumentBuilder() + fun createManifest(streams: Streams, supportsHdr: Boolean, audioOnly: Boolean = false): String { + val builder = builderFactory.newDocumentBuilder() val doc = builder.newDocument() val mpd = doc.createElement("MPD") @@ -39,41 +39,43 @@ object DashHelper { val adapSetInfos = ArrayList() - val enabledVideoCodecs = PlayerHelper.enabledVideoCodecs - for ( - stream in streams.videoStreams - // used to avoid including LBRY HLS inside the streams in the manifest - .filter { !it.format.orEmpty().contains("HLS") } - // filter the codecs according to the user's preferences - .filter { - enabledVideoCodecs == "all" || it.codec.orEmpty().lowercase().startsWith( - enabledVideoCodecs, + if (!audioOnly) { + val enabledVideoCodecs = PlayerHelper.enabledVideoCodecs + for ( + stream in streams.videoStreams + // used to avoid including LBRY HLS inside the streams in the manifest + .filter { !it.format.orEmpty().contains("HLS") } + // filter the codecs according to the user's preferences + .filter { + enabledVideoCodecs == "all" || it.codec.orEmpty().lowercase().startsWith( + enabledVideoCodecs, + ) + } + .filter { supportsHdr || !it.quality.orEmpty().uppercase().contains("HDR") } + ) { + // ignore dual format streams + if (!stream.videoOnly!!) { + continue + } + + // ignore streams which might be OTF + if (stream.indexEnd!! <= 0) { + continue + } + + val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } + if (adapSetInfo != null) { + adapSetInfo.formats.add(stream) + continue + } + adapSetInfos.add( + AdapSetInfo( + stream.mimeType!!, + null, + mutableListOf(stream), + ), ) } - .filter { supportsHdr || !it.quality.orEmpty().uppercase().contains("HDR") } - ) { - // ignore dual format streams - if (!stream.videoOnly!!) { - continue - } - - // ignore streams which might be OTF - if (stream.indexEnd!! <= 0) { - continue - } - - val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } - if (adapSetInfo != null) { - adapSetInfo.formats.add(stream) - continue - } - adapSetInfos.add( - AdapSetInfo( - stream.mimeType!!, - null, - mutableListOf(stream), - ), - ) } for (stream in streams.audioStreams) { diff --git a/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt index 757e8b94b..53145c086 100644 --- a/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt @@ -5,6 +5,8 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.ActivityInfo +import android.net.Uri +import android.util.Base64 import android.view.accessibility.CaptioningManager import android.widget.Toast import androidx.annotation.StringRes @@ -12,6 +14,7 @@ import androidx.core.app.PendingIntentCompat import androidx.core.app.RemoteActionCompat import androidx.core.content.getSystemService import androidx.core.graphics.drawable.IconCompat +import androidx.core.net.toUri import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.PlaybackParameters @@ -22,6 +25,7 @@ import androidx.media3.ui.CaptionStyleCompat import com.github.libretube.R import com.github.libretube.api.obj.PipedStream import com.github.libretube.api.obj.Segment +import com.github.libretube.api.obj.Streams import com.github.libretube.constants.PreferenceKeys import com.github.libretube.enums.AudioQuality import com.github.libretube.enums.PlayerEvent @@ -33,48 +37,23 @@ object PlayerHelper { const val CONTROL_TYPE = "control_type" /** - * Get the audio source following the users preferences + * Create a base64 encoded DASH stream manifest */ - fun getAudioSource(context: Context, audios: List): String { - val audioFormat = PreferenceHelper.getString(PreferenceKeys.PLAYER_AUDIO_FORMAT, "all") - val audioPrefKey = if (NetworkHelper.isNetworkMetered(context)) { - PreferenceKeys.PLAYER_AUDIO_QUALITY_MOBILE - } else { - PreferenceKeys.PLAYER_AUDIO_QUALITY - } - - val audioQuality = PreferenceHelper.getString(audioPrefKey, "best") - - val filteredAudios = audios.filter { - val audioMimeType = "audio/$audioFormat" - it.mimeType != audioMimeType || audioFormat == "all" - } - - return getBitRate( - filteredAudios, - if (audioQuality == "best") AudioQuality.BEST else AudioQuality.WORST, + fun createDashSource(streams: Streams, context: Context, audioOnly: Boolean = false): Uri { + val manifest = DashHelper.createManifest( + streams, + DisplayHelper.supportsHdr(context), + audioOnly ) + + // encode to base64 + val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) + return "data:application/dash+xml;charset=utf-8;base64,$encoded".toUri() } /** - * Get the best or worst bitrate from a list of audio streams - * @param audios list of the audio streams - * @param quality Whether to use the best or worst quality available - * @return Url of the audio source + * Get the system's default captions style */ - private fun getBitRate(audios: List, quality: AudioQuality): String { - val filteredAudios = audios.filter { - it.bitrate != null - }.sortedBy { - it.bitrate - } - return when (quality) { - AudioQuality.BEST -> filteredAudios.last() - AudioQuality.WORST -> filteredAudios.first() - }.url!! - } - - // get the system default caption style @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) fun getCaptionStyle(context: Context): CaptionStyleCompat { val captioningManager = context.getSystemService()!! diff --git a/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt b/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt index d474c6520..b9eae2e64 100644 --- a/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt +++ b/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt @@ -10,9 +10,11 @@ import android.util.Log import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat +import androidx.core.net.toUri import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope import androidx.media3.common.MediaItem +import androidx.media3.common.MimeTypes import androidx.media3.common.PlaybackException import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer @@ -299,14 +301,15 @@ class OnlinePlayerService : LifecycleService() { private fun setMediaItem() { val streams = streams ?: return - val uri = if (streams.audioStreams.isNotEmpty()) { - PlayerHelper.getAudioSource(this, streams.audioStreams) + val (uri, mimeType) = if (streams.audioStreams.isNotEmpty()) { + PlayerHelper.createDashSource(streams, this, true) to MimeTypes.APPLICATION_MPD } else { - streams.hls ?: return + ProxyHelper.rewriteUrl(streams.hls)?.toUri() to MimeTypes.APPLICATION_M3U8 } val mediaItem = MediaItem.Builder() - .setUri(ProxyHelper.rewriteUrl(uri)) + .setUri(uri) + .setMimeType(mimeType) .setMetadata(streams) .build() player?.setMediaItem(mediaItem) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index e8f5243b9..4dd9c2271 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1329,15 +1329,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions { val uri = streams.dash?.let { ProxyHelper.unwrapIfEnabled(it) }?.toUri().takeIf { streams.livestream || streams.videoStreams.isEmpty() } ?: let { - val manifest = DashHelper.createManifest( - streams, - DisplayHelper.supportsHdr(requireContext()), - ) - - // encode to base64 - val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) - - "data:application/dash+xml;charset=utf-8;base64,$encoded".toUri() + PlayerHelper.createDashSource(streams, requireContext()) } this.setMediaSource(uri, MimeTypes.APPLICATION_MPD) diff --git a/app/src/main/res/xml/audio_video_settings.xml b/app/src/main/res/xml/audio_video_settings.xml index 26321a671..79909192f 100644 --- a/app/src/main/res/xml/audio_video_settings.xml +++ b/app/src/main/res/xml/audio_video_settings.xml @@ -13,6 +13,7 @@ app:title="@string/defres" app:useSimpleSummaryProvider="true" /> + @@ -35,6 +37,7 @@ app:title="@string/defres" app:useSimpleSummaryProvider="true" /> +