mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-11-05 18:30:59 +01:00
Experimental option to transcode surround sound audio to AC3 (#947)
## Description Adds an experimental setting to prefer transcoding audio with more than 2 channels to AC3. Stereo audio does not transcode. This is basically a workaround for HDMI ARC (not eARC) or optical audio devices because instead of 2 uncompressed PCM channels, 6 AC3 channels (ie 5.1) is possible. An downside of enabling this is that embedded subtitles be burned in causing a video transcode as well. ### Related issues Fixes #255 Incorporates some code from #1410 ### Testing Just on the emulator so far to observe if the settings trigger the transcode or not ## Screenshots N/A ## AI or LLM usage None --------- Co-authored-by: darkflame91 <darkflame91@gmail.com>
This commit is contained in:
parent
f2287a85e8
commit
d481aea85a
11 changed files with 205 additions and 54 deletions
|
|
@ -53,7 +53,10 @@ sealed interface AppPreference<Pref, T> {
|
|||
value: T?,
|
||||
): String? = null
|
||||
|
||||
fun validate(value: T): PreferenceValidation = PreferenceValidation.Valid
|
||||
fun validate(
|
||||
prefs: Pref,
|
||||
value: T,
|
||||
): PreferenceValidation = PreferenceValidation.Valid
|
||||
|
||||
companion object {
|
||||
val SkipForward =
|
||||
|
|
@ -391,18 +394,25 @@ sealed interface AppPreference<Pref, T> {
|
|||
defaultValue = true,
|
||||
getter = { it.playbackPreferences.overrides.ac3Supported },
|
||||
setter = { prefs, value ->
|
||||
prefs.updatePlaybackOverrides { ac3Supported = value }
|
||||
if (!value) prefs.updateExperimentalPreferences { preferAc3Surround = false }
|
||||
prefs.updatePlaybackOverrides {
|
||||
ac3Supported = value
|
||||
}
|
||||
},
|
||||
summaryOn = R.string.enabled,
|
||||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val DownMixStereo =
|
||||
AppSwitchPreference<AppPreferences>(
|
||||
title = R.string.downmix_stereo,
|
||||
defaultValue = false,
|
||||
getter = { it.playbackPreferences.overrides.downmixStereo },
|
||||
setter = { prefs, value ->
|
||||
prefs.updatePlaybackOverrides { downmixStereo = value }
|
||||
if (value) prefs.updateExperimentalPreferences { preferAc3Surround = false }
|
||||
prefs.updatePlaybackOverrides {
|
||||
downmixStereo = value
|
||||
}
|
||||
},
|
||||
summaryOn = R.string.enabled,
|
||||
summaryOff = R.string.disabled,
|
||||
|
|
@ -1311,11 +1321,16 @@ data class AppSwitchPreference<Pref>(
|
|||
override val defaultValue: Boolean,
|
||||
override val getter: (prefs: Pref) -> Boolean,
|
||||
override val setter: (prefs: Pref, value: Boolean) -> Pref,
|
||||
val validator: (value: Boolean) -> PreferenceValidation = { PreferenceValidation.Valid },
|
||||
val validator: (prefs: Pref, value: Boolean) -> PreferenceValidation = { _, _ -> PreferenceValidation.Valid },
|
||||
@param:StringRes val summary: Int? = null,
|
||||
@param:StringRes val summaryOn: Int? = null,
|
||||
@param:StringRes val summaryOff: Int? = null,
|
||||
) : AppPreference<Pref, Boolean> {
|
||||
override fun validate(
|
||||
prefs: Pref,
|
||||
value: Boolean,
|
||||
): PreferenceValidation = validator.invoke(prefs, value)
|
||||
|
||||
override fun summary(
|
||||
context: Context,
|
||||
value: Boolean?,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.github.damontecres.wholphin.R
|
|||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceGroup
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceScreenOption
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferenceValidation
|
||||
|
||||
object ExperimentalPreference {
|
||||
val Enable =
|
||||
|
|
@ -35,6 +36,30 @@ object ExperimentalPreference {
|
|||
summaryOn = R.string.enabled,
|
||||
summaryOff = R.string.disabled,
|
||||
)
|
||||
|
||||
val PreferAc3ForSurround =
|
||||
AppSwitchPreference<AppPreferences>(
|
||||
title = R.string.prefer_ac3_for_surround,
|
||||
defaultValue = true,
|
||||
getter = { it.experimentalPreferences.preferAc3Surround },
|
||||
setter = { prefs, value ->
|
||||
prefs.updateExperimentalPreferences {
|
||||
preferAc3Surround = value
|
||||
}
|
||||
},
|
||||
summary = R.string.prefer_ac3_for_surround_summary,
|
||||
validator = { prefs, value ->
|
||||
prefs.playbackPreferences.overrides.let {
|
||||
if (value && !it.ac3Supported) {
|
||||
PreferenceValidation.Invalid("AC3 support is not enabled")
|
||||
} else if (value && it.downmixStereo) {
|
||||
PreferenceValidation.Invalid("Always downmixing to stereo")
|
||||
} else {
|
||||
PreferenceValidation.Valid
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val experimentalPreferences =
|
||||
|
|
@ -45,6 +70,7 @@ val experimentalPreferences =
|
|||
preferences =
|
||||
listOf(
|
||||
ExperimentalPreference.VideoTunneling,
|
||||
ExperimentalPreference.PreferAc3ForSurround,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.AssPlaybackMode
|
||||
import com.github.damontecres.wholphin.preferences.PlaybackPreferences
|
||||
import com.github.damontecres.wholphin.preferences.ExperimentalPreferences
|
||||
import com.github.damontecres.wholphin.preferences.PlaybackOverrides
|
||||
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
||||
import com.github.damontecres.wholphin.util.profile.MediaCodecCapabilitiesTest
|
||||
import com.github.damontecres.wholphin.util.profile.createDeviceProfile
|
||||
|
|
@ -34,20 +36,17 @@ class DeviceProfileService
|
|||
private var deviceProfile: DeviceProfile? = null
|
||||
|
||||
suspend fun getOrCreateDeviceProfile(
|
||||
prefs: PlaybackPreferences,
|
||||
appPrefs: AppPreferences,
|
||||
serverVersion: ServerVersion?,
|
||||
): DeviceProfile =
|
||||
withContext(WholphinDispatchers.Default) {
|
||||
val prefs = appPrefs.playbackPreferences
|
||||
mutex.withLock {
|
||||
val newConfig =
|
||||
DeviceProfileConfiguration(
|
||||
maxBitrate = prefs.maxBitrate.toInt(),
|
||||
isAC3Enabled = prefs.overrides.ac3Supported,
|
||||
downMixAudio = prefs.overrides.downmixStereo,
|
||||
assPlaybackMode = prefs.overrides.assPlaybackMode,
|
||||
pgsDirectPlay = prefs.overrides.directPlayPgs,
|
||||
dolbyVisionELDirectPlay = prefs.overrides.directPlayDolbyVisionEL,
|
||||
decodeAv1 = prefs.overrides.decodeAv1,
|
||||
overrides = prefs.overrides,
|
||||
experimental = appPrefs.experimentalPreferences,
|
||||
jellyfinTenEleven =
|
||||
serverVersion != null && serverVersion >= ServerVersion(10, 11, 0),
|
||||
)
|
||||
|
|
@ -57,12 +56,13 @@ class DeviceProfileService
|
|||
createDeviceProfile(
|
||||
mediaTest = mediaCodecCapabilitiesTest,
|
||||
maxBitrate = newConfig.maxBitrate,
|
||||
isAC3Enabled = newConfig.isAC3Enabled,
|
||||
downMixAudio = newConfig.downMixAudio,
|
||||
assDirectPlay = newConfig.assPlaybackMode != AssPlaybackMode.ASS_TRANSCODE,
|
||||
pgsDirectPlay = newConfig.pgsDirectPlay,
|
||||
dolbyVisionELDirectPlay = newConfig.dolbyVisionELDirectPlay,
|
||||
isAC3Enabled = newConfig.overrides.ac3Supported,
|
||||
downMixAudio = newConfig.overrides.downmixStereo,
|
||||
assDirectPlay = newConfig.overrides.assPlaybackMode != AssPlaybackMode.ASS_TRANSCODE,
|
||||
pgsDirectPlay = newConfig.overrides.directPlayPgs,
|
||||
dolbyVisionELDirectPlay = newConfig.overrides.directPlayDolbyVisionEL,
|
||||
decodeAv1 = prefs.overrides.decodeAv1,
|
||||
preferAc3ForSurround = appPrefs.experimentalPreferences.preferAc3Surround,
|
||||
jellyfinTenEleven = newConfig.jellyfinTenEleven,
|
||||
)
|
||||
}
|
||||
|
|
@ -76,11 +76,7 @@ class DeviceProfileService
|
|||
*/
|
||||
data class DeviceProfileConfiguration(
|
||||
val maxBitrate: Int,
|
||||
val isAC3Enabled: Boolean,
|
||||
val downMixAudio: Boolean,
|
||||
val assPlaybackMode: AssPlaybackMode,
|
||||
val pgsDirectPlay: Boolean,
|
||||
val dolbyVisionELDirectPlay: Boolean,
|
||||
val decodeAv1: Boolean,
|
||||
val overrides: PlaybackOverrides,
|
||||
val experimental: ExperimentalPreferences,
|
||||
val jellyfinTenEleven: Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ class MediaReportService
|
|||
.getItem(itemId = item.id)
|
||||
.content.mediaSources
|
||||
val sourcesJson = json.encodeToString(sources)
|
||||
val playbackPrefs = userPreferencesService.getCurrent().appPreferences.playbackPreferences
|
||||
val appPreferences = userPreferencesService.getCurrent().appPreferences
|
||||
val serverVersion = serverRepository.currentServer?.serverVersion
|
||||
val deviceProfile =
|
||||
deviceProfileService.getOrCreateDeviceProfile(playbackPrefs, serverVersion)
|
||||
deviceProfileService.getOrCreateDeviceProfile(appPreferences, serverVersion)
|
||||
val deviceProfileJson = json.encodeToString(deviceProfile)
|
||||
val body =
|
||||
"""
|
||||
|
|
@ -76,7 +76,14 @@ class MediaReportService
|
|||
model=${Build.MODEL}
|
||||
apiLevel=${Build.VERSION.SDK_INT}
|
||||
|
||||
playbackPrefs=${playbackPrefs.toString().replace("\n", ", ").replace("\t", " ")}
|
||||
playbackPrefs=${
|
||||
appPreferences.playbackPreferences.toString().replace("\n", ", ")
|
||||
.replace("\t", " ")
|
||||
}
|
||||
experimental=${
|
||||
appPreferences.experimentalPreferences.toString().replace("\n", ", ")
|
||||
.replace("\t", " ")
|
||||
}
|
||||
|
||||
deviceProfile=$deviceProfileJson
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.github.damontecres.wholphin.data.model.AudioItem
|
|||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
||||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.gt
|
||||
import com.github.damontecres.wholphin.ui.main.settings.MoveDirection
|
||||
import com.github.damontecres.wholphin.ui.onMain
|
||||
import com.github.damontecres.wholphin.ui.seekBack
|
||||
|
|
@ -25,6 +26,7 @@ import com.github.damontecres.wholphin.util.LoadingState
|
|||
import com.github.damontecres.wholphin.util.PlaybackItemState
|
||||
import com.github.damontecres.wholphin.util.TrackActivityPlaybackListener
|
||||
import com.github.damontecres.wholphin.util.WholphinDispatchers
|
||||
import com.github.damontecres.wholphin.util.profile.Codec
|
||||
import com.github.damontecres.wholphin.util.profile.supportedAudioCodecs
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -44,6 +46,7 @@ import org.jellyfin.sdk.api.client.extensions.universalAudioApi
|
|||
import org.jellyfin.sdk.api.sockets.subscribe
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||
import org.jellyfin.sdk.model.api.PlayMethod
|
||||
import org.jellyfin.sdk.model.api.PlaystateCommand
|
||||
import org.jellyfin.sdk.model.api.PlaystateMessage
|
||||
|
|
@ -72,6 +75,7 @@ class MusicService
|
|||
private val playerFactory: PlayerFactory,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val imageUrlService: ImageUrlService,
|
||||
private val userPreferencesService: UserPreferencesService,
|
||||
) {
|
||||
private val _state = MutableStateFlow(MusicServiceState.EMPTY)
|
||||
val state: StateFlow<MusicServiceState> = _state
|
||||
|
|
@ -92,6 +96,11 @@ class MusicService
|
|||
private var activityTracker: TrackActivityPlaybackListener? = null
|
||||
private var websocketJob: Job? = null
|
||||
|
||||
private suspend fun preferAc3Surround() =
|
||||
userPreferencesService
|
||||
.getCurrent()
|
||||
.appPreferences.experimentalPreferences.preferAc3Surround
|
||||
|
||||
/**
|
||||
* Start music playback
|
||||
*
|
||||
|
|
@ -195,10 +204,11 @@ class MusicService
|
|||
shuffled: Boolean,
|
||||
) {
|
||||
Timber.d("setQueue: %s items, shuffled=%s", items.size, shuffled)
|
||||
val preferAc3Surround = preferAc3Surround()
|
||||
val mediaItems =
|
||||
items
|
||||
.filter { it.type == BaseItemKind.AUDIO }
|
||||
.map(::convert)
|
||||
.map { convert(it, preferAc3Surround) }
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
player.setMediaItems(mediaItems)
|
||||
player.shuffleModeEnabled = shuffled
|
||||
|
|
@ -215,7 +225,8 @@ class MusicService
|
|||
index: Int? = null,
|
||||
) {
|
||||
if (item.type == BaseItemKind.AUDIO) {
|
||||
val mediaItem = convert(item)
|
||||
val preferAc3Surround = preferAc3Surround()
|
||||
val mediaItem = convert(item, preferAc3Surround)
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
if (index != null) {
|
||||
player.addMediaItem(index, mediaItem)
|
||||
|
|
@ -242,6 +253,7 @@ class MusicService
|
|||
list: BlockingList<BaseItem?>,
|
||||
startIndex: Int,
|
||||
) = loading {
|
||||
val preferAc3Surround = preferAc3Surround()
|
||||
var remaining = startIndex
|
||||
list.indices
|
||||
.chunked(25)
|
||||
|
|
@ -252,7 +264,7 @@ class MusicService
|
|||
list
|
||||
.getBlocking(it)
|
||||
?.takeIf { it.type == BaseItemKind.AUDIO }
|
||||
?.let(::convert)
|
||||
?.let { convert(it, preferAc3Surround) }
|
||||
} else {
|
||||
Timber.v("Skipping $remaining")
|
||||
remaining--
|
||||
|
|
@ -268,12 +280,36 @@ class MusicService
|
|||
/**
|
||||
* Converts a [BaseItem] into a [MediaItem] setting an [AudioItem] as its tag
|
||||
*/
|
||||
private fun convert(audio: BaseItem): MediaItem {
|
||||
private fun convert(
|
||||
audio: BaseItem,
|
||||
preferAc3Surround: Boolean,
|
||||
): MediaItem {
|
||||
val needsAc3Transcode =
|
||||
preferAc3Surround &&
|
||||
audio.data.mediaSources
|
||||
?.firstOrNull()
|
||||
?.mediaStreams
|
||||
?.any { stream ->
|
||||
stream.type == MediaStreamType.AUDIO &&
|
||||
stream.channels.gt(2)
|
||||
stream.codec != Codec.Audio.AC3
|
||||
} == true
|
||||
val url =
|
||||
api.universalAudioApi.getUniversalAudioStreamUrl(
|
||||
itemId = audio.id,
|
||||
container = audioFormats,
|
||||
)
|
||||
if (needsAc3Transcode) {
|
||||
api.universalAudioApi.getUniversalAudioStreamUrl(
|
||||
itemId = audio.id,
|
||||
container = listOf("mka"),
|
||||
transcodingContainer = "mka",
|
||||
maxAudioChannels = 6,
|
||||
transcodingAudioChannels = 6,
|
||||
audioCodec = Codec.Audio.AC3,
|
||||
)
|
||||
} else {
|
||||
api.universalAudioApi.getUniversalAudioStreamUrl(
|
||||
itemId = audio.id,
|
||||
container = audioFormats,
|
||||
)
|
||||
}
|
||||
Timber.i("url=%s", url)
|
||||
val imageUrl =
|
||||
audio.data.albumId?.let { albumId ->
|
||||
|
|
@ -354,7 +390,7 @@ class MusicService
|
|||
* Play this item next after the current, ie add the item as the next index in the queue
|
||||
*/
|
||||
suspend fun playNext(song: BaseItem) {
|
||||
val mediaItem = convert(song)
|
||||
val mediaItem = convert(song, preferAc3Surround())
|
||||
onMain {
|
||||
player.addMediaItem(state.value.currentIndex + 1, mediaItem)
|
||||
if (player.mediaItemCount == 1) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import com.github.damontecres.wholphin.services.ScreensaverService
|
|||
import com.github.damontecres.wholphin.services.StreamChoiceService
|
||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||
import com.github.damontecres.wholphin.ui.formatBitrate
|
||||
import com.github.damontecres.wholphin.ui.gt
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
|
|
@ -646,7 +647,7 @@ class PlaybackViewModel
|
|||
deviceProfile =
|
||||
if (currentPlayer.value!!.backend == PlayerBackend.EXO_PLAYER) {
|
||||
deviceProfileService.getOrCreateDeviceProfile(
|
||||
preferences.appPreferences.playbackPreferences,
|
||||
preferences.appPreferences,
|
||||
serverRepository.currentServer?.serverVersion,
|
||||
)
|
||||
} else {
|
||||
|
|
@ -855,9 +856,24 @@ class PlaybackViewModel
|
|||
// TODO there's probably no reason why we can't add external subtitles?
|
||||
Timber.v("changeStreams direct play")
|
||||
|
||||
// TODO Better way to handle unsupported types in general is needed
|
||||
// This is a workaround for switching to a non AC3 track when the user wants audio transcoded to AC3
|
||||
if (preferences.appPreferences.experimentalPreferences.preferAc3Surround && audioIndex != null) {
|
||||
currentPlayback.mediaSourceInfo.mediaStreams
|
||||
.orEmpty()
|
||||
.firstOrNull { it.index == audioIndex }
|
||||
?.let {
|
||||
if (it.channels.gt(2) && it.codec != Codec.Audio.AC3) {
|
||||
// User wants to transcode audio into AC3
|
||||
return@withContext false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val source = currentPlayback.mediaSourceInfo
|
||||
val externalSubtitle = source.findExternalSubtitle(subtitleIndex)
|
||||
|
||||
// TODO there's probably no reason why we can't add external subtitles?
|
||||
if (externalSubtitle == null) {
|
||||
val result =
|
||||
withContext(WholphinDispatchers.Main) {
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@ fun PreferencesContent(
|
|||
value = value,
|
||||
onNavigate = viewModel.navigationManager::navigateTo,
|
||||
onValueChange = { newValue ->
|
||||
val validation = pref.validate(newValue)
|
||||
val validation = pref.validate(preferences, newValue)
|
||||
when (validation) {
|
||||
is PreferenceValidation.Invalid -> {
|
||||
// TODO?
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ fun SubtitlePreferencesContent(
|
|||
value = value,
|
||||
onNavigate = viewModel.navigationManager::navigateTo,
|
||||
onValueChange = { newValue ->
|
||||
val validation = pref.validate(newValue)
|
||||
val validation = pref.validate(preferences, newValue)
|
||||
when (validation) {
|
||||
is PreferenceValidation.Invalid -> {
|
||||
// TODO?
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ fun createDeviceProfile(
|
|||
dolbyVisionELDirectPlay: Boolean,
|
||||
decodeAv1: Boolean,
|
||||
jellyfinTenEleven: Boolean,
|
||||
preferAc3ForSurround: Boolean,
|
||||
) = buildDeviceProfile {
|
||||
val allowedAudioCodecs =
|
||||
when {
|
||||
|
|
@ -132,31 +133,61 @@ fun createDeviceProfile(
|
|||
|
||||
// / Transcoding profiles
|
||||
// Video
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
if (preferAc3ForSurround) {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
|
||||
audioCodec(*allowedAudioCodecs)
|
||||
audioCodec(Codec.Audio.AC3)
|
||||
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
}
|
||||
} else {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.VIDEO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
if (supportsHevc) videoCodec(Codec.Video.HEVC)
|
||||
videoCodec(Codec.Video.H264)
|
||||
|
||||
audioCodec(*allowedAudioCodecs)
|
||||
|
||||
copyTimestamps = false
|
||||
enableSubtitlesInManifest = true
|
||||
}
|
||||
}
|
||||
|
||||
// Audio
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
if (preferAc3ForSurround) {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
audioCodec(Codec.Audio.AAC)
|
||||
audioCodec(Codec.Audio.AC3)
|
||||
}
|
||||
} else {
|
||||
transcodingProfile {
|
||||
type = DlnaProfileType.AUDIO
|
||||
context = EncodingContext.STREAMING
|
||||
|
||||
container = Codec.Container.TS
|
||||
protocol = MediaStreamProtocol.HLS
|
||||
|
||||
audioCodec(Codec.Audio.AAC)
|
||||
}
|
||||
}
|
||||
|
||||
// / Direct play profiles
|
||||
|
|
@ -530,6 +561,27 @@ fun createDeviceProfile(
|
|||
}
|
||||
}
|
||||
|
||||
if (preferAc3ForSurround) {
|
||||
supportedAudioCodecs
|
||||
.filterNot { it == Codec.Audio.AC3 }
|
||||
.forEach { audioCodec ->
|
||||
codecProfile {
|
||||
type = CodecType.VIDEO_AUDIO
|
||||
codec = audioCodec
|
||||
conditions {
|
||||
ProfileConditionValue.AUDIO_CHANNELS lowerThanOrEquals 2
|
||||
}
|
||||
}
|
||||
codecProfile {
|
||||
type = CodecType.AUDIO
|
||||
codec = audioCodec
|
||||
conditions {
|
||||
ProfileConditionValue.AUDIO_CHANNELS lowerThanOrEquals 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Audio channel profile
|
||||
codecProfile {
|
||||
type = CodecType.VIDEO_AUDIO
|
||||
|
|
|
|||
|
|
@ -215,6 +215,7 @@ message MusicPreferences {
|
|||
message ExperimentalPreferences {
|
||||
bool enabled = 1;
|
||||
bool video_tunneling_enabled = 2;
|
||||
bool prefer_ac3_surround = 3;
|
||||
}
|
||||
|
||||
message AppPreferences {
|
||||
|
|
|
|||
|
|
@ -551,6 +551,8 @@
|
|||
<string name="display_presets">Display presets</string>
|
||||
<string name="display_presets_description">Built-in presets to quickly style all rows</string>
|
||||
<string name="customize_home_summary">Choose rows and images on the home page</string>
|
||||
<string name="prefer_ac3_for_surround_summary"><![CDATA[Transcode non-stereo audio to AC3/Dolby Digital]]></string>
|
||||
<string name="prefer_ac3_for_surround">Use AC3 for surround sound audio</string>
|
||||
<string name="start_after">Start after</string>
|
||||
<string name="animate">Animate</string>
|
||||
<string name="max_age_rating">Max age rating</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue