diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index ba92d811c..a58da17d5 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -738,7 +738,7 @@ public class DynamicHlsHelper { var width = state.VideoStream.Width ?? 0; var height = state.VideoStream.Height ?? 0; - var framerate = state.VideoStream.AverageFrameRate ?? 30; + var framerate = state.VideoStream.ReferenceFrameRate ?? 30; var bitDepth = state.VideoStream.BitDepth ?? 8; return HlsCodecStringHelpers.GetVp9String( width, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 24cd141dc..220b5d57b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1534,7 +1534,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (maxrate.HasValue && state.VideoStream is not null) { - var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate; + var contentRate = state.VideoStream.ReferenceFrameRate; if (contentRate.HasValue && contentRate.Value > maxrate.Value) { @@ -2218,7 +2218,7 @@ namespace MediaBrowser.Controller.MediaEncoding var requestedFramerate = request.MaxFramerate ?? request.Framerate; if (requestedFramerate.HasValue) { - var videoFrameRate = videoStream.AverageFrameRate ?? videoStream.RealFrameRate; + var videoFrameRate = videoStream.ReferenceFrameRate; if (!videoFrameRate.HasValue || videoFrameRate.Value > requestedFramerate.Value) { @@ -3234,7 +3234,7 @@ namespace MediaBrowser.Controller.MediaEncoding public static string GetSwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options) { - var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.AverageFrameRate <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && state.VideoStream?.ReferenceFrameRate <= 30; return string.Format( CultureInfo.InvariantCulture, "{0}={1}:-1:0", @@ -3244,7 +3244,7 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetHwDeinterlaceFilter(EncodingJobInfo state, EncodingOptions options, string hwDeintSuffix) { - var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; if (hwDeintSuffix.Contains("cuda", StringComparison.OrdinalIgnoreCase)) { var useBwdif = string.Equals(options.DeinterlaceMethod, "bwdif", StringComparison.OrdinalIgnoreCase) @@ -3598,7 +3598,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isSwEncoder = !isNvencEncoder; var isCuInCuOut = isNvDecoder && isNvencEncoder; - var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.AverageFrameRate ?? 60) <= 30; + var doubleRateDeint = options.DeinterlaceDoubleRate && (state.VideoStream?.ReferenceFrameRate ?? 60) <= 30; var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true); var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true); var doDeintH2645 = doDeintH264 || doDeintHevc; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 72df7151d..caa312987 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -305,7 +305,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (BaseRequest.Static || EncodingHelper.IsCopyCodec(OutputVideoCodec)) { - return VideoStream is null ? null : (VideoStream.AverageFrameRate ?? VideoStream.RealFrameRate); + return VideoStream?.ReferenceFrameRate; } return BaseRequest.MaxFramerate ?? BaseRequest.Framerate; diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 7f387bfaa..cd18fea12 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -810,7 +810,7 @@ namespace MediaBrowser.Model.Dlna if (options.AllowVideoStreamCopy) { // prefer direct copy profile - float videoFramerate = videoStream?.AverageFrameRate ?? videoStream?.RealFrameRate ?? 0; + float videoFramerate = videoStream?.ReferenceFrameRate ?? 0; TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp; int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio); int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video); @@ -875,7 +875,7 @@ namespace MediaBrowser.Model.Dlna playlistItem.VideoCodecs = videoCodecs; // Copy video codec options as a starting point, this applies to transcode and direct-stream - playlistItem.MaxFramerate = videoStream?.AverageFrameRate; + playlistItem.MaxFramerate = videoStream?.ReferenceFrameRate; var qualifier = videoStream?.Codec; if (videoStream?.Level is not null) { @@ -949,7 +949,7 @@ namespace MediaBrowser.Model.Dlna double? videoLevel = videoStream?.Level; string? videoProfile = videoStream?.Profile; VideoRangeType? videoRangeType = videoStream?.VideoRangeType; - float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; + float videoFramerate = videoStream is null ? 0 : videoStream.ReferenceFrameRate ?? 0; bool? isAnamorphic = videoStream?.IsAnamorphic; bool? isInterlaced = videoStream?.IsInterlaced; string? videoCodecTag = videoStream?.CodecTag; @@ -1208,7 +1208,7 @@ namespace MediaBrowser.Model.Dlna double? videoLevel = videoStream?.Level; string? videoProfile = videoStream?.Profile; VideoRangeType? videoRangeType = videoStream?.VideoRangeType; - float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; + float videoFramerate = videoStream is null ? 0 : videoStream.ReferenceFrameRate ?? 0; bool? isAnamorphic = videoStream?.IsAnamorphic; bool? isInterlaced = videoStream?.IsInterlaced; string? videoCodecTag = videoStream?.CodecTag; diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index c8a341d41..8232ee3fe 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -217,7 +217,7 @@ namespace MediaBrowser.Model.Dlna var stream = TargetVideoStream; return MaxFramerate.HasValue && !IsDirectStream ? MaxFramerate - : stream is null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; + : stream?.ReferenceFrameRate; } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index a0e8c39be..b5d19edd6 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -525,6 +525,23 @@ namespace MediaBrowser.Model.Entities /// The real frame rate. public float? RealFrameRate { get; set; } + /// + /// Gets the framerate used as reference. + /// Prefer AverageFrameRate, if that is null or an unrealistic value + /// then fallback to RealFrameRate. + /// + /// The reference frame rate. + public float? ReferenceFrameRate + { + get + { + // In some cases AverageFrameRate for videos will be read as 1000fps even if it is not. + // This is probably due to a library compatability issue. + // See https://github.com/jellyfin/jellyfin/pull/12603#discussion_r1748044018 for more info. + return AverageFrameRate < 1000 ? AverageFrameRate : RealFrameRate; + } + } + /// /// Gets or sets the profile. /// diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index a547779de..2afec3f6c 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -348,7 +348,7 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("aspectratio", stream.AspectRatio); } - var framerate = stream.AverageFrameRate ?? stream.RealFrameRate; + var framerate = stream.ReferenceFrameRate; if (framerate.HasValue) {