Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preferences to configure audio codecs and 4k transcoding/direct play #3110

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import android.view.KeyEvent
import androidx.preference.PreferenceManager
import org.jellyfin.androidtv.preference.constant.AppTheme
import org.jellyfin.androidtv.preference.constant.AudioTranscodeTarget
import org.jellyfin.androidtv.preference.constant.ClockBehavior
import org.jellyfin.androidtv.preference.constant.NextUpBehavior
import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer
Expand Down Expand Up @@ -100,6 +101,11 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
*/
var refreshRateSwitchingBehavior = enumPreference("refresh_rate_switching_behavior", RefreshRateSwitchingBehavior.DISABLED)

/**
* Enable 4k codecs for direct play and transcode target
*/
var enable4kSupport = booleanPreference("pref_enable_4k_support", DeviceUtils.has4kVideoSupport())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference keys should not start with pref_. This is only used on old keys for compatibility reasons (creating a migration just for a rename of the key is kinda pointless)


/* Playback - Audio related */
/**
* Preferred behavior for audio streaming.
Expand All @@ -111,6 +117,21 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
*/
var audioNightMode = enumPreference("audio_night_mode", false)

/**
* Enable mp2/mp3
*/
var mpegEnabled = booleanPreference("pref_bitstream_mpeg", true)

/**
* Enable PCM
*/
var pcmEnabled = booleanPreference("pref_bitstream_pcm", true)

/**
* Enable AAC
*/
var aacEnabled = booleanPreference("pref_bitstream_aac", true)

/**
* Enable DTS
*/
Expand All @@ -121,6 +142,26 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
*/
var ac3Enabled = booleanPreference("pref_bitstream_ac3", !DeviceUtils.isFireTvStickGen1)

/**
* Enable EAC3
*/
var eac3Enabled = booleanPreference("pref_bitstream_eac3", !DeviceUtils.isFireTvStickGen1)

/**
* Enable TrueUD
*/
var thdEnabled = booleanPreference("pref_bitstream_thd", false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should shorten this to thd, doesn't seem like an "official" abbreviation.


/**
* Enable Other
*/
var otherAudioEnabled = booleanPreference("pref_bitstream_other_audio", false)

/**
* Preferred audio transcode target codec
*/
var audioTranscodeTarget = enumPreference("audio_transcode_target", AudioTranscodeTarget.AUTO)

/**
* Default audio delay in milliseconds for libVLC
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.jellyfin.androidtv.preference.constant

import org.jellyfin.androidtv.R
import org.jellyfin.preference.PreferenceEnum

enum class AudioTranscodeTarget(
override val nameRes: Int,
) : PreferenceEnum {
/**
* Enable all codecs
*/
AUTO(R.string.lbl_audiotarget_auto),

PCM(R.string.lbl_codec_pcm),

AAC(R.string.lbl_codec_aac),

AC3(R.string.lbl_codec_ac3),

EAC3(R.string.lbl_codec_eac3),

DTS(R.string.lbl_codec_dts)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;

import org.jellyfin.androidtv.R;
import org.jellyfin.androidtv.constant.Codec;
import org.jellyfin.androidtv.constant.QueryType;
import org.jellyfin.androidtv.data.compat.AudioOptions;
import org.jellyfin.androidtv.data.compat.StreamInfo;
import org.jellyfin.androidtv.data.model.DataRefreshService;
import org.jellyfin.androidtv.preference.UserPreferences;
import org.jellyfin.androidtv.preference.UserSettingPreferences;
import org.jellyfin.androidtv.ui.itemhandling.AudioQueueItem;
import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem;
Expand Down Expand Up @@ -606,7 +608,10 @@ private void playInternal(final org.jellyfin.sdk.model.api.BaseItemDto item, fin
options.setMediaSources(item.getMediaSources());
DeviceProfile profile;
if (DeviceUtils.is60()) {
profile = new ExoPlayerProfile(context, false, false);
String[] audioDirectPlay = { Codec.Audio.AAC, Codec.Audio.MP3, Codec.Audio.MP2 };
String[] audioTranscode = { Codec.Audio.AAC, Codec.Audio.MP3, Codec.Audio.MP2 };
Boolean enable4kSupport = userPrefs.getValue().get(UserPreferences.Companion.getEnable4kSupport());
profile = new ExoPlayerProfile(context, false, audioDirectPlay, audioTranscode, enable4kSupport);
} else {
profile = new LibVlcProfile(context, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.acra.config.CoreConfiguration;
import org.jellyfin.androidtv.R;
import org.jellyfin.androidtv.auth.repository.UserRepository;
import org.jellyfin.androidtv.constant.Codec;
import org.jellyfin.androidtv.data.compat.PlaybackException;
import org.jellyfin.androidtv.data.compat.StreamInfo;
import org.jellyfin.androidtv.data.compat.SubtitleStreamInfo;
Expand All @@ -22,6 +24,7 @@
import org.jellyfin.androidtv.preference.SystemPreferences;
import org.jellyfin.androidtv.preference.UserPreferences;
import org.jellyfin.androidtv.preference.UserSettingPreferences;
import org.jellyfin.androidtv.preference.constant.AudioTranscodeTarget;
import org.jellyfin.androidtv.preference.constant.NextUpBehavior;
import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer;
import org.jellyfin.androidtv.preference.constant.RefreshRateSwitchingBehavior;
Expand Down Expand Up @@ -51,6 +54,8 @@
import org.jellyfin.sdk.model.api.PlayAccess;
import org.koin.java.KoinJavaComponent;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import kotlin.Lazy;
Expand Down Expand Up @@ -569,12 +574,84 @@ private VideoOptions buildExoPlayerOptions(@Nullable Integer forcedSubtitleIndex
DeviceProfile internalProfile = new ExoPlayerProfile(
mFragment.getContext(),
isLiveTv && !userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()),
userPreferences.getValue().get(UserPreferences.Companion.getAc3Enabled())
getDirectPlayPreferences(),
getAudioTranscodeTarget(),
userPreferences.getValue().get(UserPreferences.Companion.getEnable4kSupport())
);
internalOptions.setProfile(internalProfile);
return internalOptions;
}

@NonNull
private String[] getDirectPlayPreferences() {
ArrayList<String> codecs = new ArrayList<>();

if(userPreferences.getValue().get(UserPreferences.Companion.getMpegEnabled())) {
codecs.add(Codec.Audio.MP2);
codecs.add(Codec.Audio.MP3);
}

if(userPreferences.getValue().get(UserPreferences.Companion.getAacEnabled())) {
codecs.add(Codec.Audio.AAC);
codecs.add(Codec.Audio.AAC_LATM);
}

if(userPreferences.getValue().get(UserPreferences.Companion.getAc3Enabled())) {
codecs.add(Codec.Audio.AC3);
}

if(userPreferences.getValue().get(UserPreferences.Companion.getEac3Enabled())) {
codecs.add(Codec.Audio.EAC3);
}

if(userPreferences.getValue().get(UserPreferences.Companion.getDtsEnabled())) {
codecs.add(Codec.Audio.DTS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
codecs.add(Codec.Audio.DTS);
codecs.add(Codec.Audio.DCA);
codecs.add(Codec.Audio.DTS);

}

if(userPreferences.getValue().get(UserPreferences.Companion.getThdEnabled())) {
codecs.add(Codec.Audio.TRUEHD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
codecs.add(Codec.Audio.TRUEHD);
codecs.add(Codec.Audio.MLP);
codecs.add(Codec.Audio.TRUEHD);

}

if(userPreferences.getValue().get(UserPreferences.Companion.getPcmEnabled())) {
codecs.add(Codec.Audio.PCM_ALAW);
codecs.add(Codec.Audio.PCM_MULAW);
}

if(userPreferences.getValue().get(UserPreferences.Companion.getOtherAudioEnabled())) {
codecs.add(Codec.Audio.DCA);
codecs.add(Codec.Audio.MLP);
Comment on lines +621 to +622
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
codecs.add(Codec.Audio.DCA);
codecs.add(Codec.Audio.MLP);

codecs.add(Codec.Audio.OPUS);
codecs.add(Codec.Audio.FLAC);
Comment on lines +623 to +624
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you’re adding a new panel and there are only two other codecs, I would add them as separate options.

}

return codecs.toArray(new String[0]);
}

@NonNull
private String[] getAudioTranscodeTarget() {
AudioTranscodeTarget target = userPreferences.getValue().get(UserPreferences.Companion.getAudioTranscodeTarget());
switch (target) {
case AUTO:
String[] directPlayCodecs = getDirectPlayPreferences();
// If any codecs are enabled, return those
if(directPlayCodecs.length > 0) return directPlayCodecs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(directPlayCodecs.length > 0) return directPlayCodecs;
if (directPlayCodecs.length > 0) return directPlayCodecs;

// Otherwise fallback to MP3
return new String[]{ Codec.Audio.MP3 };
case AAC:
return new String[] { Codec.Audio.AAC, Codec.Audio.AAC_LATM };
case DTS:
return new String[]{ Codec.Audio.DTS };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new String[]{ Codec.Audio.DTS };
return new String[]{ Codec.Audio.DCA, Codec.Audio.DTS};

case PCM:
return new String[]{ Codec.Audio.PCM_ALAW, Codec.Audio.PCM_MULAW };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When trying to transcode to DTS or PCM (original files in an MKV container), the server sends an incompatible file container message and remuxes the file with the original audio and video. I would consider removing these as transcoding options.

case AC3:
return new String[]{ Codec.Audio.AC3 };
case EAC3:
return new String[]{ Codec.Audio.EAC3 };
default:
throw new IllegalArgumentException("Unknown audio transcode target preference");
}
}

@NonNull
private VideoOptions buildVLCOptions(@Nullable Integer forcedSubtitleIndex, org.jellyfin.sdk.model.api.BaseItemDto item, int maxBitrate) {
VideoOptions vlcOptions = new VideoOptions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.getQualityProfiles
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.preference.constant.AudioBehavior
import org.jellyfin.androidtv.preference.constant.AudioTranscodeTarget
import org.jellyfin.androidtv.preference.constant.NEXTUP_TIMER_DISABLED
import org.jellyfin.androidtv.preference.constant.NextUpBehavior
import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer
Expand Down Expand Up @@ -107,6 +108,12 @@ class PlaybackPreferencesScreen : OptionsFragment() {
bind(userPreferences, UserPreferences.refreshRateSwitchingBehavior)
depends { DeviceUtils.is60() && userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.pref_enable_4k_support)
setContent(R.string.pref_enable_4k_support_description)
bind(userPreferences, UserPreferences.enable4kSupport)
}
}

category {
Expand Down Expand Up @@ -171,27 +178,19 @@ class PlaybackPreferencesScreen : OptionsFragment() {
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

enum<AudioTranscodeTarget> {
setTitle(R.string.pref_audio_transcodetarget)
bind(userPreferences, UserPreferences.audioTranscodeTarget)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.pref_audio_night_mode)
setContent(R.string.desc_audio_night_mode)
bind(userPreferences, UserPreferences.audioNightMode)
depends { Build.VERSION.SDK_INT >= Build.VERSION_CODES.P }
}

checkbox {
setTitle(R.string.lbl_bitstream_ac3)
setContent(R.string.desc_bitstream_ac3)
bind(userPreferences, UserPreferences.ac3Enabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_bitstream_dts)
setContent(R.string.desc_bitstream_ac3)
bind(userPreferences, UserPreferences.dtsEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

@Suppress("MagicNumber")
seekbar {
setTitle(R.string.pref_libvlc_audio_delay_title)
Expand All @@ -204,6 +203,66 @@ class PlaybackPreferencesScreen : OptionsFragment() {
}
}

category {
setTitle(R.string.pref_audio_directplay)
Comment on lines +206 to +207
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better if add a new preference screen for this category. You can look at UserPreferencesScreen to see how to link to one.


checkbox {
setTitle(R.string.lbl_codec_mpeg)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.mpegEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_pcm)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.pcmEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_aac)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.aacEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_dts)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.dtsEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_ac3)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.ac3Enabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_eac3)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.eac3Enabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_thd)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.thdEnabled)
depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}

checkbox {
setTitle(R.string.lbl_codec_other)
setContent(R.string.desc_bitstream_generic)
bind(userPreferences, UserPreferences.otherAudioEnabled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As commented above, I think it makes sense to split out OPUS and FLAC as separate options.

depends { userPreferences[UserPreferences.videoPlayer] != PreferredVideoPlayer.EXTERNAL }
}
}

category {
setTitle(R.string.pref_music_cat)

Expand Down
Loading