diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 535ef27c3..3cc6a393b 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -142,28 +142,15 @@ public static class StreamingHelpers } else { - // Enforce more restrictive transcoding profile for LiveTV due to compatability reasons - // Cap the MaxStreamingBitrate to 30Mbps, because we are unable to reliably probe source bitrate, - // which will cause the client to request extremely high bitrate that may fail the player/encoder - streamingRequest.VideoBitRate = streamingRequest.VideoBitRate > 30000000 ? 30000000 : streamingRequest.VideoBitRate; - - if (streamingRequest.SegmentContainer is not null) - { - // Remove all fmp4 transcoding profiles, because it causes playback error and/or A/V sync issues - // Notably: Some channels won't play on FireFox and LG webOS - // Some channels from HDHomerun will experience A/V sync issues - streamingRequest.SegmentContainer = "ts"; - streamingRequest.VideoCodec = "h264"; - streamingRequest.AudioCodec = "aac"; - state.SupportedVideoCodecs = ["h264"]; - state.Request.VideoCodec = "h264"; - state.SupportedAudioCodecs = ["aac"]; - state.Request.AudioCodec = "aac"; - } - var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false); mediaSource = liveStreamInfo.Item1; state.DirectStreamProvider = liveStreamInfo.Item2; + + // Cap the max bitrate when it is too high. This is usually due to ffmpeg is unable to probe the source liveTV streams' bitrate. + if (mediaSource.FallbackMaxStreamingBitrate is not null && streamingRequest.VideoBitRate is not null) + { + streamingRequest.VideoBitRate = Math.Min(streamingRequest.VideoBitRate.Value, mediaSource.FallbackMaxStreamingBitrate.Value); + } } var encodingOptions = serverConfigurationManager.GetEncodingOptions(); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index ad00149e0..c18becf72 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -805,6 +805,7 @@ namespace MediaBrowser.Model.Dlna } var transcodingProfiles = options.Profile.TranscodingProfiles + .Where(i => !item.UseMostCompatibleTranscodingProfile || string.Equals(i.Container, "ts", StringComparison.OrdinalIgnoreCase)) .Where(i => i.Type == playlistItem.MediaType && i.Context == options.Context); if (options.AllowVideoStreamCopy) diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 1c6037325..eff2e09da 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; using MediaBrowser.Model.Dlna; @@ -24,6 +25,7 @@ namespace MediaBrowser.Model.Dto SupportsDirectStream = true; SupportsDirectPlay = true; SupportsProbing = true; + UseMostCompatibleTranscodingProfile = false; } public MediaProtocol Protocol { get; set; } @@ -70,6 +72,9 @@ namespace MediaBrowser.Model.Dto public bool IsInfiniteStream { get; set; } + [DefaultValue(false)] + public bool UseMostCompatibleTranscodingProfile { get; set; } + public bool RequiresOpening { get; set; } public string OpenToken { get; set; } @@ -98,6 +103,8 @@ namespace MediaBrowser.Model.Dto public int? Bitrate { get; set; } + public int? FallbackMaxStreamingBitrate { get; set; } + public TransportStreamTimestamp? Timestamp { get; set; } public Dictionary RequiredHttpHeaders { get; set; } diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs index a832169c2..a355387b1 100644 --- a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs +++ b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs @@ -9,6 +9,9 @@ namespace MediaBrowser.Model.LiveTv { AllowHWTranscoding = true; IgnoreDts = true; + AllowStreamSharing = true; + AllowFmp4TranscodingContainer = false; + FallbackMaxStreamingBitrate = 30000000; } public string Id { get; set; } @@ -25,6 +28,12 @@ namespace MediaBrowser.Model.LiveTv public bool AllowHWTranscoding { get; set; } + public bool AllowFmp4TranscodingContainer { get; set; } + + public bool AllowStreamSharing { get; set; } + + public int FallbackMaxStreamingBitrate { get; set; } + public bool EnableStreamLooping { get; set; } public string Source { get; set; } diff --git a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index fef84dd00..e1f87a7bd 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -331,6 +331,8 @@ namespace Jellyfin.LiveTv.TunerHosts.HdHomerun SupportsTranscoding = true, IsInfiniteStream = true, IgnoreDts = true, + UseMostCompatibleTranscodingProfile = true, // All HDHR tuners require this + FallbackMaxStreamingBitrate = info.FallbackMaxStreamingBitrate, // IgnoreIndex = true, // ReadAtNativeFramerate = true }; diff --git a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs index 365f0188d..be81171a0 100644 --- a/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs +++ b/src/Jellyfin.LiveTv/TunerHosts/M3UTunerHost.cs @@ -94,7 +94,7 @@ namespace Jellyfin.LiveTv.TunerHosts var mediaSource = sources[0]; - if (mediaSource.Protocol == MediaProtocol.Http && !mediaSource.RequiresLooping) + if (tunerHost.AllowStreamSharing && mediaSource.Protocol == MediaProtocol.Http && !mediaSource.RequiresLooping) { var extension = Path.GetExtension(new UriBuilder(mediaSource.Path).Path); @@ -200,7 +200,9 @@ namespace Jellyfin.LiveTv.TunerHosts SupportsDirectPlay = supportsDirectPlay, SupportsDirectStream = supportsDirectStream, - RequiredHttpHeaders = httpHeaders + RequiredHttpHeaders = httpHeaders, + UseMostCompatibleTranscodingProfile = !info.AllowFmp4TranscodingContainer, + FallbackMaxStreamingBitrate = info.FallbackMaxStreamingBitrate }; mediaSource.InferTotalBitrate();