From 62712aa12cdc88c524923cb318783b23a8a7a2c5 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 12 Sep 2024 23:53:21 +0800 Subject: [PATCH] Add option to always burn in subtitles if transcoding is triggered (#12430) --- .../Controllers/MediaInfoController.cs | 11 +++++++--- .../Controllers/UniversalAudioController.cs | 1 + Jellyfin.Api/Helpers/MediaInfoHelper.cs | 6 +++++- .../Models/MediaInfoDtos/OpenLiveStreamDto.cs | 5 +++++ .../Models/MediaInfoDtos/PlaybackInfoDto.cs | 5 +++++ MediaBrowser.Model/Dlna/MediaOptions.cs | 5 +++++ MediaBrowser.Model/Dlna/StreamBuilder.cs | 20 +++++++++++++++---- .../MediaInfo/LiveStreamRequest.cs | 3 +++ MediaBrowser.Model/Session/TranscodeReason.cs | 1 + 9 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index bc52be1842..f22ac0b73a 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -209,6 +209,7 @@ public class MediaInfoController : BaseJellyfinApiController enableTranscoding.Value, allowVideoStreamCopy.Value, allowAudioStreamCopy.Value, + playbackInfoDto?.AlwaysBurnInSubtitleWhenTranscoding ?? false, Request.HttpContext.GetNormalizedRemoteIP()); } @@ -236,7 +237,8 @@ public class MediaInfoController : BaseJellyfinApiController StartTimeTicks = startTimeTicks, SubtitleStreamIndex = subtitleStreamIndex, UserId = userId ?? Guid.Empty, - OpenToken = mediaSource.OpenToken + OpenToken = mediaSource.OpenToken, + AlwaysBurnInSubtitleWhenTranscoding = playbackInfoDto?.AlwaysBurnInSubtitleWhenTranscoding ?? false }).ConfigureAwait(false); info.MediaSources = new[] { openStreamResult.MediaSource }; @@ -261,6 +263,7 @@ public class MediaInfoController : BaseJellyfinApiController /// The open live stream dto. /// Whether to enable direct play. Default: true. /// Whether to enable direct stream. Default: true. + /// Always burn-in subtitle when transcoding. /// Media source opened. /// A containing a . [HttpPost("LiveStreams/Open")] @@ -277,7 +280,8 @@ public class MediaInfoController : BaseJellyfinApiController [FromQuery] Guid? itemId, [FromBody] OpenLiveStreamDto? openLiveStreamDto, [FromQuery] bool? enableDirectPlay, - [FromQuery] bool? enableDirectStream) + [FromQuery] bool? enableDirectStream, + [FromQuery] bool? alwaysBurnInSubtitleWhenTranscoding) { userId ??= openLiveStreamDto?.UserId; userId = RequestHelpers.GetUserId(User, userId); @@ -295,7 +299,8 @@ public class MediaInfoController : BaseJellyfinApiController DeviceProfile = openLiveStreamDto?.DeviceProfile, EnableDirectPlay = enableDirectPlay ?? openLiveStreamDto?.EnableDirectPlay ?? true, EnableDirectStream = enableDirectStream ?? openLiveStreamDto?.EnableDirectStream ?? true, - DirectPlayProtocols = openLiveStreamDto?.DirectPlayProtocols ?? new[] { MediaProtocol.Http } + DirectPlayProtocols = openLiveStreamDto?.DirectPlayProtocols ?? new[] { MediaProtocol.Http }, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding ?? openLiveStreamDto?.AlwaysBurnInSubtitleWhenTranscoding ?? false }; return await _mediaInfoHelper.OpenMediaSource(HttpContext, request).ConfigureAwait(false); } diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index fe73534967..41c4886d4f 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -160,6 +160,7 @@ public class UniversalAudioController : BaseJellyfinApiController true, true, true, + false, Request.HttpContext.GetNormalizedRemoteIP()); } diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 9bda27031b..5050cab418 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -156,6 +156,7 @@ public class MediaInfoHelper /// Enable transcoding. /// Allow video stream copy. /// Allow audio stream copy. + /// Always burn-in subtitle when transcoding. /// Requesting IP address. public void SetDeviceSpecificData( BaseItem item, @@ -175,6 +176,7 @@ public class MediaInfoHelper bool enableTranscoding, bool allowVideoStreamCopy, bool allowAudioStreamCopy, + bool alwaysBurnInSubtitleWhenTranscoding, IPAddress ipAddress) { var streamBuilder = new StreamBuilder(_mediaEncoder, _logger); @@ -188,7 +190,8 @@ public class MediaInfoHelper Profile = profile, MaxAudioChannels = maxAudioChannels, AllowAudioStreamCopy = allowAudioStreamCopy, - AllowVideoStreamCopy = allowVideoStreamCopy + AllowVideoStreamCopy = allowVideoStreamCopy, + AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding, }; if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) @@ -420,6 +423,7 @@ public class MediaInfoHelper true, true, true, + request.AlwaysBurnInSubtitleWhenTranscoding, httpContext.GetNormalizedRemoteIP()); } else diff --git a/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs b/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs index 53104988f5..978e99b35c 100644 --- a/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs +++ b/Jellyfin.Api/Models/MediaInfoDtos/OpenLiveStreamDto.cs @@ -65,6 +65,11 @@ public class OpenLiveStreamDto /// public bool? EnableDirectStream { get; set; } + /// + /// Gets or sets a value indicating whether always burn in subtitles when transcoding. + /// + public bool? AlwaysBurnInSubtitleWhenTranscoding { get; set; } + /// /// Gets or sets the device profile. /// diff --git a/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs index 9e12ddde65..82f603ca1e 100644 --- a/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs +++ b/Jellyfin.Api/Models/MediaInfoDtos/PlaybackInfoDto.cs @@ -82,4 +82,9 @@ public class PlaybackInfoDto /// Gets or sets a value indicating whether to auto open the live stream. /// public bool? AutoOpenLiveStream { get; set; } + + /// + /// Gets or sets a value indicating whether always burn in subtitles when transcoding. + /// + public bool? AlwaysBurnInSubtitleWhenTranscoding { get; set; } } diff --git a/MediaBrowser.Model/Dlna/MediaOptions.cs b/MediaBrowser.Model/Dlna/MediaOptions.cs index eca971e95e..6b26ca94b5 100644 --- a/MediaBrowser.Model/Dlna/MediaOptions.cs +++ b/MediaBrowser.Model/Dlna/MediaOptions.cs @@ -49,6 +49,11 @@ namespace MediaBrowser.Model.Dlna /// public bool AllowVideoStreamCopy { get; set; } + /// + /// Gets or sets a value indicating whether always burn in subtitles when transcoding. + /// + public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; } + /// /// Gets or sets the item id. /// diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index f68a8bca34..e4492ac79b 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -20,8 +20,8 @@ namespace MediaBrowser.Model.Dlna // Aliases internal const TranscodeReason ContainerReasons = TranscodeReason.ContainerNotSupported | TranscodeReason.ContainerBitrateExceedsLimit; internal const TranscodeReason AudioReasons = TranscodeReason.AudioCodecNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.AudioChannelsNotSupported | TranscodeReason.AudioProfileNotSupported | TranscodeReason.AudioSampleRateNotSupported | TranscodeReason.SecondaryAudioNotSupported | TranscodeReason.AudioBitDepthNotSupported | TranscodeReason.AudioIsExternal; - internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported; - internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported; + internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.VideoProfileNotSupported; + internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecTagNotSupported; private readonly ILogger _logger; private readonly ITranscoderSupport _transcoderSupport; @@ -352,7 +352,7 @@ namespace MediaBrowser.Model.Dlna return TranscodeReason.VideoBitrateNotSupported; case ProfileConditionValue.VideoCodecTag: - return TranscodeReason.VideoCodecNotSupported; + return TranscodeReason.VideoCodecTagNotSupported; case ProfileConditionValue.VideoFramerate: return TranscodeReason.VideoFramerateNotSupported; @@ -765,7 +765,19 @@ namespace MediaBrowser.Model.Dlna { var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol); - playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; + if (options.AlwaysBurnInSubtitleWhenTranscoding && (playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0) + { + playlistItem.SubtitleDeliveryMethod = SubtitleDeliveryMethod.Encode; + foreach (SubtitleProfile profile in options.Profile.SubtitleProfiles) + { + profile.Method = SubtitleDeliveryMethod.Encode; + } + } + else + { + playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; + } + playlistItem.SubtitleFormat = subtitleProfile.Format; playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format }; } diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs index 24eab1a744..92f467eb08 100644 --- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Model.MediaInfo { EnableDirectPlay = true; EnableDirectStream = true; + AlwaysBurnInSubtitleWhenTranscoding = false; DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http }; } @@ -40,6 +41,8 @@ namespace MediaBrowser.Model.MediaInfo public bool EnableDirectStream { get; set; } + public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; } + public IReadOnlyList DirectPlayProtocols { get; set; } } } diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs index bbdf4536b7..39c5ac8fa4 100644 --- a/MediaBrowser.Model/Session/TranscodeReason.cs +++ b/MediaBrowser.Model/Session/TranscodeReason.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.Model.Session // Video Constraints VideoProfileNotSupported = 1 << 6, VideoRangeTypeNotSupported = 1 << 24, + VideoCodecTagNotSupported = 1 << 25, VideoLevelNotSupported = 1 << 7, VideoResolutionNotSupported = 1 << 8, VideoBitDepthNotSupported = 1 << 9,