From fc3de93c3d6aa9d5c5fd6f3659ee27ae3df42cf0 Mon Sep 17 00:00:00 2001 From: Jakob Kukla Date: Mon, 15 Nov 2021 12:33:20 +0100 Subject: [PATCH 1/3] remove UnknownMediaSourceFactory and infer if transcoding --- .../service/playback/LocalPlayer.java | 33 ++++++-- .../playback/UnknownMediaSourceFactory.kt | 79 ------------------- 2 files changed, 28 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/com/dkanada/gramophone/service/playback/UnknownMediaSourceFactory.kt diff --git a/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java b/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java index e0752fc2..aa24ac2b 100644 --- a/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java +++ b/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java @@ -17,7 +17,7 @@ import com.google.android.exoplayer2.Player.EventListener; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.database.ExoDatabaseProvider; -import com.google.android.exoplayer2.source.MediaSourceFactory; +import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.FileDataSource; @@ -25,8 +25,12 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSink; import com.google.android.exoplayer2.upstream.cache.CacheDataSource; import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; +import com.google.android.exoplayer2.util.MimeTypes; import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; public class LocalPlayer implements Playback { public static final String TAG = LocalPlayer.class.getSimpleName(); @@ -89,14 +93,13 @@ public class LocalPlayer implements Playback { public LocalPlayer(Context context) { this.context = context; - MediaSourceFactory mediaSourceFactory = new UnknownMediaSourceFactory(buildDataSourceFactory()); AudioAttributes audioAttributes = new AudioAttributes.Builder() .setUsage(C.USAGE_MEDIA) .setContentType(C.CONTENT_TYPE_MUSIC) .build(); exoPlayer = new SimpleExoPlayer.Builder(context) - .setMediaSourceFactory(mediaSourceFactory) + .setMediaSourceFactory(new DefaultMediaSourceFactory(buildDataSourceFactory())) .setAudioAttributes(audioAttributes, true) .build(); @@ -141,8 +144,28 @@ public class LocalPlayer implements Playback { uri = Uri.parse(MusicUtil.getTranscodeUri(song)); } - MediaItem mediaItem = MediaItem.fromUri(uri); - mediaItem = mediaItem.buildUpon().setMediaId(song.id).build(); + List containers = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream() + .map(codec -> codec.container.toLowerCase(Locale.ROOT)) + .collect(Collectors.toList()); + List codecs = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream() + .map(codec -> codec.codec.toLowerCase(Locale.ROOT)) + .collect(Collectors.toList()); + String maxBitrate = PreferenceUtil.getInstance(context).getMaximumBitrate(); + + MediaItem mediaItem; + + if (uri.toString().contains("file://") || (containers.contains(song.container.toLowerCase(Locale.ROOT)) && codecs.contains(song.codec.toLowerCase(Locale.ROOT)) && song.bitRate <= Integer.parseInt(maxBitrate))) { + mediaItem = new MediaItem.Builder() + .setUri(uri) + .setMediaId(song.id) + .build(); + } else { + mediaItem = new MediaItem.Builder() + .setUri(uri) + .setMediaId(song.id) + .setMimeType(MimeTypes.APPLICATION_M3U8) + .build(); + } exoPlayer.addMediaItem(mediaItem); } diff --git a/app/src/main/java/com/dkanada/gramophone/service/playback/UnknownMediaSourceFactory.kt b/app/src/main/java/com/dkanada/gramophone/service/playback/UnknownMediaSourceFactory.kt deleted file mode 100644 index 81072c3f..00000000 --- a/app/src/main/java/com/dkanada/gramophone/service/playback/UnknownMediaSourceFactory.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.dkanada.gramophone.service.playback - -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.drm.DrmSessionManager -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory -import com.google.android.exoplayer2.source.MediaSource -import com.google.android.exoplayer2.source.MediaSourceFactory -import com.google.android.exoplayer2.source.ProgressiveMediaSource -import com.google.android.exoplayer2.source.hls.HlsMediaSource -import com.google.android.exoplayer2.upstream.* - -import kotlinx.coroutines.* - -import java.net.HttpURLConnection -import java.net.URL - -@Suppress("JoinDeclarationAndAssignment") -class UnknownMediaSourceFactory(dataSourceFactory: DataSource.Factory) : MediaSourceFactory { - private val hlsMediaSource : HlsMediaSource.Factory - private val progressiveMediaSource : ProgressiveMediaSource.Factory - - private var loadErrorHandlingPolicy: LoadErrorHandlingPolicy - - override fun setDrmSessionManager(drmSessionManager: DrmSessionManager?): MediaSourceFactory { - return this - } - - override fun setDrmHttpDataSourceFactory(drmHttpDataSourceFactory: HttpDataSource.Factory?): MediaSourceFactory { - return this - } - - override fun setDrmUserAgent(drmUserAgent: String?): MediaSourceFactory { - return this - } - - override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy?): MediaSourceFactory { - this.loadErrorHandlingPolicy = loadErrorHandlingPolicy!! - return this - } - - override fun getSupportedTypes(): IntArray { - return intArrayOf() - } - - override fun createMediaSource(mediaItem: MediaItem): MediaSource { - if (mediaItem.playbackProperties?.uri.toString().contains("file://")) { - return progressiveMediaSource.createMediaSource(mediaItem) - } - - val type: String? = runBlocking { - httpGet(mediaItem.playbackProperties!!.uri.toString()) - } - - val sourceFactory: MediaSourceFactory = if (type == "application/x-mpegURL") { - hlsMediaSource - } else { - progressiveMediaSource - } - - return sourceFactory.createMediaSource(mediaItem) - } - - @Suppress("BlockingMethodInNonBlockingContext") - private suspend fun httpGet(url: String?): String? { - return withContext(Dispatchers.IO) { - val request = URL(url) - val conn = request.openConnection() as HttpURLConnection - - return@withContext conn.getHeaderField("Content-Type") - } - } - - init { - hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory) - progressiveMediaSource = ProgressiveMediaSource.Factory(dataSourceFactory, DefaultExtractorsFactory()) - - loadErrorHandlingPolicy = DefaultLoadErrorHandlingPolicy() - } -} From 5a2fa0a49d55a150ca5eeaf0aec0fec87dff858c Mon Sep 17 00:00:00 2001 From: Jakob Kukla Date: Mon, 15 Nov 2021 12:45:02 +0100 Subject: [PATCH 2/3] add todo --- app/src/main/java/com/dkanada/gramophone/util/MusicUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/dkanada/gramophone/util/MusicUtil.java b/app/src/main/java/com/dkanada/gramophone/util/MusicUtil.java index a2522b9a..78e4d2ca 100644 --- a/app/src/main/java/com/dkanada/gramophone/util/MusicUtil.java +++ b/app/src/main/java/com/dkanada/gramophone/util/MusicUtil.java @@ -55,6 +55,7 @@ public class MusicUtil { builder.append("&TranscodingProtocol=hls"); // preferred codec when transcoding + // ToDo: Think about removing this. The server doesn't seem to respect it anyways builder.append("&AudioCodec=").append(preferenceUtil.getTranscodeCodec()); builder.append("&api_key=").append(apiClient.getAccessToken()); From 318f07ebd0317cedb06018c7ebc409bbdbced392 Mon Sep 17 00:00:00 2001 From: jakobkukla Date: Sun, 20 Mar 2022 17:38:39 +0100 Subject: [PATCH 3/3] Fix playback if transcoding is disabled on the server --- app/src/main/java/com/dkanada/gramophone/model/Song.java | 4 ++++ .../dkanada/gramophone/service/playback/LocalPlayer.java | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/dkanada/gramophone/model/Song.java b/app/src/main/java/com/dkanada/gramophone/model/Song.java index 3dbcb538..1e48147a 100644 --- a/app/src/main/java/com/dkanada/gramophone/model/Song.java +++ b/app/src/main/java/com/dkanada/gramophone/model/Song.java @@ -42,6 +42,8 @@ public class Song implements Parcelable { public String container; public String codec; + public boolean supportsTranscoding; + public int sampleRate; public int bitRate; public int bitDepth; @@ -89,6 +91,8 @@ public class Song implements Parcelable { this.container = source.getContainer(); this.bitRate = source.getBitrate() != null ? source.getBitrate() : 0; + this.supportsTranscoding = source.getSupportsTranscoding(); + if (source.getMediaStreams() != null && source.getMediaStreams().size() != 0) { MediaStream stream = source.getMediaStreams().get(0); diff --git a/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java b/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java index aa24ac2b..57595bba 100644 --- a/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java +++ b/app/src/main/java/com/dkanada/gramophone/service/playback/LocalPlayer.java @@ -147,14 +147,21 @@ public class LocalPlayer implements Playback { List containers = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream() .map(codec -> codec.container.toLowerCase(Locale.ROOT)) .collect(Collectors.toList()); + List codecs = PreferenceUtil.getInstance(context).getDirectPlayCodecs().stream() .map(codec -> codec.codec.toLowerCase(Locale.ROOT)) .collect(Collectors.toList()); + String maxBitrate = PreferenceUtil.getInstance(context).getMaximumBitrate(); MediaItem mediaItem; - if (uri.toString().contains("file://") || (containers.contains(song.container.toLowerCase(Locale.ROOT)) && codecs.contains(song.codec.toLowerCase(Locale.ROOT)) && song.bitRate <= Integer.parseInt(maxBitrate))) { + boolean shouldDirectPlay = + containers.contains(song.container.toLowerCase(Locale.ROOT)) && + codecs.contains(song.codec.toLowerCase(Locale.ROOT)) && + song.bitRate <= Integer.parseInt(maxBitrate); + + if (uri.toString().contains("file://") || shouldDirectPlay || !song.supportsTranscoding) { mediaItem = new MediaItem.Builder() .setUri(uri) .setMediaId(song.id)