diff --git a/app/src/main/java/org/jellyfin/androidtv/data/compat/VideoOptions.kt b/app/src/main/java/org/jellyfin/androidtv/data/compat/VideoOptions.kt
index 3e3d9caea2..d62aaab13d 100644
--- a/app/src/main/java/org/jellyfin/androidtv/data/compat/VideoOptions.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/data/compat/VideoOptions.kt
@@ -3,4 +3,5 @@ package org.jellyfin.androidtv.data.compat
class VideoOptions : AudioOptions() {
var audioStreamIndex: Int? = null
var subtitleStreamIndex: Int? = null
+ var alwaysBurnInSubtitleWhenTranscoding: Boolean = false
}
diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt
index 8a7a672416..adcee8cf8a 100644
--- a/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/preference/UserPreferences.kt
@@ -244,6 +244,11 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
*/
var assDirectPlay = booleanPreference("libass_enabled", false)
+ /**
+ * Always burn in subtitles when transcoding.
+ */
+ var subtitlesBurnDuringTranscode = booleanPreference("subtitles_burn_during_transcode", false)
+
/**
* Enable PGS subtitle direct-play.
*/
diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
index f6e75d626c..d05a0a1456 100644
--- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
+++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java
@@ -85,6 +85,23 @@ public class PlaybackController implements PlaybackControllerNotifiable {
protected VideoOptions mCurrentOptions;
private int mDefaultAudioIndex = -1;
protected boolean burningSubs = false;
+
+ // The server does not update the subtitle delivery method when alwaysBurnInSubtitleWhenTranscoding
+ // is set, so we need to assume subs are burned in when transcoding with the option enabled.
+ protected boolean shouldBurnInSubtitles(PlayMethod playMethod) {
+ return userPreferences.getValue().get(UserPreferences.Companion.getSubtitlesBurnDuringTranscode())
+ && playMethod == PlayMethod.TRANSCODE;
+ }
+
+ private void updateBurningSubs(StreamInfo response) {
+ if (response.getSubtitleDeliveryMethod() == SubtitleDeliveryMethod.DROP) {
+ burningSubs = false;
+ return;
+ }
+
+ burningSubs = response.getSubtitleDeliveryMethod() == SubtitleDeliveryMethod.ENCODE
+ || shouldBurnInSubtitles(response.getPlayMethod());
+ }
private float mRequestedPlaybackSpeed = -1.0f;
private Runnable mReportLoop;
@@ -502,6 +519,7 @@ private VideoOptions buildExoPlayerOptions(
VideoOptions internalOptions = new VideoOptions();
internalOptions.setItemId(item.getId());
internalOptions.setMediaSources(item.getMediaSources());
+ internalOptions.setAlwaysBurnInSubtitleWhenTranscoding(userPreferences.getValue().get(UserPreferences.Companion.getSubtitlesBurnDuringTranscode()));
if (playbackRetries > 0 || (isLiveTv && !directStreamLiveTv)) internalOptions.setEnableDirectPlay(false);
if (playbackRetries > 1) internalOptions.setEnableDirectStream(false);
if (mCurrentOptions != null) {
@@ -564,7 +582,7 @@ public void onResponse(StreamInfo internalResponse) {
if (mVideoManager == null)
return;
mCurrentOptions = internalOptions;
- if (internalOptions.getSubtitleStreamIndex() == null) burningSubs = internalResponse.getSubtitleDeliveryMethod() == SubtitleDeliveryMethod.ENCODE;
+ updateBurningSubs(internalResponse);
startItem(item, position, internalResponse);
}
@@ -995,6 +1013,9 @@ public void seek(long pos, boolean skipToNext) {
public void onResponse(StreamInfo response) {
if (!isActive()) return;
mCurrentStreamInfo = response;
+
+ updateBurningSubs(response);
+
if (mVideoManager != null) {
mVideoManager.setMediaStreamInfo(api.getValue(), response);
mVideoManager.start();
diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackControllerHelper.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackControllerHelper.kt
index f5b6e34e5a..2b8d2c5431 100644
--- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackControllerHelper.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackControllerHelper.kt
@@ -99,8 +99,8 @@ fun PlaybackController.setSubtitleIndex(index: Int, force: Boolean = false) {
return setSubtitleIndex(-1)
}
- when (stream.deliveryMethod) {
- SubtitleDeliveryMethod.ENCODE -> {
+ when {
+ stream.deliveryMethod == SubtitleDeliveryMethod.ENCODE || shouldBurnInSubtitles(currentStreamInfo.playMethod) -> {
Timber.i("Restarting playback for subtitle baking")
stop()
@@ -109,9 +109,9 @@ fun PlaybackController.setSubtitleIndex(index: Int, force: Boolean = false) {
play(mCurrentPosition, index)
}
- SubtitleDeliveryMethod.EXTERNAL,
- SubtitleDeliveryMethod.EMBED,
- SubtitleDeliveryMethod.HLS -> {
+ stream.deliveryMethod == SubtitleDeliveryMethod.EXTERNAL ||
+ stream.deliveryMethod == SubtitleDeliveryMethod.EMBED ||
+ stream.deliveryMethod == SubtitleDeliveryMethod.HLS -> {
// External subtitles need to be resolved differently
val group = if (stream.deliveryMethod == SubtitleDeliveryMethod.EXTERNAL) {
mVideoManager.mExoPlayer.currentTracks.groups.firstOrNull { group ->
@@ -155,7 +155,7 @@ fun PlaybackController.setSubtitleIndex(index: Int, force: Boolean = false) {
}
}
- SubtitleDeliveryMethod.DROP, null -> {
+ stream.deliveryMethod == SubtitleDeliveryMethod.DROP || stream.deliveryMethod == null -> {
Timber.i("Dropping subtitles")
setSubtitleIndex(-1)
}
diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackManager.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackManager.kt
index d89583b5b7..93770f6a9d 100644
--- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackManager.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackManager.kt
@@ -111,6 +111,7 @@ class PlaybackManager(
allowVideoStreamCopy = true,
allowAudioStreamCopy = true,
autoOpenLiveStream = true,
+ alwaysBurnInSubtitleWhenTranscoding = options.alwaysBurnInSubtitleWhenTranscoding,
)
).content
}
diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/settings/screen/playback/SettingsPlaybackAdvancedScreen.kt b/app/src/main/java/org/jellyfin/androidtv/ui/settings/screen/playback/SettingsPlaybackAdvancedScreen.kt
index 330170a685..83bb7fb7cd 100644
--- a/app/src/main/java/org/jellyfin/androidtv/ui/settings/screen/playback/SettingsPlaybackAdvancedScreen.kt
+++ b/app/src/main/java/org/jellyfin/androidtv/ui/settings/screen/playback/SettingsPlaybackAdvancedScreen.kt
@@ -195,6 +195,17 @@ fun SettingsPlaybackAdvancedScreen() {
)
}
+ item {
+ var subtitlesBurnDuringTranscode by rememberPreference(userPreferences, UserPreferences.subtitlesBurnDuringTranscode)
+
+ ListButton(
+ headingContent = { Text(stringResource(R.string.pref_burn_subtitles_when_transcoding)) },
+ captionContent = { Text(stringResource(R.string.pref_burn_subtitles_when_transcoding_description)) },
+ trailingContent = { Checkbox(checked = subtitlesBurnDuringTranscode) },
+ onClick = { subtitlesBurnDuringTranscode = !subtitlesBurnDuringTranscode }
+ )
+ }
+
item { ListSection(headingContent = { Text(stringResource(R.string.pref_live_tv_cat)) }) }
item {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c7e90cce2a..d3e5468323 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -542,6 +542,8 @@
Rewind
Enable Advanced SubStation Alpha subtitle client rendering
Direct play PGS subtitles
+ Burn in subtitles when transcoding
+ Always burn subtitles into the video during transcoding. This can fix subtitle sync issues but disables subtitle customization.
No permission to use the microphone. Please enable it in system settings.
Speech recognition is unavailable on your device.
An unexpected error occurred during speech recognition.