Fix the fMP4 HLS audio sync issue on Safari
This commit is contained in:
parent
dacbfc83ff
commit
b2c58338f2
|
@ -1599,7 +1599,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
state.BaseRequest.BreakOnNonKeyFrames = false;
|
state.BaseRequest.BreakOnNonKeyFrames = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions);
|
|
||||||
var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
|
var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
|
||||||
|
|
||||||
var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath));
|
var directory = Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath));
|
||||||
|
@ -1608,12 +1607,15 @@ namespace Jellyfin.Api.Controllers
|
||||||
var outputExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer);
|
var outputExtension = EncodingHelper.GetSegmentFileExtension(state.Request.SegmentContainer);
|
||||||
var outputTsArg = outputPrefix + "%d" + outputExtension;
|
var outputTsArg = outputPrefix + "%d" + outputExtension;
|
||||||
|
|
||||||
var segmentFormat = outputExtension.TrimStart('.');
|
var segmentFormat = string.Empty;
|
||||||
if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
|
var segmentContainer = outputExtension.TrimStart('.');
|
||||||
|
var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions, segmentContainer);
|
||||||
|
|
||||||
|
if (string.Equals(segmentContainer, "ts", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
segmentFormat = "mpegts";
|
segmentFormat = "mpegts";
|
||||||
}
|
}
|
||||||
else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch
|
var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch
|
||||||
{
|
{
|
||||||
|
@ -1627,7 +1629,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError("Invalid HLS segment container: {SegmentFormat}", segmentFormat);
|
_logger.LogError("Invalid HLS segment container: {SegmentContainer}, default to mpegts", segmentContainer);
|
||||||
|
segmentFormat = "mpegts";
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128
|
var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128
|
||||||
|
@ -1647,7 +1650,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -hls_segment_type {8} -start_number {9}{10} -hls_segment_filename \"{12}\" -hls_playlist_type {11} -hls_list_size 0 -y \"{13}\"",
|
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size {6} -f hls -max_delay 5000000 -hls_time {7} -hls_segment_type {8} -start_number {9}{10} -hls_segment_filename \"{12}\" -hls_playlist_type {11} -hls_list_size 0 -y \"{13}\"",
|
||||||
inputModifier,
|
inputModifier,
|
||||||
_encodingHelper.GetInputArgument(state, _encodingOptions),
|
_encodingHelper.GetInputArgument(state, _encodingOptions, segmentContainer),
|
||||||
threads,
|
threads,
|
||||||
mapArgs,
|
mapArgs,
|
||||||
GetVideoArguments(state, startNumber, isEventPlaylist),
|
GetVideoArguments(state, startNumber, isEventPlaylist),
|
||||||
|
|
|
@ -842,8 +842,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Encoding state.</param>
|
/// <param name="state">Encoding state.</param>
|
||||||
/// <param name="options">Encoding options.</param>
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="segmentContainer">Segment Container.</param>
|
||||||
/// <returns>Input arguments.</returns>
|
/// <returns>Input arguments.</returns>
|
||||||
public string GetInputArgument(EncodingJobInfo state, EncodingOptions options)
|
public string GetInputArgument(EncodingJobInfo state, EncodingOptions options, string segmentContainer)
|
||||||
{
|
{
|
||||||
var arg = new StringBuilder();
|
var arg = new StringBuilder();
|
||||||
var inputVidHwaccelArgs = GetInputVideoHwaccelArgs(state, options);
|
var inputVidHwaccelArgs = GetInputVideoHwaccelArgs(state, options);
|
||||||
|
@ -880,7 +881,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also seek the external subtitles stream.
|
// Also seek the external subtitles stream.
|
||||||
var seekSubParam = GetFastSeekCommandLineParameter(state, options);
|
var seekSubParam = GetFastSeekCommandLineParameter(state, options, segmentContainer);
|
||||||
if (!string.IsNullOrEmpty(seekSubParam))
|
if (!string.IsNullOrEmpty(seekSubParam))
|
||||||
{
|
{
|
||||||
arg.Append(' ').Append(seekSubParam);
|
arg.Append(' ').Append(seekSubParam);
|
||||||
|
@ -897,7 +898,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
if (state.AudioStream != null && state.AudioStream.IsExternal)
|
if (state.AudioStream != null && state.AudioStream.IsExternal)
|
||||||
{
|
{
|
||||||
// Also seek the external audio stream.
|
// Also seek the external audio stream.
|
||||||
var seekAudioParam = GetFastSeekCommandLineParameter(state, options);
|
var seekAudioParam = GetFastSeekCommandLineParameter(state, options, segmentContainer);
|
||||||
if (!string.IsNullOrEmpty(seekAudioParam))
|
if (!string.IsNullOrEmpty(seekAudioParam))
|
||||||
{
|
{
|
||||||
arg.Append(' ').Append(seekAudioParam);
|
arg.Append(' ').Append(seekAudioParam);
|
||||||
|
@ -2167,9 +2168,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
|
/// <param name="segmentContainer">Segment Container.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <value>The fast seek command line parameter.</value>
|
/// <value>The fast seek command line parameter.</value>
|
||||||
public string GetFastSeekCommandLineParameter(EncodingJobInfo state, EncodingOptions options)
|
public string GetFastSeekCommandLineParameter(EncodingJobInfo state, EncodingOptions options, string segmentContainer)
|
||||||
{
|
{
|
||||||
var time = state.BaseRequest.StartTimeTicks ?? 0;
|
var time = state.BaseRequest.StartTimeTicks ?? 0;
|
||||||
var seekParam = string.Empty;
|
var seekParam = string.Empty;
|
||||||
|
@ -2181,11 +2183,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
if (state.IsVideoRequest)
|
if (state.IsVideoRequest)
|
||||||
{
|
{
|
||||||
var outputVideoCodec = GetVideoEncoder(state, options);
|
var outputVideoCodec = GetVideoEncoder(state, options);
|
||||||
|
var segmentFormat = GetSegmentFileExtension(segmentContainer).TrimStart('.');
|
||||||
|
|
||||||
// Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
|
// Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
|
||||||
|
// Disable -noaccurate_seek on mpegts container due to the timestamps issue on some clients,
|
||||||
|
// but it's still required for fMP4 container otherwise the audio can't be synced to the video.
|
||||||
if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase)
|
if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase)
|
||||||
&& state.TranscodingType != TranscodingJobType.Progressive
|
&& state.TranscodingType != TranscodingJobType.Progressive
|
||||||
&& state.TranscodingType != TranscodingJobType.Hls
|
|
||||||
&& !state.EnableBreakOnNonKeyFrames(outputVideoCodec)
|
&& !state.EnableBreakOnNonKeyFrames(outputVideoCodec)
|
||||||
&& (state.BaseRequest.StartTimeTicks ?? 0) > 0)
|
&& (state.BaseRequest.StartTimeTicks ?? 0) > 0)
|
||||||
{
|
{
|
||||||
|
@ -4836,7 +4841,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
|
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions, string segmentContainer)
|
||||||
{
|
{
|
||||||
var inputModifier = string.Empty;
|
var inputModifier = string.Empty;
|
||||||
var probeSizeArgument = string.Empty;
|
var probeSizeArgument = string.Empty;
|
||||||
|
@ -4872,7 +4877,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
inputModifier += " " + GetFastSeekCommandLineParameter(state, encodingOptions);
|
inputModifier += " " + GetFastSeekCommandLineParameter(state, encodingOptions, segmentContainer);
|
||||||
inputModifier = inputModifier.Trim();
|
inputModifier = inputModifier.Trim();
|
||||||
|
|
||||||
if (state.InputProtocol == MediaProtocol.Rtsp)
|
if (state.InputProtocol == MediaProtocol.Rtsp)
|
||||||
|
@ -5179,13 +5184,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
var threads = GetNumberOfThreads(state, encodingOptions, videoCodec);
|
var threads = GetNumberOfThreads(state, encodingOptions, videoCodec);
|
||||||
|
|
||||||
var inputModifier = GetInputModifier(state, encodingOptions);
|
var inputModifier = GetInputModifier(state, encodingOptions, null);
|
||||||
|
|
||||||
return string.Format(
|
return string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
"{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
|
"{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
|
||||||
inputModifier,
|
inputModifier,
|
||||||
GetInputArgument(state, encodingOptions),
|
GetInputArgument(state, encodingOptions, null),
|
||||||
keyFrame,
|
keyFrame,
|
||||||
GetMapArgs(state),
|
GetMapArgs(state),
|
||||||
GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultPreset),
|
GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultPreset),
|
||||||
|
@ -5367,13 +5372,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
var threads = GetNumberOfThreads(state, encodingOptions, null);
|
var threads = GetNumberOfThreads(state, encodingOptions, null);
|
||||||
|
|
||||||
var inputModifier = GetInputModifier(state, encodingOptions);
|
var inputModifier = GetInputModifier(state, encodingOptions, null);
|
||||||
|
|
||||||
return string.Format(
|
return string.Format(
|
||||||
CultureInfo.InvariantCulture,
|
CultureInfo.InvariantCulture,
|
||||||
"{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"",
|
"{0} {1}{7}{8} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1{6} -y \"{5}\"",
|
||||||
inputModifier,
|
inputModifier,
|
||||||
GetInputArgument(state, encodingOptions),
|
GetInputArgument(state, encodingOptions, null),
|
||||||
threads,
|
threads,
|
||||||
" -vn",
|
" -vn",
|
||||||
string.Join(' ', audioTranscodeParams),
|
string.Join(' ', audioTranscodeParams),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user