diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index ba92d811cf..a58da17d59 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 24cd141dcd..220b5d57b9 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 72df7151da..caa312987d 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 7f387bfaae..cd18fea123 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 c8a341d413..8232ee3fe5 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 a0e8c39bee..b5d19edd66 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 a547779de4..2afec3f6cd 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)
{