Browse Source

fix jellyfin streaming and sar calculation (#1195)

pull/1196/head
Jason Dove 3 years ago committed by GitHub
parent
commit
8ff6bf652c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 40
      ErsatzTV.FFmpeg/MediaStream.cs
  3. 19
      ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs
  4. 1
      ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinMediaStreamResponse.cs

4
CHANGELOG.md

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed
- Fix scaling anamorphic content from non-local libraries
- Fix direct streaming content from Jellyfin that has external subtitles
- Note that these subtitles are not currently supported in ETV, but they did cause a playback issue
## [0.7.5-beta] - 2023-03-05 ## [0.7.5-beta] - 2023-03-05
### Added ### Added

40
ErsatzTV.FFmpeg/MediaStream.cs

@ -15,7 +15,7 @@ public record VideoStream(
Option<IPixelFormat> PixelFormat, Option<IPixelFormat> PixelFormat,
ColorParams ColorParams, ColorParams ColorParams,
FrameSize FrameSize, FrameSize FrameSize,
string SampleAspectRatio, string MaybeSampleAspectRatio,
string DisplayAspectRatio, string DisplayAspectRatio,
Option<string> FrameRate, Option<string> FrameRate,
bool StillImage, bool StillImage,
@ -25,7 +25,37 @@ public record VideoStream(
StreamKind.Video) StreamKind.Video)
{ {
public int BitDepth => PixelFormat.Map(pf => pf.BitDepth).IfNone(8); public int BitDepth => PixelFormat.Map(pf => pf.BitDepth).IfNone(8);
public string SampleAspectRatio
{
get
{
// some media servers don't provide sample aspect ratio so we have to calculate it
if (string.IsNullOrWhiteSpace(MaybeSampleAspectRatio) || MaybeSampleAspectRatio == "0:0")
{
// first check for decimal DAR
if (!double.TryParse(DisplayAspectRatio, out double dar))
{
// if not, assume it's a ratio
string[] split = DisplayAspectRatio.Split(':');
var num = double.Parse(split[0]);
var den = double.Parse(split[1]);
dar = num / den;
}
double res = FrameSize.Width / (double)FrameSize.Height;
return $"{dar}:{res}";
}
else
{
string[] split = MaybeSampleAspectRatio.Split(':');
var num = double.Parse(split[0]);
var den = double.Parse(split[1]);
return $"{num}:{den}";
}
}
}
public bool IsAnamorphic public bool IsAnamorphic
{ {
get get
@ -89,13 +119,13 @@ public record VideoStream(
private double GetSAR() private double GetSAR()
{ {
// some media servers don't provide sample aspect ratio so we have to calculate it // some media servers don't provide sample aspect ratio so we have to calculate it
if (string.IsNullOrWhiteSpace(SampleAspectRatio)) if (string.IsNullOrWhiteSpace(MaybeSampleAspectRatio) || MaybeSampleAspectRatio == "0:0")
{ {
// first check for decimal DAR // first check for decimal DAR
if (!double.TryParse(DisplayAspectRatio, out double dar)) if (!double.TryParse(DisplayAspectRatio, out double dar))
{ {
// if not, assume it's a ratio // if not, assume it's a ratio
string[] split = DisplayAspectRatio.Split(':'); string[] split = DisplayAspectRatio.Split(':');
var num = double.Parse(split[0]); var num = double.Parse(split[0]);
var den = double.Parse(split[1]); var den = double.Parse(split[1]);
dar = num / den; dar = num / den;
@ -106,7 +136,7 @@ public record VideoStream(
} }
else else
{ {
string[] split = SampleAspectRatio.Split(':'); string[] split = MaybeSampleAspectRatio.Split(':');
var num = double.Parse(split[0]); var num = double.Parse(split[0]);
var den = double.Parse(split[1]); var den = double.Parse(split[1]);
return num / den; return num / den;

19
ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs

@ -895,7 +895,18 @@ public class JellyfinApiClient : IJellyfinApiClient
} }
JellyfinMediaSourceResponse mediaSource = response.MediaSources.Head(); JellyfinMediaSourceResponse mediaSource = response.MediaSources.Head();
IList<JellyfinMediaStreamResponse> streams = mediaSource.MediaStreams;
// jellyfin includes external streams first, obscuring real stream indexes
// from the source file
int streamIndexOffset = mediaSource.MediaStreams
.Filter(s => s.IsExternal)
.Map(s => s.Index + 1)
.OrderByDescending(i => i)
.FirstOrDefault();
IList<JellyfinMediaStreamResponse> streams = mediaSource.MediaStreams
.Filter(s => s.IsExternal == false)
.ToList();
Option<JellyfinMediaStreamResponse> maybeVideoStream = Option<JellyfinMediaStreamResponse> maybeVideoStream =
streams.Find(s => s.Type == JellyfinMediaStreamType.Video); streams.Find(s => s.Type == JellyfinMediaStreamType.Video);
return maybeVideoStream.Map( return maybeVideoStream.Map(
@ -948,7 +959,7 @@ public class JellyfinApiClient : IJellyfinApiClient
{ {
MediaVersionId = version.Id, MediaVersionId = version.Id,
MediaStreamKind = MediaStreamKind.Video, MediaStreamKind = MediaStreamKind.Video,
Index = videoStream.Index, Index = videoStream.Index - streamIndexOffset,
Codec = videoStream.Codec, Codec = videoStream.Codec,
Profile = (videoStream.Profile ?? string.Empty).ToLowerInvariant(), Profile = (videoStream.Profile ?? string.Empty).ToLowerInvariant(),
Default = videoStream.IsDefault, Default = videoStream.IsDefault,
@ -968,7 +979,7 @@ public class JellyfinApiClient : IJellyfinApiClient
{ {
MediaVersionId = version.Id, MediaVersionId = version.Id,
MediaStreamKind = MediaStreamKind.Audio, MediaStreamKind = MediaStreamKind.Audio,
Index = audioStream.Index, Index = audioStream.Index - streamIndexOffset,
Codec = audioStream.Codec, Codec = audioStream.Codec,
Profile = (audioStream.Profile ?? string.Empty).ToLowerInvariant(), Profile = (audioStream.Profile ?? string.Empty).ToLowerInvariant(),
Channels = audioStream.Channels ?? 2, Channels = audioStream.Channels ?? 2,
@ -988,7 +999,7 @@ public class JellyfinApiClient : IJellyfinApiClient
{ {
MediaVersionId = version.Id, MediaVersionId = version.Id,
MediaStreamKind = MediaStreamKind.Subtitle, MediaStreamKind = MediaStreamKind.Subtitle,
Index = subtitleStream.Index, Index = subtitleStream.Index - streamIndexOffset,
Codec = subtitleStream.Codec, Codec = subtitleStream.Codec,
Default = subtitleStream.IsDefault, Default = subtitleStream.IsDefault,
Forced = subtitleStream.IsForced, Forced = subtitleStream.IsForced,

1
ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinMediaStreamResponse.cs

@ -11,6 +11,7 @@ public class JellyfinMediaStreamResponse
public int Index { get; set; } public int Index { get; set; }
public bool IsDefault { get; set; } public bool IsDefault { get; set; }
public bool IsForced { get; set; } public bool IsForced { get; set; }
public bool IsExternal { get; set; }
public string Profile { get; set; } public string Profile { get; set; }
public string AspectRatio { get; set; } public string AspectRatio { get; set; }
public int? Channels { get; set; } public int? Channels { get; set; }

Loading…
Cancel
Save