Skip to content

Commit

Permalink
Let HLS Controller decide if subtitle should be burn in
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gnattu committed Sep 21, 2024
1 parent 9ff7575 commit d944f41
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 53 deletions.
63 changes: 42 additions & 21 deletions Jellyfin.Api/Controllers/DynamicHlsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public DynamicHlsController(
/// <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")]
Expand Down Expand Up @@ -216,7 +217,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
{
Expand Down Expand Up @@ -251,7 +253,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -271,7 +273,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
MaxHeight = maxHeight,
MaxWidth = maxWidth,
EnableSubtitlesInManifest = enableSubtitlesInManifest ?? true,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

// CTS lifecycle is managed internally.
Expand Down Expand Up @@ -398,6 +401,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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")]
Expand Down Expand Up @@ -457,7 +461,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
{
Expand Down Expand Up @@ -493,7 +498,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -512,7 +517,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
StreamOptions = streamOptions,
EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
EnableTrickplay = enableTrickplay,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
Expand Down Expand Up @@ -572,6 +578,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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")]
Expand Down Expand Up @@ -629,7 +636,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
{
Expand Down Expand Up @@ -663,7 +671,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -681,7 +689,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
Expand Down Expand Up @@ -741,6 +750,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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")]
Expand Down Expand Up @@ -797,7 +807,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
Expand Down Expand Up @@ -834,7 +845,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -851,7 +862,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource)
Expand Down Expand Up @@ -911,6 +923,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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")]
Expand Down Expand Up @@ -966,7 +979,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
Expand Down Expand Up @@ -1001,7 +1015,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -1018,7 +1032,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await GetVariantPlaylistInternal(streamingRequest, cancellationTokenSource)
Expand Down Expand Up @@ -1084,6 +1099,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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}")]
Expand Down Expand Up @@ -1146,7 +1162,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
{
Expand Down Expand Up @@ -1185,7 +1202,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
MaxHeight = maxHeight,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -1202,7 +1219,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await GetDynamicSegment(streamingRequest, segmentId)
Expand Down Expand Up @@ -1267,6 +1285,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
/// <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}")]
Expand Down Expand Up @@ -1328,7 +1347,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
[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
{
Expand Down Expand Up @@ -1365,7 +1385,7 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
Height = height,
VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.External,
MaxRefFrames = maxRefFrames,
MaxVideoBitDepth = maxVideoBitDepth,
RequireAvc = requireAvc ?? false,
Expand All @@ -1382,7 +1402,8 @@ [FromQuery] [RegularExpression(EncodingHelper.ValidationRegex)] string? subtitle
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
EnableAudioVbrEncoding = enableAudioVbrEncoding
EnableAudioVbrEncoding = enableAudioVbrEncoding,
AlwaysBurnInSubtitleWhenTranscoding = alwaysBurnInSubtitleWhenTranscoding
};

return await GetDynamicSegment(streamingRequest, segmentId)
Expand Down
6 changes: 6 additions & 0 deletions Jellyfin.Api/Helpers/MediaInfoHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ public void SetDeviceSpecificData(
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
mediaSource.TranscodingContainer = streamInfo.Container;
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true";
}
else
{
Expand All @@ -310,6 +311,11 @@ public void SetDeviceSpecificData(
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}

if (streamInfo.AlwaysBurnInSubtitleWhenTranscoding)
{
mediaSource.TranscodingUrl += "&alwaysBurnInSubtitleWhenTranscoding=true";
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ public BaseEncodingJobOptions()

public bool EnableAudioVbrEncoding { get; set; }

public bool AlwaysBurnInSubtitleWhenTranscoding { get; set; }

public string GetOption(string qualifier, string name)
{
var value = GetOption(qualifier + "-" + name);
Expand Down
Loading

0 comments on commit d944f41

Please sign in to comment.