Skip to content

Commit 545216c

Browse files
committed
feat: improve track selection and restore audio-only for combined UMP
1 parent 81d0b08 commit 545216c

5 files changed

Lines changed: 154 additions & 31 deletions

File tree

app/src/main/java/com/futo/platformplayer/Extensions_Content.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
99
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
1010
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource
1111
import com.futo.platformplayer.helpers.VideoHelper
12+
import com.futo.platformplayer.views.video.FutoVideoPlayerBase.Companion.DEFAULT_USER_AGENT
1213
import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
1314

1415
fun IPlatformVideoDetails.isDownloadable(): Boolean = VideoHelper.isDownloadable(this);
@@ -24,8 +25,9 @@ fun JSSource.getHttpDataSourceFactory(): HttpDataSource.Factory {
2425
} else if (requestModifier != null) {
2526
JSHttpDataSource.Factory().setRequestModifier(requestModifier);
2627
} else {
27-
DefaultHttpDataSource.Factory();
28+
DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT);
2829
}
2930
}
3031

32+
3133
fun IVideoSourceDescriptor.hasAnySource(): Boolean = this.videoSources.any() || (this is VideoUnMuxedSourceDescriptor && this.audioSources.any());

app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideo
4444
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
4545
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
4646
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawAudioSource
47+
import com.futo.platformplayer.api.media.platforms.js.models.sources.IJSDashManifestRawSource
4748
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawSource
49+
import com.futo.platformplayer.helpers.toAudioSource
4850
import com.futo.platformplayer.constructs.Event0
4951
import com.futo.platformplayer.constructs.Event1
5052
import com.futo.platformplayer.constructs.Event3
@@ -438,9 +440,8 @@ class ShortView : FrameLayout {
438440
} else {
439441
video = videoDetails
440442
videoSources = video?.video?.videoSources?.toList()
441-
audioSources =
442-
if (video?.video?.isUnMuxed == true) (video.video as VideoUnMuxedSourceDescriptor).audioSources.toList()
443-
else null
443+
audioSources = if (video?.video?.isUnMuxed == true) (video.video as VideoUnMuxedSourceDescriptor).audioSources.toList()
444+
else video?.video?.videoSources?.map { it.toAudioSource() }
444445
if (videoLocal != null) {
445446
localVideoSources = videoLocal.videoSource.toList()
446447
localAudioSource = videoLocal.audioSource.toList()
@@ -460,10 +461,15 @@ class ShortView : FrameLayout {
460461
?.filterNotNull()?.toList() ?: listOf() else videoSources?.toList() ?: listOf()
461462
val bestAudioContainer =
462463
audioSources?.let { VideoHelper.selectBestAudioSource(it, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)?.container }
463-
val bestAudioSources =
464-
if (doDedup) audioSources?.filter { it.container == bestAudioContainer }
465-
?.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource })
466-
?.distinct()?.toList() ?: listOf() else audioSources?.toList() ?: listOf()
464+
val bestAudioSources = if (doDedup && audioSources != null) {
465+
val audioLangs = audioSources.map { it.language }.distinct()
466+
audioLangs.map { lang ->
467+
VideoHelper.selectBestAudioSource(audioSources.filter { it.language == lang }, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)
468+
}.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource || it is IJSDashManifestRawSource })
469+
.filterNotNull()
470+
.distinct()
471+
.toList()
472+
} else audioSources?.toList() ?: listOf()
467473

468474
val canSetSpeed = true
469475
val currentPlaybackRate = player.getPlaybackRate()
@@ -572,7 +578,7 @@ class ShortView : FrameLayout {
572578
overlayQualitySelector?.selectOption("audio", _lastAudioSource)
573579
overlayQualitySelector?.selectOption("subtitles", _lastSubtitleSource)
574580

575-
if (_lastVideoSource is IDashManifestSource || _lastVideoSource is IHLSManifestSource) {
581+
if (_lastVideoSource is IDashManifestSource || _lastVideoSource is IHLSManifestSource || _lastVideoSource is IJSDashManifestRawSource) {
576582
val videoTracks =
577583
player.exoPlayer?.player?.currentTracks?.groups?.firstOrNull { it.mediaTrackGroup.type == C.TRACK_TYPE_VIDEO }
578584

@@ -601,6 +607,22 @@ class ShortView : FrameLayout {
601607
?.setSubText("${player.exoPlayer?.player?.videoFormat?.width}x${player.exoPlayer?.player?.videoFormat?.height}")
602608
overlayQualitySelector?.selectOption("video", "auto")
603609
}
610+
611+
// Audio track selection from manifest
612+
val audioTracks = player.exoPlayer?.player?.currentTracks?.groups?.firstOrNull { it.mediaTrackGroup.type == C.TRACK_TYPE_AUDIO }
613+
if (audioTracks != null) {
614+
var audioMenuGroup: SlideUpMenuGroup? = null
615+
for (view in overlayQualitySelector!!.groupItems) {
616+
if (view is SlideUpMenuGroup && view.groupTag == "audio") {
617+
audioMenuGroup = view
618+
}
619+
}
620+
621+
val currentAudioTrack = player.exoPlayer?.player?.audioFormat
622+
if (currentAudioTrack != null) {
623+
overlayQualitySelector?.selectOption("audio", currentAudioTrack)
624+
}
625+
}
604626
}
605627

606628
val currentPlaybackRate = player.getPlaybackRate()

app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ import com.futo.platformplayer.api.media.models.video.LocalVideoDetails
8484
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
8585
import com.futo.platformplayer.api.media.platforms.js.JSClient
8686
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
87+
import com.futo.platformplayer.api.media.platforms.js.models.sources.IJSDashManifestRawSource
8788
import com.futo.platformplayer.api.media.platforms.js.models.JSVideoDetails
89+
8890
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource
8991
import com.futo.platformplayer.api.media.structures.IPager
9092
import com.futo.platformplayer.casting.CastConnectionState
@@ -106,6 +108,7 @@ import com.futo.platformplayer.fixHtmlLinks
106108
import com.futo.platformplayer.fixHtmlWhitespace
107109
import com.futo.platformplayer.getNowDiffSeconds
108110
import com.futo.platformplayer.helpers.VideoHelper
111+
import com.futo.platformplayer.helpers.toAudioSource
109112
import com.futo.platformplayer.logging.Logger
110113
import com.futo.platformplayer.models.Subscription
111114
import com.futo.platformplayer.receivers.MediaControlReceiver
@@ -2046,6 +2049,10 @@ class VideoDetailView : ConstraintLayout {
20462049
val videoSource = _lastVideoSource ?: _player.getPreferredVideoSource(video, Settings.instance.playback.getCurrentPreferredQualityPixelCount());
20472050
val audioSource = _lastAudioSource ?: _player.getPreferredAudioSource(video, Settings.instance.playback.getPrimaryLanguage(context));
20482051
val subtitleSource = _lastSubtitleSource ?: (if (Settings.instance.playback.stickySubtitles) _player.getPreferredSubtitleSource(video, _subtitleLanguage) else null) ?: (if(video is VideoLocal) video.subtitlesSources.firstOrNull() else null);
2052+
2053+
_player.setPreferredAudioLanguage(Settings.instance.playback.getPrimaryLanguage(context));
2054+
_player.setPreferredSubtitleLanguage(_subtitleLanguage);
2055+
20492056
Logger.i(TAG, "loadCurrentVideo(videoSource=$videoSource, audioSource=$audioSource, subtitleSource=$subtitleSource, resumePositionMs=$resumePositionMs)")
20502057

20512058
if(videoSource == null && audioSource == null) {
@@ -2310,7 +2317,7 @@ class VideoDetailView : ConstraintLayout {
23102317
_overlay_quality_selector?.selectOption("audio", _lastAudioSource);
23112318
_overlay_quality_selector?.selectOption("subtitles", _lastSubtitleSource);
23122319

2313-
if (_lastVideoSource is IDashManifestSource || _lastVideoSource is IHLSManifestSource) {
2320+
if (_lastVideoSource is IDashManifestSource || _lastVideoSource is IHLSManifestSource || _lastVideoSource is IJSDashManifestRawSource) {
23142321

23152322
val videoTracks =
23162323
_player.exoPlayer?.player?.currentTracks?.groups?.firstOrNull { it.mediaTrackGroup.type == C.TRACK_TYPE_VIDEO }
@@ -2340,6 +2347,22 @@ class VideoDetailView : ConstraintLayout {
23402347
?.setSubText("${_player.exoPlayer?.player?.videoFormat?.width}x${_player.exoPlayer?.player?.videoFormat?.height}")
23412348
_overlay_quality_selector?.selectOption("video", "auto")
23422349
}
2350+
2351+
// Audio track selection from manifest
2352+
val audioTracks = _player.exoPlayer?.player?.currentTracks?.groups?.firstOrNull { it.mediaTrackGroup.type == C.TRACK_TYPE_AUDIO }
2353+
if (audioTracks != null) {
2354+
var audioMenuGroup: SlideUpMenuGroup? = null
2355+
for (view in _overlay_quality_selector!!.groupItems) {
2356+
if (view is SlideUpMenuGroup && view.groupTag == "audio") {
2357+
audioMenuGroup = view
2358+
}
2359+
}
2360+
2361+
val currentAudioTrack = _player.exoPlayer?.player?.audioFormat
2362+
if (currentAudioTrack != null) {
2363+
_overlay_quality_selector?.selectOption("audio", currentAudioTrack)
2364+
}
2365+
}
23432366
}
23442367

23452368
val currentPlaybackRate = (if (_isCasting) StateCasting.instance.activeDevice?.speed else _player.getPlaybackRate()) ?: 1.0
@@ -2436,7 +2459,7 @@ class VideoDetailView : ConstraintLayout {
24362459
videoSources = video?.video?.videoSources?.toList();
24372460
audioSources = if(video?.video?.isUnMuxed == true)
24382461
(video.video as VideoUnMuxedSourceDescriptor).audioSources.toList()
2439-
else null
2462+
else video?.video?.videoSources?.map { it.toAudioSource() }
24402463
if(videoLocal != null) {
24412464
localVideoSources = videoLocal.videoSource.toList();
24422465
localAudioSource = videoLocal.audioSource.toList();
@@ -2504,11 +2527,15 @@ class VideoDetailView : ConstraintLayout {
25042527
?.filterNotNull()
25052528
?.toList() ?: listOf() else videoSources?.toList() ?: listOf()
25062529
val bestAudioContainer = audioSources?.let { VideoHelper.selectBestAudioSource(it, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)?.container };
2507-
val bestAudioSources = if(doDedup) audioSources
2508-
?.filter { it.container == bestAudioContainer }
2509-
?.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource })
2510-
?.distinct()
2511-
?.toList() ?: listOf() else audioSources?.toList() ?: listOf();
2530+
val bestAudioSources = if(doDedup && audioSources != null) {
2531+
val audioLangs = audioSources.map { it.language }.distinct();
2532+
audioLangs.map { lang ->
2533+
VideoHelper.selectBestAudioSource(audioSources.filter { it.language == lang }, FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS)
2534+
}.plus(audioSources.filter { it is IHLSManifestAudioSource || it is IDashManifestSource || it is IJSDashManifestRawSource })
2535+
.filterNotNull()
2536+
.distinct()
2537+
.toList()
2538+
} else audioSources?.toList() ?: listOf();
25122539

25132540
val canSetSpeed = !_isCasting || StateCasting.instance.activeDevice?.canSetSpeed() == true
25142541
val currentPlaybackRate = if (_isCasting) StateCasting.instance.activeDevice?.speed else _player.getPlaybackRate()

app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.media3.exoplayer.source.MediaSource
1212
import com.futo.platformplayer.Settings
1313
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
1414
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
15+
import com.futo.platformplayer.api.media.models.streams.sources.AudioUrlSource
1516
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
1617
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource
1718
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource
@@ -23,9 +24,11 @@ import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
2324
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
2425
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
2526
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource
27+
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlSource
2628
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawAudioSource
2729
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawSource
2830
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
31+
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlSource
2932
import com.futo.platformplayer.logging.Logger
3033
import com.futo.platformplayer.others.Language
3134
import getHttpDataSourceFactory
@@ -105,8 +108,10 @@ class VideoHelper {
105108

106109

107110
fun selectBestAudioSource(desc: IVideoSourceDescriptor, prefContainers : Array<String>, prefLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? {
108-
if(!desc.isUnMuxed)
109-
return null;
111+
if(!desc.isUnMuxed) {
112+
val audioEquivalent = desc.videoSources.map { it.toAudioSource() };
113+
return selectBestAudioSource(audioEquivalent, prefContainers, prefLanguage, targetBitrate);
114+
}
110115

111116
return selectBestAudioSource((desc as VideoUnMuxedSourceDescriptor).audioSources.toList(), prefContainers, prefLanguage, targetBitrate);
112117
}
@@ -329,3 +334,46 @@ class VideoHelper {
329334
}
330335
}
331336
}
337+
338+
fun IVideoSource.toAudioSource(): IAudioSource {
339+
if (this is IAudioSource) return this
340+
return when (this) {
341+
is JSVideoUrlRangeSource -> {
342+
val plugin = this.getUnderlyingPlugin() ?: throw IllegalStateException("Plugin is null")
343+
val obj = this.getUnderlyingObject() ?: throw IllegalStateException("Object is null")
344+
JSAudioUrlRangeSource(plugin, obj)
345+
}
346+
is JSVideoUrlSource -> {
347+
val plugin = this.getUnderlyingPlugin() ?: throw IllegalStateException("Plugin is null")
348+
val obj = this.getUnderlyingObject() ?: throw IllegalStateException("Object is null")
349+
JSAudioUrlSource(plugin, obj)
350+
}
351+
is IVideoUrlSource -> AudioUrlSource(
352+
name = this.name,
353+
url = this.getVideoUrl(),
354+
bitrate = this.bitrate ?: 0,
355+
container = this.container,
356+
codec = this.codec,
357+
language = this.language ?: Language.UNKNOWN,
358+
duration = this.duration,
359+
priority = this.priority,
360+
original = this.original ?: false
361+
)
362+
is JSDashManifestRawSource -> {
363+
val plugin = this.getUnderlyingPlugin() ?: throw IllegalStateException("Plugin is null")
364+
val obj = this.getUnderlyingObject() ?: throw IllegalStateException("Object is null")
365+
JSDashManifestRawAudioSource(plugin, obj)
366+
}
367+
else -> AudioUrlSource(
368+
name = this.name,
369+
url = "",
370+
bitrate = this.bitrate ?: 0,
371+
container = this.container,
372+
codec = this.codec,
373+
language = this.language ?: Language.UNKNOWN,
374+
duration = this.duration,
375+
priority = this.priority,
376+
original = this.original ?: false
377+
)
378+
}
379+
}

0 commit comments

Comments
 (0)