aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Api/Controllers/MediaInfoController.cs10
-rw-r--r--Jellyfin.Api/Helpers/MediaInfoHelper.cs183
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs16
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs5
-rw-r--r--MediaBrowser.Model/Dlna/AudioOptions.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs946
-rw-r--r--MediaBrowser.Model/Dlna/VideoOptions.cs2
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs2
-rw-r--r--MediaBrowser.Model/Properties/AssemblyInfo.cs1
-rw-r--r--MediaBrowser.Model/Session/TranscodeReason.cs19
-rw-r--r--MediaBrowser.Model/Session/TranscodeReasonExtensions.cs24
-rw-r--r--MediaBrowser.Model/Session/TranscodingInfo.cs2
-rw-r--r--tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs353
13 files changed, 801 insertions, 764 deletions
diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs
index b422eb78c..75df18204 100644
--- a/Jellyfin.Api/Controllers/MediaInfoController.cs
+++ b/Jellyfin.Api/Controllers/MediaInfoController.cs
@@ -126,7 +126,7 @@ namespace Jellyfin.Api.Controllers
var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false);
var profile = playbackInfoDto?.DeviceProfile;
- _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
+ _logger.LogDebug("GetPostedPlaybackInfo profile: {@Profile}", profile);
if (profile == null)
{
@@ -225,14 +225,6 @@ namespace Jellyfin.Api.Controllers
}
}
- if (info.MediaSources != null)
- {
- foreach (var mediaSource in info.MediaSources)
- {
- _mediaInfoHelper.NormalizeMediaSourceContainer(mediaSource, profile!, DlnaProfileType.Video);
- }
- }
-
return info;
}
diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
index 3b8dc7e31..1fb7c6707 100644
--- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs
+++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs
@@ -191,7 +191,9 @@ namespace Jellyfin.Api.Helpers
DeviceId = auth.DeviceId,
ItemId = item.Id,
Profile = profile,
- MaxAudioChannels = maxAudioChannels
+ MaxAudioChannels = maxAudioChannels,
+ AllowAudioStreamCopy = allowAudioStreamCopy,
+ AllowVideoStreamCopy = allowVideoStreamCopy
};
if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
@@ -208,7 +210,7 @@ namespace Jellyfin.Api.Helpers
mediaSource.SupportsDirectPlay = false;
}
- if (!enableDirectStream)
+ if (!enableDirectStream || !allowVideoStreamCopy)
{
mediaSource.SupportsDirectStream = false;
}
@@ -235,168 +237,79 @@ namespace Jellyfin.Api.Helpers
user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
- // Beginning of Playback Determination: Attempt DirectPlay first
- if (mediaSource.SupportsDirectPlay)
- {
- if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
- {
- mediaSource.SupportsDirectPlay = false;
- }
- else
- {
- var supportsDirectStream = mediaSource.SupportsDirectStream;
-
- // Dummy this up to fool StreamBuilder
- mediaSource.SupportsDirectStream = true;
- options.MaxBitrate = maxBitrate;
+ options.MaxBitrate = GetMaxBitrate(maxBitrate, user, ipAddress);
- if (item is Audio)
- {
- if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
- {
- options.ForceDirectPlay = true;
- }
- }
- else if (item is Video)
- {
- if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
- && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
- && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
- {
- options.ForceDirectPlay = true;
- }
- }
+ if (!options.ForceDirectStream)
+ {
+ // direct-stream http streaming is currently broken
+ options.EnableDirectStream = false;
+ }
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
- ? streamBuilder.BuildAudioItem(options)
- : streamBuilder.BuildVideoItem(options);
+ // Beginning of Playback Determination
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
+ ? streamBuilder.BuildAudioItem(options)
+ : streamBuilder.BuildVideoItem(options);
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectPlay = false;
- }
+ if (streamInfo != null)
+ {
+ streamInfo.PlaySessionId = playSessionId;
+ streamInfo.StartPositionTicks = startTimeTicks;
- // Set this back to what it was
- mediaSource.SupportsDirectStream = supportsDirectStream;
+ mediaSource.SupportsDirectPlay = streamInfo.PlayMethod == PlayMethod.DirectPlay;
+ // Players do not handle this being set according to PlayMethod
+ mediaSource.SupportsDirectStream = options.EnableDirectStream ? streamInfo.PlayMethod == PlayMethod.DirectPlay || streamInfo.PlayMethod == PlayMethod.DirectStream : streamInfo.PlayMethod == PlayMethod.DirectPlay;
+ mediaSource.SupportsTranscoding = streamInfo.PlayMethod == PlayMethod.DirectStream || mediaSource.TranscodingContainer != null;
- if (streamInfo != null)
+ if (item is Audio)
+ {
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex;
+ mediaSource.SupportsTranscoding = false;
}
}
- }
-
- if (mediaSource.SupportsDirectStream)
- {
- if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
+ else if (item is Video)
{
- mediaSource.SupportsDirectStream = false;
- }
- else
- {
- options.MaxBitrate = GetMaxBitrate(maxBitrate, user, ipAddress);
-
- if (item is Audio)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
- if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
- {
- options.ForceDirectStream = true;
- }
- }
- else if (item is Video)
- {
- if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
- && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
- && user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
- {
- options.ForceDirectStream = true;
- }
- }
-
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
- ? streamBuilder.BuildAudioItem(options)
- : streamBuilder.BuildVideoItem(options);
-
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectStream = false;
- }
-
- if (streamInfo != null)
- {
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex;
+ mediaSource.SupportsTranscoding = false;
}
}
- }
-
- if (mediaSource.SupportsTranscoding)
- {
- options.MaxBitrate = GetMaxBitrate(maxBitrate, user, ipAddress);
-
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
- ? streamBuilder.BuildAudioItem(options)
- : streamBuilder.BuildVideoItem(options);
if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
- if (streamInfo != null)
- {
- streamInfo.PlaySessionId = playSessionId;
- streamInfo.StartPositionTicks = startTimeTicks;
- mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
- mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
- mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
- mediaSource.TranscodingContainer = streamInfo.Container;
- mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
-
- // Do this after the above so that StartPositionTicks is set
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex;
- }
+ mediaSource.SupportsDirectPlay = false;
+ mediaSource.SupportsDirectStream = false;
+
+ mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
+ mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
+ mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
+ mediaSource.TranscodingContainer = streamInfo.Container;
+ mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
}
else
{
- if (streamInfo != null)
+ if (mediaSource.SupportsTranscoding || mediaSource.SupportsDirectStream)
{
- streamInfo.PlaySessionId = playSessionId;
+ streamInfo.PlayMethod = PlayMethod.Transcode;
+ mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
- if (streamInfo.PlayMethod == PlayMethod.Transcode)
+ if (!allowVideoStreamCopy)
{
- streamInfo.StartPositionTicks = startTimeTicks;
- mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
-
- if (!allowVideoStreamCopy)
- {
- mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
- }
-
- if (!allowAudioStreamCopy)
- {
- mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
- }
-
- mediaSource.TranscodingContainer = streamInfo.Container;
- mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
+ mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
}
if (!allowAudioStreamCopy)
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}
-
- mediaSource.TranscodingContainer = streamInfo.Container;
- mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
-
- // Do this after the above so that StartPositionTicks is set
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
- mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex;
}
}
+
+ // Do this after the above so that StartPositionTicks is set
+ SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex;
}
foreach (var attachment in mediaSource.MediaAttachments)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index f7248acac..b4aff60e1 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1798,7 +1798,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return false;
}
- return request.EnableAutoStreamCopy;
+ return true;
}
public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, IEnumerable<string> supportedAudioCodecs)
@@ -1855,17 +1855,11 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// Video bitrate must fall within requested value
- if (request.AudioBitRate.HasValue)
+ if (request.AudioBitRate.HasValue
+ && audioStream.BitDepth.HasValue
+ && audioStream.BitRate.Value > request.AudioBitRate.Value)
{
- if (!audioStream.BitRate.HasValue || audioStream.BitRate.Value <= 0)
- {
- return false;
- }
-
- if (audioStream.BitRate.Value > request.AudioBitRate.Value)
- {
- return false;
- }
+ return false;
}
return request.EnableAutoStreamCopy;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 0f5fdcc3c..23067c7b1 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.MediaEncoding
SupportedSubtitleCodecs = Array.Empty<string>();
}
- public TranscodeReason[] TranscodeReasons { get => TranscodeReason.ToArray(); }
+ public TranscodeReason[] TranscodeReasons => TranscodeReason.ToArray();
[JsonIgnore]
public TranscodeReason TranscodeReason
@@ -50,8 +50,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return TranscodeReason.None;
}
- TranscodeReason reason = TranscodeReason.None;
- Enum.TryParse<TranscodeReason>(BaseRequest.TranscodeReasons, out reason);
+ _ = Enum.TryParse<TranscodeReason>(BaseRequest.TranscodeReasons, out var reason);
_transcodeReasons = reason;
}
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
index 4d4d8d78c..33755e746 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/AudioOptions.cs
@@ -27,6 +27,8 @@ namespace MediaBrowser.Model.Dlna
public bool ForceDirectStream { get; set; }
+ public bool AllowAudioStreamCopy { get; set; }
+
public Guid ItemId { get; set; }
public MediaSourceInfo[] MediaSources { get; set; }
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 7654337e7..0f48e985d 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -15,6 +15,12 @@ namespace MediaBrowser.Model.Dlna
{
public class StreamBuilder
{
+ // Aliases
+ internal const TranscodeReason ContainerReasons = TranscodeReason.ContainerNotSupported | TranscodeReason.ContainerBitrateExceedsLimit;
+ internal const TranscodeReason AudioReasons = TranscodeReason.AudioCodecNotSupported | TranscodeReason.AudioBitrateNotSupported | TranscodeReason.AudioChannelsNotSupported | TranscodeReason.AudioProfileNotSupported | TranscodeReason.AudioSampleRateNotSupported | TranscodeReason.SecondaryAudioNotSupported | TranscodeReason.AudioBitDepthNotSupported | TranscodeReason.AudioIsExternal;
+ internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported;
+ internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported;
+
private readonly ILogger _logger;
private readonly ITranscoderSupport _transcoderSupport;
@@ -227,7 +233,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type)
+ public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type, DirectPlayProfile playProfile = null)
{
if (string.IsNullOrEmpty(inputContainer))
{
@@ -236,16 +242,12 @@ namespace MediaBrowser.Model.Dlna
var formats = ContainerProfile.SplitValue(inputContainer);
- if (formats.Length == 1)
- {
- return formats[0];
- }
-
if (profile != null)
{
+ var playProfiles = playProfile == null ? profile.DirectPlayProfiles : new[] { playProfile };
foreach (var format in formats)
{
- foreach (var directPlayProfile in profile.DirectPlayProfiles)
+ foreach (var directPlayProfile in playProfiles)
{
if (directPlayProfile.Type == type
&& directPlayProfile.SupportsContainer(format))
@@ -287,9 +289,9 @@ namespace MediaBrowser.Model.Dlna
var audioStream = item.GetDefaultAudioStream(null);
- var directPlayInfo = GetAudioDirectPlayMethods(item, audioStream, options);
+ var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
- var directPlayMethods = directPlayInfo.PlayMethods;
+ var directPlayMethod = directPlayInfo.PlayMethod;
var transcodeReasons = directPlayInfo.TranscodeReasons;
int? inputAudioChannels = audioStream?.Channels;
@@ -297,55 +299,17 @@ namespace MediaBrowser.Model.Dlna
int? inputAudioSampleRate = audioStream?.SampleRate;
int? inputAudioBitDepth = audioStream?.BitDepth;
- if (directPlayMethods.Any())
+ if (directPlayMethod.HasValue)
{
- string audioCodec = audioStream?.Codec;
-
- // Make sure audio codec profiles are satisfied
- var conditions = new List<ProfileCondition>();
- foreach (var i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.Audio && i.ContainsAnyCodec(audioCodec, item.Container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- if (!ConditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- conditions.AddRange(i.Conditions);
- }
- }
- }
+ var profile = options.Profile;
+ var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
+ var audioFailureReasons = AggregateFailureConditions(item, profile, "AudioCodecProfile", audioFailureConditions);
+ transcodeReasons |= audioFailureReasons;
- bool all = true;
- foreach (ProfileCondition c in conditions)
+ if (audioFailureReasons == TranscodeReason.None)
{
- if (!ConditionProcessor.IsAudioConditionSatisfied(c, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", c, item);
- transcodeReasons |= GetTranscodeReasonForFailedCondition(c);
-
- all = false;
- break;
- }
- }
-
- if (all)
- {
- if (directPlayMethods.Contains(PlayMethod.DirectStream))
- {
- playlistItem.PlayMethod = PlayMethod.DirectStream;
- }
-
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
+ playlistItem.PlayMethod = directPlayMethod.Value;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio, directPlayInfo.Profile);
return playlistItem;
}
@@ -370,45 +334,9 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- SetStreamInfoOptionsFromTranscodingProfile(playlistItem, transcodingProfile);
-
- var audioCodecProfiles = new List<CodecProfile>();
- foreach (var i in options.Profile.CodecProfiles)
- {
- if (i.Type == CodecType.Audio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container))
- {
- audioCodecProfiles.Add(i);
- }
-
- if (audioCodecProfiles.Count >= 1)
- {
- break;
- }
- }
-
- var audioTranscodingConditions = new List<ProfileCondition>();
- foreach (var i in audioCodecProfiles)
- {
- bool applyConditions = true;
- foreach (var applyCondition in i.ApplyConditions)
- {
- if (!ConditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth))
- {
- LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- audioTranscodingConditions.Add(c);
- }
- }
- }
+ SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
+ var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
// Honor requested max channels
@@ -444,9 +372,9 @@ namespace MediaBrowser.Model.Dlna
return options.GetMaxBitrate(isAudio);
}
- private (IEnumerable<PlayMethod> PlayMethods, TranscodeReason TranscodeReasons) GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
+ private (DirectPlayProfile Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
{
- DirectPlayProfile directPlayProfile = options.Profile.DirectPlayProfiles
+ var directPlayProfile = options.Profile.DirectPlayProfiles
.FirstOrDefault(x => x.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(x, item, audioStream));
if (directPlayProfile == null)
@@ -457,20 +385,21 @@ namespace MediaBrowser.Model.Dlna
item.Path ?? "Unknown path",
audioStream.Codec ?? "Unknown codec");
- return (Enumerable.Empty<PlayMethod>(), GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
+ return (null, null, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
}
var playMethods = new List<PlayMethod>();
var transcodeReasons = TranscodeReason.None;
- // While options takes the network and other factors into account. Only applies to direct stream
- if (item.SupportsDirectStream)
+ // The profile describes what the device supports
+ // If device requirements are satisfied then allow both direct stream and direct play
+ if (item.SupportsDirectPlay)
{
- if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
+ if (IsItemBitrateEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, PlayMethod.DirectPlay))
{
- if (options.EnableDirectStream)
+ if (options.EnableDirectPlay)
{
- playMethods.Add(PlayMethod.DirectStream);
+ return (directPlayProfile, PlayMethod.DirectPlay, TranscodeReason.None);
}
}
else
@@ -479,15 +408,14 @@ namespace MediaBrowser.Model.Dlna
}
}
- // The profile describes what the device supports
- // If device requirements are satisfied then allow both direct stream and direct play
- if (item.SupportsDirectPlay)
+ // While options takes the network and other factors into account. Only applies to direct stream
+ if (item.SupportsDirectStream)
{
- if (IsItemBitrateEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, PlayMethod.DirectPlay))
+ if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
{
- if (options.EnableDirectPlay)
+ if (options.EnableDirectStream)
{
- playMethods.Add(PlayMethod.DirectPlay);
+ return (directPlayProfile, PlayMethod.DirectStream, transcodeReasons);
}
}
else
@@ -496,12 +424,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (playMethods.Count > 0)
- {
- transcodeReasons = TranscodeReason.None;
- }
-
- return (playMethods, transcodeReasons);
+ return (directPlayProfile, null, transcodeReasons);
}
private static TranscodeReason GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<DirectPlayProfile> directPlayProfiles)
@@ -592,30 +515,29 @@ namespace MediaBrowser.Model.Dlna
return item.DefaultSubtitleStreamIndex;
}
- private static void SetStreamInfoOptionsFromTranscodingProfile(StreamInfo playlistItem, TranscodingProfile transcodingProfile)
+ private static void SetStreamInfoOptionsFromTranscodingProfile(MediaSourceInfo item, StreamInfo playlistItem, TranscodingProfile transcodingProfile)
{
- if (string.IsNullOrEmpty(transcodingProfile.AudioCodec))
- {
- playlistItem.AudioCodecs = Array.Empty<string>();
- }
- else
- {
- playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(',');
- }
+ var container = transcodingProfile.Container;
+ var protocol = transcodingProfile.Protocol;
- playlistItem.Container = transcodingProfile.Container;
- playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
- playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
+ item.TranscodingContainer = container;
+ item.TranscodingSubProtocol = protocol;
- if (string.IsNullOrEmpty(transcodingProfile.VideoCodec))
+ if (playlistItem.PlayMethod == PlayMethod.Transcode)
{
- playlistItem.VideoCodecs = Array.Empty<string>();
+ playlistItem.Container = container;
+ playlistItem.SubProtocol = protocol;
}
- else
+
+ playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
+ if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)
+ && int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels))
{
- playlistItem.VideoCodecs = transcodingProfile.VideoCodec.Split(',');
+ playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels;
}
+ playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
+
playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
playlistItem.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
@@ -631,14 +553,21 @@ namespace MediaBrowser.Model.Dlna
{
playlistItem.SegmentLength = transcodingProfile.SegmentLength;
}
+ }
- playlistItem.SubProtocol = transcodingProfile.Protocol;
+ private static void SetStreamInfoOptionsFromDirectPlayProfile(VideoOptions options, MediaSourceInfo item, StreamInfo playlistItem, DirectPlayProfile directPlayProfile)
+ {
+ var container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
+ var protocol = "http";
- if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)
- && int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels))
- {
- playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels;
- }
+ item.TranscodingContainer = container;
+ item.TranscodingSubProtocol = protocol;
+
+ playlistItem.Container = container;
+ playlistItem.SubProtocol = protocol;
+
+ playlistItem.VideoCodecs = new[] { item.VideoStream.Codec };
+ playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile.AudioCodec);
}
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
@@ -667,11 +596,27 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioStreamIndex = audioStream.Index;
}
+ // Collect candidate audio streams
+ IEnumerable<MediaStream> candidateAudioStreams = audioStream == null ? Array.Empty<MediaStream>() : new[] { audioStream };
+ if (!options.AudioStreamIndex.HasValue || options.AudioStreamIndex < 0)
+ {
+ if (audioStream?.IsDefault == true)
+ {
+ candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.IsDefault);
+ }
+ else
+ {
+ candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.Language == audioStream?.Language);
+ }
+ }
+
+ candidateAudioStreams = candidateAudioStreams.ToArray();
+
var videoStream = item.VideoStream;
// TODO: This doesn't account for situations where the device is able to handle the media's bitrate, but the connection isn't fast enough
- var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, subtitleStream, audioStream, options, PlayMethod.DirectPlay);
- var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, subtitleStream, audioStream, options, PlayMethod.DirectStream);
+ var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, options, PlayMethod.DirectPlay);
+ var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult == TranscodeReason.None);
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directPlayEligibilityResult == TranscodeReason.None);
var transcodeReasons = directPlayEligibilityResult | directStreamEligibilityResult;
@@ -683,183 +628,292 @@ namespace MediaBrowser.Model.Dlna
isEligibleForDirectPlay,
isEligibleForDirectStream);
+ DirectPlayProfile directPlayProfile = null;
if (isEligibleForDirectPlay || isEligibleForDirectStream)
{
// See if it can be direct played
- var directPlayInfo = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectStream);
+ var directPlayInfo = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, candidateAudioStreams, subtitleStream, isEligibleForDirectPlay, isEligibleForDirectStream);
var directPlay = directPlayInfo.PlayMethod;
+ transcodeReasons |= directPlayInfo.TranscodeReasons;
if (directPlay != null)
{
+ directPlayProfile = directPlayInfo.Profile;
playlistItem.PlayMethod = directPlay.Value;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video);
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
+ playlistItem.VideoCodecs = new[] { videoStream.Codec };
+
+ if (directPlay == PlayMethod.DirectPlay)
+ {
+ playlistItem.SubProtocol = "http";
+
+ var audioStreamIndex = directPlayInfo.AudioStreamIndex ?? audioStream?.Index;
+ if (audioStreamIndex.HasValue)
+ {
+ playlistItem.AudioStreamIndex = audioStreamIndex;
+ playlistItem.AudioCodecs = new[] { item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value)?.Codec };
+ }
+ }
+ else if (directPlay == PlayMethod.DirectStream)
+ {
+ playlistItem.AudioStreamIndex = audioStream?.Index;
+ if (audioStream != null)
+ {
+ playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile.AudioCodec);
+ }
+
+ SetStreamInfoOptionsFromDirectPlayProfile(options, item, playlistItem, directPlayProfile);
+ BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, directPlayProfile.Container, directPlayProfile.VideoCodec, directPlayProfile.AudioCodec);
+ }
if (subtitleStream != null)
{
- var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, item.Container, null);
+ var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, directPlayProfile.Container, null);
playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
playlistItem.SubtitleFormat = subtitleProfile.Format;
}
-
- return playlistItem;
}
- transcodeReasons |= directPlayInfo.TranscodeReasons;
+ _logger.LogInformation(
+ "DirectPlay Result for Profile: {0}, Path: {1}, PlayMethod: {2}, AudioStreamIndex: {3}, SubtitleStreamIndex: {4}, Reasons: {5}",
+ options.Profile.Name ?? "Anonymous Profile",
+ item.Path ?? "Unknown path",
+ directPlayInfo.PlayMethod,
+ directPlayInfo.AudioStreamIndex ?? audioStream?.Index,
+ playlistItem.SubtitleStreamIndex,
+ directPlayInfo.TranscodeReasons);
}
- if (playlistItem.PlayMethod != PlayMethod.Transcode)
+ playlistItem.TranscodeReasons = transcodeReasons;
+
+ if (playlistItem.PlayMethod != PlayMethod.DirectStream || !options.EnableDirectStream)
{
- playlistItem.TranscodeReasons = transcodeReasons;
- return playlistItem;
+ // Can't direct play, find the transcoding profile
+ // If we do this for direct-stream we will overwrite the info
+ var transcodingProfile = GetVideoTranscodeProfile(item, options, videoStream, audioStream, candidateAudioStreams, subtitleStream, playlistItem);
+ if (transcodingProfile != null)
+ {
+ SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
+
+ BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, transcodingProfile.Container, transcodingProfile.VideoCodec, transcodingProfile.AudioCodec);
+
+ if (subtitleStream != null)
+ {
+ var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
+
+ playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
+ playlistItem.SubtitleFormat = subtitleProfile.Format;
+ playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
+ }
+
+ if (playlistItem.PlayMethod != PlayMethod.DirectPlay)
+ {
+ playlistItem.PlayMethod = PlayMethod.Transcode;
+ }
+ }
}
- // Can't direct play, find the transcoding profile
- TranscodingProfile transcodingProfile = null;
- foreach (var i in options.Profile.TranscodingProfiles)
+ _logger.LogInformation(
+ "StreamBuilder.BuildVideoItem( Profile={0}, Path={1}, AudioStreamIndex={2}, SubtitleStreamIndex={3} ) => ( PlayMethod={4}, TranscodeReason={5} ) {6}",
+ options.Profile.Name ?? "Anonymous Profile",
+ item.Path ?? "Unknown path",
+ options.AudioStreamIndex,
+ options.SubtitleStreamIndex,
+ playlistItem.PlayMethod,
+ playlistItem.TranscodeReasons,
+ playlistItem.ToUrl("media:", "<token>"));
+
+ item.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
+ return playlistItem;
+ }
+
+ private TranscodingProfile GetVideoTranscodeProfile(MediaSourceInfo item, VideoOptions options, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, MediaStream subtitleStream, StreamInfo playlistItem)
+ {
+ if (!(item.SupportsTranscoding || item.SupportsDirectStream))
+ {
+ return null;
+ }
+
+ var transcodingProfiles = options.Profile.TranscodingProfiles
+ .Where(i => i.Type == playlistItem.MediaType && i.Context == options.Context);
+
+ if (options.AllowVideoStreamCopy)
{
- if (i.Type == playlistItem.MediaType && i.Context == options.Context)
+ // prefer direct copy profile
+ float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
+ TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
+ int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
+ int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
+
+ transcodingProfiles = transcodingProfiles.ToLookup(transcodingProfile =>
{
- transcodingProfile = i;
- break;
- }
+ var videoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
+ var match = ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream.Codec) &&
+ options.Profile.CodecProfiles
+ .Any(i => i.Type == CodecType.Video &&
+ i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container) &&
+ i.ApplyConditions.Any(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
+
+ return match ? 1 : 2;
+ })
+ .OrderBy(lookup => lookup.Key)
+ .SelectMany(lookup => lookup);
}
- if (transcodingProfile != null)
+ return transcodingProfiles.FirstOrDefault();
+ }
+
+ private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
+ {
+ // prefer matching video codecs
+ var videoCodecs = ContainerProfile.SplitValue(videoCodec);
+ var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream.Codec) ? videoStream.Codec : null;
+ playlistItem.VideoCodecs = directVideoCodec != null ? new[] { directVideoCodec } : videoCodecs;
+
+ // copy video codec options as a starting point, this applies to transcode and direct-stream
+ playlistItem.MaxFramerate = videoStream.AverageFrameRate;
+ var qualifier = videoStream.Codec;
+ if (videoStream.Level.HasValue)
{
- if (!item.SupportsTranscoding)
+ playlistItem.SetOption(qualifier, "level", videoStream.Level.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (videoStream.BitDepth.HasValue)
+ {
+ playlistItem.SetOption(qualifier, "videobitdepth", videoStream.BitDepth.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (!string.IsNullOrEmpty(videoStream.Profile))
+ {
+ playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant());
+ }
+
+ if (videoStream.Level != 0)
+ {
+ playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
+ }
+
+ // prefer matching audio codecs, could do beter here
+ var audioCodecs = ContainerProfile.SplitValue(audioCodec);
+ var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
+ playlistItem.AudioCodecs = audioCodecs;
+ if (directAudioStream != null)
+ {
+ audioStream = directAudioStream;
+ playlistItem.AudioStreamIndex = audioStream.Index;
+ playlistItem.AudioCodecs = new[] { audioStream.Codec };
+
+ // copy matching audio codec options
+ playlistItem.AudioSampleRate = audioStream.SampleRate;
+ playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString());
+
+ if (!string.IsNullOrEmpty(audioStream.Profile))
{
- return null;
+ playlistItem.SetOption(audioStream.Codec, "profile", audioStream.Profile.ToLowerInvariant());
}
- if (subtitleStream != null)
+ if (audioStream.Level != 0)
{
- var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
-
- playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
- playlistItem.SubtitleFormat = subtitleProfile.Format;
- playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
+ playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString());
}
+ }
- playlistItem.PlayMethod = PlayMethod.Transcode;
+ int? width = videoStream?.Width;
+ int? height = videoStream?.Height;
+ int? bitDepth = videoStream?.BitDepth;
+ int? videoBitrate = videoStream?.BitRate;
+ double? videoLevel = videoStream?.Level;
+ string videoProfile = videoStream?.Profile;
+ float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
+ bool? isAnamorphic = videoStream?.IsAnamorphic;
+ bool? isInterlaced = videoStream?.IsInterlaced;
+ string videoCodecTag = videoStream?.CodecTag;
+ bool? isAvc = videoStream?.IsAVC;
+
+ TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
+ int? packetLength = videoStream?.PacketLength;
+ int? refFrames = videoStream?.RefFrames;
- SetStreamInfoOptionsFromTranscodingProfile(playlistItem, transcodingProfile);
+ int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
+ int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
- var isFirstAppliedCodecProfile = true;
- foreach (var i in options.Profile.CodecProfiles)
+ var appliedVideoConditions = options.Profile.CodecProfiles
+ .Where(i => i.Type == CodecType.Video &&
+ i.ContainsAnyCodec(videoCodec, container) &&
+ i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
+ var isFirstAppliedCodecProfile = true;
+ foreach (var i in appliedVideoConditions)
+ {
+ var transcodingVideoCodecs = ContainerProfile.SplitValue(videoCodec);
+ foreach (var transcodingVideoCodec in transcodingVideoCodecs)
{
- if (i.Type == CodecType.Video && i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
+ if (i.ContainsAnyCodec(transcodingVideoCodec, container))
{
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- int? width = videoStream?.Width;
- int? height = videoStream?.Height;
- int? bitDepth = videoStream?.BitDepth;
- int? videoBitrate = videoStream?.BitRate;
- double? videoLevel = videoStream?.Level;
- string videoProfile = videoStream?.Profile;
- float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
- bool? isAnamorphic = videoStream?.IsAnamorphic;
- bool? isInterlaced = videoStream?.IsInterlaced;
- string videoCodecTag = videoStream?.CodecTag;
- bool? isAvc = videoStream?.IsAVC;
-
- TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
- int? packetLength = videoStream?.PacketLength;
- int? refFrames = videoStream?.RefFrames;
-
- int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
- int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
-
- if (!ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- // LogConditionFailure(options.Profile, "VideoCodecProfile.ApplyConditions", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- var transcodingVideoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
- foreach (var transcodingVideoCodec in transcodingVideoCodecs)
- {
- if (i.ContainsAnyCodec(transcodingVideoCodec, transcodingProfile.Container))
- {
- ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, true, isFirstAppliedCodecProfile);
- isFirstAppliedCodecProfile = false;
- }
- }
- }
+ ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, true, isFirstAppliedCodecProfile);
+ isFirstAppliedCodecProfile = false;
+ continue;
}
}
+ }
- // Honor requested max channels
- playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
+ // Honor requested max channels
+ playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
- int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(false) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
- playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
+ int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(false) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
+ playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
- isFirstAppliedCodecProfile = true;
- foreach (var i in options.Profile.CodecProfiles)
+ bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
+ int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
+ int? audioChannels = audioStream == null ? null : audioStream.Channels;
+ string audioProfile = audioStream == null ? null : audioStream.Profile;
+ int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
+ int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
+
+ var appliedAudioConditions = options.Profile.CodecProfiles
+ .Where(i => i.Type == CodecType.Video &&
+ i.ContainsAnyCodec(audioCodec, container) &&
+ i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
+ isFirstAppliedCodecProfile = true;
+ foreach (var i in appliedAudioConditions)
+ {
+ var transcodingAudioCodecs = ContainerProfile.SplitValue(audioCodec);
+ foreach (var transcodingAudioCodec in transcodingAudioCodecs)
{
- if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container))
+ if (i.ContainsAnyCodec(transcodingAudioCodec, container))
{
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
- {
- bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
- int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
- int? audioChannels = audioStream == null ? null : audioStream.Channels;
- string audioProfile = audioStream == null ? null : audioStream.Profile;
- int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
- int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
-
- if (!ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
- {
- // LogConditionFailure(options.Profile, "VideoCodecProfile.ApplyConditions", applyCondition, item);
- applyConditions = false;
- break;
- }
- }
-
- if (applyConditions)
- {
- var transcodingAudioCodecs = ContainerProfile.SplitValue(transcodingProfile.AudioCodec);
- foreach (var transcodingAudioCodec in transcodingAudioCodecs)
- {
- if (i.ContainsAnyCodec(transcodingAudioCodec, transcodingProfile.Container))
- {
- ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
- isFirstAppliedCodecProfile = false;
- }
- }
- }
+ ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
+ isFirstAppliedCodecProfile = false;
+ break;
}
}
+ }
- var maxBitrateSetting = options.GetMaxBitrate(false);
- // Honor max rate
- if (maxBitrateSetting.HasValue)
- {
- var availableBitrateForVideo = maxBitrateSetting.Value;
-
- if (playlistItem.AudioBitrate.HasValue)
- {
- availableBitrateForVideo -= playlistItem.AudioBitrate.Value;
- }
+ var maxBitrateSetting = options.GetMaxBitrate(false);
+ // Honor max rate
+ if (maxBitrateSetting.HasValue)
+ {
+ var availableBitrateForVideo = maxBitrateSetting.Value;
- // Make sure the video bitrate is lower than bitrate settings but at least 64k
- long currentValue = playlistItem.VideoBitrate ?? availableBitrateForVideo;
- var longBitrate = Math.Max(Math.Min(availableBitrateForVideo, currentValue), 64000);
- playlistItem.VideoBitrate = longBitrate >= int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
+ if (playlistItem.AudioBitrate.HasValue)
+ {
+ availableBitrateForVideo -= playlistItem.AudioBitrate.Value;
}
- }
- playlistItem.TranscodeReasons = transcodeReasons;
+ // Make sure the video bitrate is lower than bitrate settings but at least 64k
+ long currentValue = playlistItem.VideoBitrate ?? availableBitrateForVideo;
+ var longBitrate = Math.Max(Math.Min(availableBitrateForVideo, currentValue), 64000);
+ playlistItem.VideoBitrate = longBitrate >= int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
+ }
- return playlistItem;
+ _logger.LogInformation(
+ "Transcode Result for Profile: {0}, Path: {1}, PlayMethod: {2}, AudioStreamIndex: {3}, SubtitleStreamIndex: {4}, Reasons: {5}",
+ options.Profile.Name ?? "Anonymous Profile",
+ item.Path ?? "Unknown path",
+ playlistItem.PlayMethod,
+ audioStream.Index,
+ playlistItem.SubtitleStreamIndex,
+ playlistItem.TranscodeReasons);
}
private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels)
@@ -988,63 +1042,30 @@ namespace MediaBrowser.Model.Dlna
return 7168000;
}
- private (PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
+ private (DirectPlayProfile Profile, PlayMethod? PlayMethod, int? AudioStreamIndex, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
VideoOptions options,
MediaSourceInfo mediaSource,
MediaStream videoStream,
MediaStream audioStream,
+ IEnumerable<MediaStream> candidateAudioStreams,
+ MediaStream subtitleStream,
+ bool isEligibleForDirectPlay,
bool isEligibleForDirectStream)
{
if (options.ForceDirectPlay)
{
- return (PlayMethod.DirectPlay, TranscodeReason.None);
+ return (null, PlayMethod.DirectPlay, audioStream?.Index, TranscodeReason.None);
}
if (options.ForceDirectStream)
{
- return (PlayMethod.DirectStream, TranscodeReason.None);
+ return (null, PlayMethod.DirectStream, audioStream?.Index, TranscodeReason.None);
}
DeviceProfile profile = options.Profile;
string container = mediaSource.Container;
- // See if it can be direct played
- DirectPlayProfile directPlay = null;
- foreach (var p in profile.DirectPlayProfiles)
- {
- if (p.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(p, container, videoStream, audioStream))
- {
- directPlay = p;
- break;
- }
- }
-
- if (directPlay == null)
- {
- _logger.LogDebug(
- "Container: {Container}, Video: {Video}, Audio: {Audio} cannot be direct played by profile: {Profile} for path: {Path}",
- container,
- videoStream?.Codec ?? "no video",
- audioStream?.Codec ?? "no audio",
- profile.Name ?? "unknown profile",
- mediaSource.Path ?? "unknown path");
-
- return (null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles));
- }
-
- var conditions = new List<ProfileCondition>();
- foreach (var p in profile.ContainerProfiles)
- {
- if (p.Type == DlnaProfileType.Video
- && p.ContainsContainer(container))
- {
- foreach (var c in p.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
-
+ // video
int? width = videoStream?.Width;
int? height = videoStream?.Height;
int? bitDepth = videoStream?.BitDepth;
@@ -1056,12 +1077,9 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced;
string videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
-
- int? audioBitrate = audioStream?.BitRate;
- int? audioChannels = audioStream?.Channels;
- string audioProfile = audioStream?.Profile;
- int? audioSampleRate = audioStream?.SampleRate;
- int? audioBitDepth = audioStream?.BitDepth;
+ // audio
+ var defaultLanguage = audioStream?.Language ?? string.Empty;
+ var defaultMarked = audioStream?.IsDefault ?? false;
TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
int? packetLength = videoStream?.PacketLength;
@@ -1070,106 +1088,165 @@ namespace MediaBrowser.Model.Dlna
int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
- // Check container conditions
- foreach (ProfileCondition i in conditions)
- {
- if (!ConditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource);
-
- var transcodeReasons = GetTranscodeReasonForFailedCondition(i);
- return (null, transcodeReasons);
- }
- }
-
- string videoCodec = videoStream?.Codec;
+ var checkVideoConditions = (ProfileCondition[] conditions) =>
+ conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
- conditions = new List<ProfileCondition>();
- foreach (var i in profile.CodecProfiles)
- {
- if (i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container))
- {
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
+ // Check container conditions
+ var containerProfileReasons = AggregateFailureConditions(
+ mediaSource,
+ profile,
+ "VideoCodecProfile",
+ profile.ContainerProfiles
+ .Where(containerProfile => containerProfile.Type == DlnaProfileType.Video && containerProfile.ContainsContainer(container))
+ .SelectMany(containerProfile => checkVideoConditions(containerProfile.Conditions)));
+
+ // Check video conditions
+ var videoCodecProfileReasons = AggregateFailureConditions(
+ mediaSource,
+ profile,
+ "VideoCodecProfile",
+ profile.CodecProfiles
+ .Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container))
+ .SelectMany(codecProfile =>
{
- if (!ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
+ var failedApplyConditions = checkVideoConditions(codecProfile.ApplyConditions);
+ if (!failedApplyConditions.Any())
{
- // LogConditionFailure(profile, "VideoCodecProfile.ApplyConditions", applyCondition, mediaSource);
- applyConditions = false;
- break;
+ return Array.Empty<ProfileCondition>();
}
- }
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
- }
- }
+ var failedConditions = checkVideoConditions(codecProfile.Conditions);
+ return failedApplyConditions.Concat(failedConditions);
+ }));
+
+ // Check audiocandidates profile conditions
+ var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream, defaultLanguage, defaultMarked));
- foreach (ProfileCondition i in conditions)
+ TranscodeReason subtitleProfileReasons = TranscodeReason.None;
+ if (subtitleStream != null)
{
- if (!ConditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
- {
- LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource);
+ var subtitleProfile = GetSubtitleProfile(mediaSource, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.DirectPlay, _transcoderSupport, container, null);
- var transcodeReason = GetTranscodeReasonForFailedCondition(i);
- return (null, transcodeReason);
+ if (subtitleProfile.Method != SubtitleDeliveryMethod.Drop
+ && subtitleProfile.Method != SubtitleDeliveryMethod.External
+ && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
+ {
+ _logger.LogDebug("Not eligible for {0} due to unsupported subtitles", PlayMethod.DirectPlay);
+ subtitleProfileReasons |= TranscodeReason.SubtitleCodecNotSupported;
}
}
- if (audioStream != null)
- {
- string audioCodec = audioStream.Codec;
- conditions = new List<ProfileCondition>();
- bool? isSecondaryAudio = mediaSource.IsSecondaryAudio(audioStream);
-
- foreach (var i in profile.CodecProfiles)
+ var rankings = new[] { VideoReasons, AudioReasons, ContainerReasons };
+ var rank = (ref TranscodeReason a) =>
{
- if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(audioCodec, container))
+ var index = 1;
+ foreach (var flag in rankings)
{
- bool applyConditions = true;
- foreach (ProfileCondition applyCondition in i.ApplyConditions)
+ var reason = a & flag;
+ if (reason != TranscodeReason.None)
{
- if (!ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio))
- {
- // LogConditionFailure(profile, "VideoAudioCodecProfile.ApplyConditions", applyCondition, mediaSource);
- applyConditions = false;
- break;
- }
+ a = reason;
+ return index;
}
- if (applyConditions)
- {
- foreach (ProfileCondition c in i.Conditions)
- {
- conditions.Add(c);
- }
- }
+ index++;
}
- }
- foreach (ProfileCondition i in conditions)
+ return index;
+ };
+
+ // Check DirectPlay profiles to see if it can be direct played
+ var analyzedProfiles = profile.DirectPlayProfiles
+ .Where(directPlayProfile => directPlayProfile.Type == DlnaProfileType.Video)
+ .Select((directPlayProfile, order) =>
{
- if (!ConditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio))
+ var directPlayProfileReasons = TranscodeReason.None;
+ var audioCodecProfileReasons = TranscodeReason.None;
+
+ // Check container type
+ if (!directPlayProfile.SupportsContainer(container))
{
- LogConditionFailure(profile, "VideoAudioCodecProfile", i, mediaSource);
+ directPlayProfileReasons |= TranscodeReason.ContainerNotSupported;
+ }
- var transcodeReasons = GetTranscodeReasonForFailedCondition(i);
- return (null, transcodeReasons);
+ // Check video codec
+ string videoCodec = videoStream?.Codec;
+ if (!directPlayProfile.SupportsVideoCodec(videoCodec))
+ {
+ directPlayProfileReasons |= TranscodeReason.VideoCodecNotSupported;
}
- }
+
+ // Check audio codec
+ var selectedAudioStream = candidateAudioStreams.FirstOrDefault(audioStream => directPlayProfile.SupportsAudioCodec(audioStream.Codec));
+ if (selectedAudioStream == null)
+ {
+ directPlayProfileReasons |= TranscodeReason.AudioCodecNotSupported;
+ }
+ else
+ {
+ audioCodecProfileReasons = audioStreamMatches.GetValueOrDefault(selectedAudioStream);
+ }
+
+ var failureReasons = directPlayProfileReasons | containerProfileReasons | videoCodecProfileReasons | audioCodecProfileReasons | subtitleProfileReasons;
+ var directStreamFailureReasons = failureReasons & (~DirectStreamReasons);
+
+ PlayMethod? playMethod = null;
+ if (failureReasons == TranscodeReason.None && isEligibleForDirectPlay && mediaSource.SupportsDirectPlay)
+ {
+ playMethod = PlayMethod.DirectPlay;
+ }
+ else if (directStreamFailureReasons == TranscodeReason.None && isEligibleForDirectStream && mediaSource.SupportsDirectStream && directPlayProfile != null)
+ {
+ playMethod = PlayMethod.DirectStream;
+ }
+
+ var ranked = rank(ref failureReasons);
+ return (Result: (Profile: directPlayProfile, PlayMethod: playMethod, AudioStreamIndex: selectedAudioStream?.Index, TranscodeReason: failureReasons), Order: order, Rank: ranked);
+ })
+ .OrderByDescending(analysis => analysis.Result.PlayMethod)
+ .ThenBy(analysis => analysis.Order)
+ .ToArray()
+ .ToLookup(analysis => analysis.Result.PlayMethod != null);
+
+ var profileMatch = analyzedProfiles[true]
+ .Select(analysis => analysis.Result)
+ .FirstOrDefault();
+ if (profileMatch.Profile != null)
+ {
+ return profileMatch;
}
- if (isEligibleForDirectStream && mediaSource.SupportsDirectStream)
+ var failureReasons = analyzedProfiles[false].OrderBy(a => a.Result.TranscodeReason).ThenBy(analysis => analysis.Order).FirstOrDefault().Result.TranscodeReason;
+ if (failureReasons == TranscodeReason.None)
{
- return (PlayMethod.DirectStream, TranscodeReason.None);
+ failureReasons = TranscodeReason.DirectPlayError;
}
- return (null, TranscodeReason.ContainerBitrateExceedsLimit);
+ return (Profile: null, PlayMethod: null, AudioStreamIndex: null, TranscodeReasons: failureReasons);
+ }
+
+ private TranscodeReason CheckVideoAudioStreamDirectPlay(VideoOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream, string language, bool isDefault)
+ {
+ var profile = options.Profile;
+ var audioFailureConditions = GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioStream.Codec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, !audioStream.IsDefault);
+
+ var audioStreamFailureReasons = AggregateFailureConditions(mediaSource, profile, "VideoAudioCodecProfile", audioFailureConditions);
+ if (audioStream?.IsExternal == true)
+ {
+ audioStreamFailureReasons |= TranscodeReason.AudioIsExternal;
+ }
+
+ return audioStreamFailureReasons;
+ }
+
+ private TranscodeReason AggregateFailureConditions(MediaSourceInfo mediaSource, DeviceProfile profile, string type, IEnumerable<ProfileCondition> conditions)
+ {
+ return conditions.Aggregate(TranscodeReason.None, (reasons, i) =>
+ {
+ LogConditionFailure(profile, type, i, mediaSource);
+ var transcodeReasons = GetTranscodeReasonForFailedCondition(i);
+ return reasons | transcodeReasons;
+ });
}
private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource)
@@ -1188,38 +1265,18 @@ namespace MediaBrowser.Model.Dlna
private TranscodeReason IsEligibleForDirectPlay(
MediaSourceInfo item,
long maxBitrate,
- MediaStream subtitleStream,
- MediaStream audioStream,
VideoOptions options,
PlayMethod playMethod)
{
- var reason = TranscodeReason.None;
- if (subtitleStream != null)
- {
- var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, item.Container, null);
-
- if (subtitleProfile.Method != SubtitleDeliveryMethod.Drop
- && subtitleProfile.Method != SubtitleDeliveryMethod.External
- && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
- {
- _logger.LogDebug("Not eligible for {0} due to unsupported subtitles", playMethod);
- reason |= TranscodeReason.SubtitleCodecNotSupported;
- }
- }
-
bool result = IsItemBitrateEligibleForDirectPlay(item, maxBitrate, playMethod);
if (!result)
{
- reason |= TranscodeReason.ContainerBitrateExceedsLimit;
+ return TranscodeReason.ContainerBitrateExceedsLimit;
}
-
- // TODO:6450 support external audio in DirectStream?
- if (audioStream?.IsExternal == true)
+ else
{
- reason |= TranscodeReason.AudioIsExternal;
+ return TranscodeReason.None;
}
-
- return reason;
}
public static SubtitleProfile GetSubtitleProfile(
@@ -1443,6 +1500,47 @@ namespace MediaBrowser.Model.Dlna
}
}
+ private static IEnumerable<ProfileCondition> GetProfileConditionsForVideoAudio(
+ IEnumerable<CodecProfile> codecProfiles,
+ string container,
+ string codec,
+ int? audioChannels,
+ int? audioBitrate,
+ int? audioSampleRate,
+ int? audioBitDepth,
+ string audioProfile,
+ bool? isSecondaryAudio)
+ {
+ return codecProfiles
+ .Where(profile => profile.Type == CodecType.VideoAudio && profile.ContainsAnyCodec(codec, container) &&
+ profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)))
+ .SelectMany(profile => profile.Conditions)
+ .Where(condition => !ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio));
+ }
+
+ private static IEnumerable<ProfileCondition> GetProfileConditionsForAudio(
+ IEnumerable<CodecProfile> codecProfiles,
+ string container,
+ string codec,
+ int? audioChannels,
+ int? audioBitrate,
+ int? audioSampleRate,
+ int? audioBitDepth,
+ bool checkConditions)
+ {
+ var conditions = codecProfiles
+ .Where(profile => profile.Type == CodecType.Audio && profile.ContainsAnyCodec(codec, container) &&
+ profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth)))
+ .SelectMany(profile => profile.Conditions);
+
+ if (!checkConditions)
+ {
+ return conditions;
+ }
+
+ return conditions.Where(condition => !ConditionProcessor.IsAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth));
+ }
+
private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool enableQualifiedConditions, bool enableNonQualifiedConditions)
{
foreach (ProfileCondition condition in conditions)
@@ -1722,10 +1820,22 @@ namespace MediaBrowser.Model.Dlna
var values = value
.Split('|', StringSplitOptions.RemoveEmptyEntries);
- if (condition.Condition == ProfileConditionType.Equals || condition.Condition == ProfileConditionType.EqualsAny)
+ if (condition.Condition == ProfileConditionType.Equals)
{
item.SetOption(qualifier, "profile", string.Join(',', values));
}
+ else if (condition.Condition == ProfileConditionType.EqualsAny)
+ {
+ var currentValue = item.GetOption(qualifier, "profile");
+ if (!string.IsNullOrEmpty(currentValue) && values.Any(value => value == currentValue))
+ {
+ item.SetOption(qualifier, "profile", currentValue);
+ }
+ else
+ {
+ item.SetOption(qualifier, "profile", string.Join(',', values));
+ }
+ }
break;
}
@@ -1883,29 +1993,5 @@ namespace MediaBrowser.Model.Dlna
return true;
}
-
- private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, string container, MediaStream videoStream, MediaStream audioStream)
- {
- // Check container type
- if (!profile.SupportsContainer(container))
- {
- return false;
- }
-
- // Check video codec
- string videoCodec = videoStream?.Codec;
- if (!profile.SupportsVideoCodec(videoCodec))
- {
- return false;
- }
-
- // Check audio codec
- if (audioStream != null && !profile.SupportsAudioCodec(audioStream.Codec))
- {
- return false;
- }
-
- return true;
- }
}
}
diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs
index 4194f17c6..0cb80af54 100644
--- a/MediaBrowser.Model/Dlna/VideoOptions.cs
+++ b/MediaBrowser.Model/Dlna/VideoOptions.cs
@@ -10,5 +10,7 @@ namespace MediaBrowser.Model.Dlna
public int? AudioStreamIndex { get; set; }
public int? SubtitleStreamIndex { get; set; }
+
+ public bool AllowVideoStreamCopy { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 2281e6ae5..bb9848848 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -161,7 +161,7 @@ namespace MediaBrowser.Model.Dto
public MediaStream GetDefaultAudioStream(int? defaultIndex)
{
- if (defaultIndex.HasValue)
+ if (defaultIndex.HasValue && defaultIndex != -1)
{
var val = defaultIndex.Value;
diff --git a/MediaBrowser.Model/Properties/AssemblyInfo.cs b/MediaBrowser.Model/Properties/AssemblyInfo.cs
index e50baf604..6bf1eb0c0 100644
--- a/MediaBrowser.Model/Properties/AssemblyInfo.cs
+++ b/MediaBrowser.Model/Properties/AssemblyInfo.cs
@@ -16,6 +16,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: InternalsVisibleTo("Jellyfin.Model.Tests")]
+[assembly: InternalsVisibleTo("Jellyfin.Dlna.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs
index c3570840f..c99136384 100644
--- a/MediaBrowser.Model/Session/TranscodeReason.cs
+++ b/MediaBrowser.Model/Session/TranscodeReason.cs
@@ -31,21 +31,16 @@ namespace MediaBrowser.Model.Session
AudioChannelsNotSupported = 1 << 14,
AudioProfileNotSupported = 1 << 15,
AudioSampleRateNotSupported = 1 << 16,
- AudioBitDepthNotSupported = 1 << 20,
+ AudioBitDepthNotSupported = 1 << 17,
// Bitrate Constraints
- ContainerBitrateExceedsLimit = 1 << 17,
- VideoBitrateNotSupported = 1 << 18,
- AudioBitrateNotSupported = 1 << 19,
+ ContainerBitrateExceedsLimit = 1 << 18,
+ VideoBitrateNotSupported = 1 << 19,
+ AudioBitrateNotSupported = 1 << 20,
// Errors
- UnknownVideoStreamInfo = 1 << 20,
- UnknownAudioStreamInfo = 1 << 21,
- DirectPlayError = 1 << 22,
-
- // Aliases
- ContainerReasons = ContainerNotSupported | ContainerBitrateExceedsLimit,
- AudioReasons = AudioCodecNotSupported | AudioBitrateNotSupported | AudioChannelsNotSupported | AudioProfileNotSupported | AudioSampleRateNotSupported | SecondaryAudioNotSupported | AudioBitDepthNotSupported | AudioIsExternal,
- VideoReasons = VideoCodecNotSupported | VideoResolutionNotSupported | AnamorphicVideoNotSupported | InterlacedVideoNotSupported | VideoBitDepthNotSupported | VideoBitrateNotSupported | VideoFramerateNotSupported | VideoLevelNotSupported | RefFramesNotSupported,
+ UnknownVideoStreamInfo = 1 << 21,
+ UnknownAudioStreamInfo = 1 << 22,
+ DirectPlayError = 1 << 23,
}
}
diff --git a/MediaBrowser.Model/Session/TranscodeReasonExtensions.cs b/MediaBrowser.Model/Session/TranscodeReasonExtensions.cs
index c7a5095f7..213257b20 100644
--- a/MediaBrowser.Model/Session/TranscodeReasonExtensions.cs
+++ b/MediaBrowser.Model/Session/TranscodeReasonExtensions.cs
@@ -1,22 +1,34 @@
-#pragma warning disable CS1591
-
using System;
using System.Linq;
namespace MediaBrowser.Model.Session
{
+ /// <summary>
+ /// Extension methods for serializing TranscodeReason.
+ /// </summary>
public static class TranscodeReasonExtensions
{
- private static TranscodeReason[] values = Enum.GetValues<TranscodeReason>();
+ private static readonly TranscodeReason[] _values = Enum.GetValues<TranscodeReason>();
- public static string Serialize(this MediaBrowser.Model.Session.TranscodeReason reasons, string sep = ",")
+ /// <summary>
+ /// Serializes a TranscodeReason into a delimiter-separated string.
+ /// </summary>
+ /// <param name="reasons">The <see cref="TranscodeReason"/> enumeration.</param>
+ /// <param name="sep">The string separator to use. defualt <c>,</c>.</param>
+ /// <returns>string of transcode reasons delimited.</returns>
+ public static string Serialize(this TranscodeReason reasons, string sep = ",")
{
return string.Join(sep, reasons.ToArray());
}
- public static TranscodeReason[] ToArray(this MediaBrowser.Model.Session.TranscodeReason reasons)
+ /// <summary>
+ /// Serializes a TranscodeReason into an array of individual TranscodeReason bits.
+ /// </summary>
+ /// <param name="reasons">The <see cref="TranscodeReason"/> enumeration.</param>
+ /// <returns>Array of <c>TranscodeReason</c>.</returns>
+ public static TranscodeReason[] ToArray(this TranscodeReason reasons)
{
- return values.Where(r => r != 0 && reasons.HasFlag(r)).ToArray();
+ return _values.Where(r => r != 0 && reasons.HasFlag(r)).ToArray();
}
}
}
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
index 78e5baad7..8834856b0 100644
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ b/MediaBrowser.Model/Session/TranscodingInfo.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Model.Session
public HardwareEncodingType? HardwareAccelerationType { get; set; }
- public TranscodeReason[] TranscodeReasons { get => TranscodeReason.ToArray(); }
+ public TranscodeReason[] TranscodeReasons => TranscodeReason.ToArray();
[JsonIgnore]
public TranscodeReason TranscodeReason { get; set; }
diff --git a/tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs b/tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs
index ccd95f750..c3e3324bb 100644
--- a/tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs
+++ b/tests/Jellyfin.Dlna.Tests/StreamBuilderTests.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
+using System.Runtime.Serialization;
using System.Text.Json;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
@@ -19,97 +20,117 @@ namespace Jellyfin.MediaBrowser.Model.Tests
{
[Theory]
// Chrome
- [InlineData("Chrome", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
+ [InlineData("Chrome", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
+ [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// Firefox
- [InlineData("Firefox", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
+ [InlineData("Firefox", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
+ [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// Safari
- [InlineData("SafariNext", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should probably be DirectPlay
- [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should probably be DirectPlay
+ [InlineData("SafariNext", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.DirectPlay)] // #6450
// AndroidPixel
- [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("AndroidPixel", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("AndroidPixel", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("AndroidPixel", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("AndroidPixel", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("AndroidPixel", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
[InlineData("AndroidPixel", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
[InlineData("AndroidPixel", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
// Yatse
- [InlineData("Yatse", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Yatse", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
+ [InlineData("Yatse", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
+ [InlineData("Yatse", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// RokuSSPlus
- [InlineData("RokuSSPlus", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
- [InlineData("RokuSSPlus", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectStream
+ [InlineData("RokuSSPlus", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450 should be DirectPlay
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
// JellyfinMediaPlayer
- [InlineData("JellyfinMediaPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("JellyfinMediaPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ // Chrome-NoHLS
+ [InlineData("Chrome-NoHLS", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome-NoHLS", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")]
+ [InlineData("Chrome-NoHLS", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode", "http")]
+ [InlineData("Chrome-NoHLS", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome-NoHLS", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome-NoHLS", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// TranscodeMedia
- [InlineData("TranscodeMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("TranscodeMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
+ [InlineData("TranscodeMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
+ [InlineData("TranscodeMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.DirectPlayError, "Transcode")]
// DirectMedia
- [InlineData("DirectMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("DirectMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("DirectMedia", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")]
+ [InlineData("DirectMedia", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
+ [InlineData("DirectMedia", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
+ [InlineData("DirectMedia", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)]
// LowBandwidth
- [InlineData("LowBandwidth", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450 should be DirectPlay
- [InlineData("LowBandwidth", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450 should be DirectPlay
+ [InlineData("LowBandwidth", "mp4-h264-aac-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
+ [InlineData("LowBandwidth", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
// Null
- [InlineData("Null", "mp4-h264-aac-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mp4-h264-ac3-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mp4-h264-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mp4-hevc-aac-srt-15200k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mp4-hevc-ac3-aac-srt-15200k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mkv-vp9-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mkv-vp9-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Null", "mkv-vp9-vorbis-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit | TranscodeReason.SubtitleCodecNotSupported)] // #6450 should be DirectPlay
+ [InlineData("Null", "mp4-h264-aac-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mp4-h264-ac3-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mp4-h264-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mp4-hevc-aac-srt-15200k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mp4-hevc-ac3-aac-srt-15200k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mkv-vp9-aac-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mkv-vp9-ac3-srt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+ [InlineData("Null", "mkv-vp9-vorbis-vtt-2600k", null, TranscodeReason.ContainerBitrateExceedsLimit)]
+
+ // [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
{
var options = await GetVideoOptions(deviceName, mediaSource);
@@ -118,88 +139,103 @@ namespace Jellyfin.MediaBrowser.Model.Tests
[Theory]
// Chrome
- [InlineData("Chrome", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
+ [InlineData("Chrome", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 <BUG: this is direct played>
+ [InlineData("Chrome", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Chrome", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
+ [InlineData("Chrome", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Chrome", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// Firefox
- [InlineData("Firefox", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
+ [InlineData("Firefox", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
[InlineData("Firefox", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
- [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported | TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be 'false'
- [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
+ [InlineData("Firefox", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Firefox", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// Safari
- [InlineData("SafariNext", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should probably be DirectPlay
- [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should probably be DirectPlay
+ [InlineData("SafariNext", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("SafariNext", "mp4-hevc-ac3-aacExt-srt-15200k", PlayMethod.DirectPlay)] // #6450
// AndroidPixel
- [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("AndroidPixel", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("AndroidPixel", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
+ [InlineData("AndroidPixel", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("AndroidPixel", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("AndroidPixel", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
[InlineData("AndroidPixel", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
[InlineData("AndroidPixel", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")]
// Yatse
- [InlineData("Yatse", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")]
- [InlineData("Yatse", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
+ [InlineData("Yatse", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("Yatse", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)]
+ [InlineData("Yatse", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
// RokuSSPlus
- [InlineData("RokuSSPlus", "mp4-h264-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-h264-ac3-srt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectStream
- [InlineData("RokuSSPlus", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-srt-15200k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // #6450 should be DirectStream
+ [InlineData("RokuSSPlus", "mp4-h264-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450 should be DirectPlay
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aacDef-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-srt-15200k", PlayMethod.DirectStream, TranscodeReason.AudioCodecNotSupported)] // #6450
// JellyfinMediaPlayer
- [InlineData("JellyfinMediaPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod?playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-h264-ac3-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-hevc-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.ContainerBitrateExceedsLimit, "Transcode")] // #6450
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("JellyfinMediaPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] // #6450
+ public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
{
var options = await GetVideoOptions(deviceName, mediaSource);
options.AudioStreamIndex = 1;
- options.SubtitleStreamIndex = options.MediaSources[0].MediaStreams.Count() - 1;
- BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
+ options.SubtitleStreamIndex = options.MediaSources[0].MediaStreams.Count - 1;
+
+ var streamInfo = BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
+ Assert.Equal(streamInfo?.AudioStreamIndex, options.AudioStreamIndex);
+ Assert.Equal(streamInfo?.SubtitleStreamIndex, options.SubtitleStreamIndex);
}
[Theory]
// Chrome
- [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] // #6450 should have container & profile video reasons?
+ [InlineData("Chrome", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Chrome", "mp4-h264-ac3-aacExt-srt-2600k", PlayMethod.DirectStream, TranscodeReason.AudioIsExternal)] // #6450
+ [InlineData("Chrome", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
// Firefox
- [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")] // #6450 should have container & profile video reasons?
+ [InlineData("Firefox", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)] // #6450
+ [InlineData("Firefox", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.VideoCodecNotSupported, "Transcode")]
// Yatse
- [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported)] // #6450 should be DirectPlay
- [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.Transcode, TranscodeReason.SecondaryAudioNotSupported, "Transcode")] // #6450 should be DirectPlay
+ [InlineData("Yatse", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("Yatse", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
// RokuSSPlus
- [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectStream)] // #6450 should be DirectPlay
- public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
+ [InlineData("RokuSSPlus", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ [InlineData("RokuSSPlus", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, TranscodeReason.None, "Remux")] // #6450
+ public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = TranscodeReason.None, string transcodeMode = "DirectStream", string transcodeProtocol = "")
{
var options = await GetVideoOptions(deviceName, mediaSource);
- var streamCount = options.MediaSources[0].MediaStreams.Count();
+ var streamCount = options.MediaSources[0].MediaStreams.Count;
options.AudioStreamIndex = streamCount - 2;
options.SubtitleStreamIndex = streamCount - 1;
- BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
+
+ var streamInfo = BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol);
+ Assert.Equal(streamInfo?.AudioStreamIndex, options.AudioStreamIndex);
+ Assert.Equal(streamInfo?.SubtitleStreamIndex, options.SubtitleStreamIndex);
}
- private void BuildVideoItemSimpleTest(VideoOptions options, PlayMethod? playMethod, TranscodeReason why, string transcodeMode, string transcodeProtocol)
+ private StreamInfo? BuildVideoItemSimpleTest(VideoOptions options, PlayMethod? playMethod, TranscodeReason why, string transcodeMode, string transcodeProtocol)
{
if (string.IsNullOrEmpty(transcodeProtocol))
{
@@ -235,7 +271,8 @@ namespace Jellyfin.MediaBrowser.Model.Tests
{
// check expected container
var containers = ContainerProfile.SplitValue(mediaSource.Container);
- Assert.Contains(uri.Extension, containers);
+ // TODO: test transcode too
+ // Assert.Contains(uri.Extension, containers);
// check expected video codec (1)
Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec);
@@ -243,15 +280,19 @@ namespace Jellyfin.MediaBrowser.Model.Tests
// check expected audio codecs (1)
Assert.Contains(targetAudioStream.Codec, val.TargetAudioCodec);
- Assert.Single(val.AudioCodecs);
+ Assert.Single(val.TargetAudioCodec);
+ // Assert.Single(val.AudioCodecs);
- // TODO: validate transcoding options as well
+ if (transcodeMode == "DirectStream")
+ {
+ Assert.Equal(val.Container, uri.Extension);
+ }
}
else if (playMethod == PlayMethod.DirectStream || playMethod == PlayMethod.Transcode)
{
Assert.NotNull(val.Container);
- // Assert.NotEmpty(val.VideoCodecs);
- // Assert.NotEmpty(val.AudioCodecs);
+ Assert.NotEmpty(val.VideoCodecs);
+ Assert.NotEmpty(val.AudioCodecs);
// check expected container (todo: this could be a test param)
if (transcodeProtocol == "http")
@@ -259,7 +300,7 @@ namespace Jellyfin.MediaBrowser.Model.Tests
// Assert.Equal("webm", val.Container);
Assert.Equal(val.Container, uri.Extension);
Assert.Equal("stream", uri.Filename);
- // Assert.Equal("http", val.SubProtocol);
+ Assert.Equal("http", val.SubProtocol);
}
else
{
@@ -272,12 +313,11 @@ namespace Jellyfin.MediaBrowser.Model.Tests
// Full transcode
if (transcodeMode == "Transcode")
{
- // TODO: what else to validate here
- if ((val.TranscodeReasons & TranscodeReason.ContainerReasons) == TranscodeReason.None)
+ if ((val.TranscodeReasons & (StreamBuilder.ContainerReasons | TranscodeReason.DirectPlayError)) == TranscodeReason.None)
{
- // Assert.All(
- // videoStreams,
- // stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs));
+ Assert.All(
+ videoStreams,
+ stream => Assert.DoesNotContain(stream.Codec, val.VideoCodecs));
}
// todo: fill out tests here
@@ -295,7 +335,7 @@ namespace Jellyfin.MediaBrowser.Model.Tests
if (!targetAudioStream.IsExternal)
{
// check expected audio codecs (1)
- // Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs);
+ Assert.DoesNotContain(targetAudioStream.Codec, val.AudioCodecs);
}
}
else if (transcodeMode == "Remux")
@@ -309,10 +349,10 @@ namespace Jellyfin.MediaBrowser.Model.Tests
var videoStream = targetVideoStream;
Assert.False(val.EstimateContentLength);
Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo);
- // Assert.Contains(videoStream.Profile?.ToLowerInvariant() ?? string.Empty, val.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? new string[0]);
- // Assert.Equal(videoStream.Level, val.TargetVideoLevel);
- // Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth);
- // Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue);
+ Assert.Contains(videoStream.Profile?.ToLowerInvariant() ?? string.Empty, val.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty<string>());
+ Assert.Equal(videoStream.Level, val.TargetVideoLevel);
+ Assert.Equal(videoStream.BitDepth, val.TargetVideoBitDepth);
+ Assert.InRange(val.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue);
// audio codec not supported
if ((why & TranscodeReason.AudioCodecNotSupported) != TranscodeReason.None)
@@ -335,24 +375,23 @@ namespace Jellyfin.MediaBrowser.Model.Tests
{
if (!stream.IsExternal)
{
- // Assert.DoesNotContain(stream.Codec, val.AudioCodecs);
+ Assert.DoesNotContain(stream.Codec, val.AudioCodecs);
}
});
}
}
}
}
-
- if (playMethod == null)
+ else if (playMethod == null)
{
- // what should the actual result be here?
Assert.Null(val.SubProtocol);
- Assert.EndsWith("/stream", uri.Path, StringComparison.InvariantCulture);
+ Assert.Equal("stream", uri.Filename);
Assert.False(val.EstimateContentLength);
Assert.Equal(TranscodeSeekInfo.Auto, val.TranscodeSeekInfo);
- // Assert.True(val.CopyTimestamps);
}
+
+ return val;
}
private static async ValueTask<T> TestData<T>(string name)
@@ -366,7 +405,7 @@ namespace Jellyfin.MediaBrowser.Model.Tests
return value;
}
- throw new Exception("Invalid test data: " + name);
+ throw new SerializationException("Invalid test data: " + name);
}
}
@@ -394,6 +433,8 @@ namespace Jellyfin.MediaBrowser.Model.Tests
MediaSources = mediaSources,
DeviceId = "test-deviceId",
Profile = dp,
+ AllowAudioStreamCopy = true,
+ AllowVideoStreamCopy = true,
};
}