aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgnattu <gnattuoc@me.com>2024-09-22 00:34:47 +0800
committergnattu <gnattuoc@me.com>2024-09-22 00:35:41 +0800
commitd944f415f3cc0e5433d94b11a16684ca3f0131ec (patch)
treecfa42406c11a612af0683c2459b42df2b5741b2e
parent9ff7575c85d05714a497502f95ee38f6f3b87752 (diff)
Let HLS Controller decide if subtitle should be burn in
Previously, we predicted whether the subtitle should be burned in with transcode reasons, but that was not accurate because the actual transcoding codec is only determined after the client has requested the stream. This pass through the option to the `DynamicHlsController` to handle the subtitle burn-in during the actual transcoding process. Now the client should be responsible to conditionally load the subtitle when this option is enabled.
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs63
-rw-r--r--Jellyfin.Api/Helpers/MediaInfoHelper.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs38
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs18
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs7
6 files changed, 81 insertions, 53 deletions
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index db1d86698..924f010e4 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -158,6 +158,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="maxHeight">Optional. The max height.</param>
/// <param name="enableSubtitlesInManifest">Optional. Whether to enable subtitles in the manifest.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Hls live stream retrieved.</response>
/// <returns>A <see cref="FileResult"/> containing the hls file.</returns>
[HttpGet("Videos/{itemId}/live.m3u8")]
@@ -216,7 +217,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? maxWidth,
[FromQuery] int? maxHeight,
[FromQuery] bool? enableSubtitlesInManifest,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
VideoRequestDto streamingRequest = new VideoRequestDto
{
@@ -251,7 +253,7 @@ public class DynamicHlsController : BaseJellyfinApiController
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -271,7 +273,8 @@ public class DynamicHlsController : BaseJellyfinApiController
MaxHeight = maxHeight,
MaxWidth = maxWidth,
EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
// CTS lifecycle is managed internally.
@@ -398,6 +401,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
/// <param name="enableTrickplay">Enable trickplay image playlists being added to master playlist.</param>
/// <param name="enableAudioVbrEncoding">Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
[HttpGet("Videos/{itemId}/master.m3u8")]
@@ -457,7 +461,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] Dictionary<string, string> streamOptions,
[FromQuery] bool enableAdaptiveBitrateStreaming = true,
[FromQuery] bool enableTrickplay = true,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
var streamingRequest = new HlsVideoRequestDto
{
@@ -493,7 +498,7 @@ public class DynamicHlsController : BaseJellyfinApiController
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -512,7 +517,8 @@ public class DynamicHlsController : BaseJellyfinApiController
StreamOptions = streamOptions,
EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
EnableTrickplay = enableTrickplay,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
@@ -572,6 +578,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAdaptiveBitrateStreaming">Enable adaptive bitrate streaming.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Audio stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the playlist file.</returns>
[HttpGet("Audio/{itemId}/master.m3u8")]
@@ -629,7 +636,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
[FromQuery] bool enableAdaptiveBitrateStreaming = true,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
var streamingRequest = new HlsAudioRequestDto
{
@@ -663,7 +671,7 @@ public class DynamicHlsController : BaseJellyfinApiController
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -681,7 +689,8 @@ public class DynamicHlsController : BaseJellyfinApiController
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
@@ -741,6 +750,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
[HttpGet("Videos/{itemId}/main.m3u8")]
@@ -797,7 +807,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
using var cancellationTokenSource = new CancellationTokenSource();
var streamingRequest = new VideoRequestDto
@@ -834,7 +845,7 @@ public class DynamicHlsController : BaseJellyfinApiController
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -851,7 +862,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource)
@@ -911,6 +923,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Audio stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
[HttpGet("Audio/{itemId}/main.m3u8")]
@@ -966,7 +979,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
using var cancellationTokenSource = new CancellationTokenSource();
var streamingRequest = new StreamingRequestDto
@@ -1001,7 +1015,7 @@ public class DynamicHlsController : BaseJellyfinApiController
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -1018,7 +1032,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource)
@@ -1084,6 +1099,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
[HttpGet("Videos/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
@@ -1146,7 +1162,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
var streamingRequest = new VideoRequestDto
{
@@ -1185,7 +1202,7 @@ public class DynamicHlsController : BaseJellyfinApiController
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -1202,7 +1219,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await GetDynamicSegment(streamingRequest, segmentId)
@@ -1267,6 +1285,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// <param name="context">Optional. The <see cref="EncodingContext"/>.</param>
/// <param name="streamOptions">Optional. The streaming options.</param>
/// <param name="enableAudioVbrEncoding">Optional. Whether to enable Audio Encoding.</param>
+ /// <param name="alwaysBurnInSubtitleWhenTranscoding">Whether to always burn in subtitles when transcoding.</param>
/// <response code="200">Video stream returned.</response>
/// <returns>A <see cref="FileResult"/> containing the audio file.</returns>
[HttpGet("Audio/{itemId}/hls1/{playlistId}/{segmentId}.{container}")]
@@ -1328,7 +1347,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary<string, string> streamOptions,
- [FromQuery] bool enableAudioVbrEncoding = true)
+ [FromQuery] bool enableAudioVbrEncoding = true,
+ [FromQuery] bool alwaysBurnInSubtitleWhenTranscoding = false)
{
var streamingRequest = new StreamingRequestDto
{
@@ -1365,7 +1385,7 @@ public class DynamicHlsController : BaseJellyfinApiController
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
- SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
+ SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
@@ -1382,7 +1402,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAudioVbrEncoding = enableAudioVbrEncoding
+ EnableAudioVbrEncoding = enableAudioVbrEncoding,
+ AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};
return await GetDynamicSegment(streamingRequest, segmentId)
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index 5050cab41..2d9ecd4f0 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -293,6 +293,7 @@ public class MediaInfoHelper
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
mediaSource.TranscodingContainer = streamInfo.Container;
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
+ mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true";
}
else
{
@@ -310,6 +311,11 @@ public class MediaInfoHelper
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}
+
+ if (streamInfo.AlwaysBurnInSubtitleWhenTranscoding)
+ {
+ mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true";
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
index f77186e25..20f51ddb7 100644
--- a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs
@@ -193,6 +193,8 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool EnableAudioVbrEncoding { get; set; }
+ public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; }
+
public string GetOption(string qualifier, string name)
{
var value = GetOption(qualifier + "-" + name);
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index d8c4f2f85..f8ba8ddd8 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -941,7 +941,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
// DVBSUB uses the fixed canvas size 720x576
if (state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& !state.SubtitleStream.IsTextSubtitleStream
&& !string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
{
@@ -1240,7 +1240,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// sub2video for external graphical subtitles
if (state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& !state.SubtitleStream.IsTextSubtitleStream
&& state.SubtitleStream.IsExternal)
{
@@ -2554,7 +2554,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive;
- if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !isCopyingTimestamps)
+ if (state.SubtitleStream is not null && state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state) && !isCopyingTimestamps)
{
var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds;
@@ -2755,7 +2755,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.AudioStream.IsExternal)
{
bool hasExternalGraphicsSubs = state.SubtitleStream is not null
- && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ && ShouldEncodeSubtitle(state)
&& state.SubtitleStream.IsExternal
&& !state.SubtitleStream.IsTextSubtitleStream;
int externalAudioMapIndex = hasExternalGraphicsSubs ? 2 : 1;
@@ -3475,7 +3475,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doToneMap = IsSwTonemapAvailable(state, options);
var requireDoviReshaping = doToneMap && state.VideoStream.VideoRangeType == VideoRangeType.DOVI;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -3618,7 +3618,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doCuTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -3824,7 +3824,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -4064,7 +4064,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doOclTonemap = !doVppTonemap && IsHwTonemapAvailable(state, options);
var doTonemap = doVppTonemap || doOclTonemap;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -4320,7 +4320,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -4636,7 +4636,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doTonemap = doVaVppTonemap || doOclTonemap;
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -4858,7 +4858,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doVkTonemap = IsVulkanHwTonemapAvailable(state, options);
var doDeintH2645 = doDeintH264 || doDeintHevc;
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -5091,7 +5091,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -5339,7 +5339,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var hwScaleFilter = GetHwScaleFilter("scale", "vt", scaleFormat, false, swpInW, swpInH, reqW, reqH, reqMaxW, reqMaxH);
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -5511,7 +5511,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var doDeintH2645 = doDeintH264 || doDeintHevc;
var doOclTonemap = IsHwTonemapAvailable(state, options);
- var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream != null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
var hasAssSubs = hasSubs
@@ -5722,7 +5722,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- var hasSubs = state.SubtitleStream is not null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasSubs = state.SubtitleStream is not null && ShouldEncodeSubtitle(state);
var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
@@ -7156,7 +7156,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args += keyFrameArg;
- var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasGraphicalSubs = state.SubtitleStream is not null && !state.SubtitleStream.IsTextSubtitleStream && ShouldEncodeSubtitle(state);
var hasCopyTs = false;
@@ -7361,5 +7361,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase);
}
+
+ private static bool ShouldEncodeSubtitle(EncodingJobInfo state)
+ {
+ return state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode
+ || (state.BaseRequest.AlwaysBurnInSubtitleWhenTranscoding && !IsCopyCodec(state.OutputVideoCodec));
+ }
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index bf122dcc7..6c45f1946 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -639,7 +639,8 @@ namespace MediaBrowser.Model.Dlna
RunTimeTicks = item.RunTimeTicks,
Context = options.Context,
DeviceProfile = options.Profile,
- SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles)
+ SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles),
+ AlwaysBurnInSubtitleWhenTranscoding = options.AlwaysBurnInSubtitleWhenTranscoding
};
var subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null;
@@ -767,20 +768,7 @@ namespace MediaBrowser.Model.Dlna
if (subtitleStream is not null)
{
var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
-
- 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.SubtitleDeliveryMethod = subtitleProfile.Method;
playlistItem.SubtitleFormat = subtitleProfile.Format;
playlistItem.SubtitleCodecs = [subtitleProfile.Format];
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 3be686088..1ae4e1962 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -271,6 +271,11 @@ public class StreamInfo
public bool EnableAudioVbrEncoding { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether always burn in subtitles when transcoding.
+ /// </summary>
+ public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; }
+
+ /// <summary>
/// Gets a value indicating whether the stream is direct.
/// </summary>
public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay)
@@ -953,7 +958,7 @@ public class StreamInfo
list.Add(new NameValuePair("VideoCodec", videoCodecs));
list.Add(new NameValuePair("AudioCodec", audioCodecs));
list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
- list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+ list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && (item.AlwaysBurnInSubtitleWhenTranscoding || item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External) ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));