aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model/Dlna/StreamBuilder.cs
diff options
context:
space:
mode:
authorJoshua M. Boniface <joshua@boniface.me>2025-08-03 17:27:17 -0400
committerGitHub <noreply@github.com>2025-08-03 17:27:17 -0400
commit4b6fb6c4bb2478badad068ce18aabe0c2955db48 (patch)
tree15f986ee62327cceb8f5c8f009bcf08d10cfaa66 /MediaBrowser.Model/Dlna/StreamBuilder.cs
parente7bc86ebb8496615e0b3f73eb4f13ab4c0913dc8 (diff)
parentdb7465e83d9cc07134a0bffad7ed17b1c7b873da (diff)
Merge branch 'master' into master
Diffstat (limited to 'MediaBrowser.Model/Dlna/StreamBuilder.cs')
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs319
1 files changed, 200 insertions, 119 deletions
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 1ed493708..61e04a813 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -101,21 +101,16 @@ namespace MediaBrowser.Model.Dlna
MediaStream audioStream = item.GetDefaultAudioStream(null);
+ ArgumentNullException.ThrowIfNull(audioStream);
+
var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
var directPlayMethod = directPlayInfo.PlayMethod;
var transcodeReasons = directPlayInfo.TranscodeReasons;
- var inputAudioChannels = audioStream?.Channels;
- var inputAudioBitrate = audioStream?.BitRate;
- var inputAudioSampleRate = audioStream?.SampleRate;
- var inputAudioBitDepth = audioStream?.BitDepth;
-
if (directPlayMethod is PlayMethod.DirectPlay)
{
- 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);
+ var audioFailureReasons = GetCompatibilityAudioCodec(options, item, item.Container, audioStream, null, false, false);
transcodeReasons |= audioFailureReasons;
if (audioFailureReasons == 0)
@@ -188,6 +183,11 @@ namespace MediaBrowser.Model.Dlna
SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
+ var inputAudioChannels = audioStream.Channels;
+ var inputAudioBitrate = audioStream.BitRate;
+ var inputAudioSampleRate = audioStream.SampleRate;
+ var inputAudioBitDepth = audioStream.BitDepth;
+
var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
@@ -338,6 +338,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.IsSecondaryAudio:
return TranscodeReason.SecondaryAudioNotSupported;
+ case ProfileConditionValue.NumStreams:
+ return TranscodeReason.StreamCountExceedsLimit;
+
case ProfileConditionValue.NumAudioStreams:
// TODO
return 0;
@@ -662,15 +665,39 @@ namespace MediaBrowser.Model.Dlna
// Collect candidate audio streams
ICollection<MediaStream> candidateAudioStreams = audioStream is null ? [] : [audioStream];
- if (!options.AudioStreamIndex.HasValue || options.AudioStreamIndex < 0)
+ // When the index is explicitly required by client or the default is specified by user, don't do any stream reselection.
+ if (!item.DefaultAudioIndexSource.HasFlag(AudioIndexSource.User) && (options.AudioStreamIndex is null or < 0))
{
- if (audioStream?.IsDefault == true)
+ // When user has no preferences allow stream selection on all streams.
+ if (item.DefaultAudioIndexSource == AudioIndexSource.None && audioStream is not null)
{
- candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.IsDefault).ToArray();
+ candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio).ToArray();
+ if (audioStream.IsDefault)
+ {
+ // If default is picked, only allow selection within default streams.
+ candidateAudioStreams = candidateAudioStreams.Where(stream => stream.IsDefault).ToArray();
+ }
}
- else
+
+ if (item.DefaultAudioIndexSource.HasFlag(AudioIndexSource.Language))
{
+ // If user has language preference, only allow stream selection within the same language.
candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.Language == audioStream?.Language).ToArray();
+ if (item.DefaultAudioIndexSource.HasFlag(AudioIndexSource.Default))
+ {
+ var defaultStreamsInPreferredLanguage = candidateAudioStreams.Where(stream => stream.IsDefault).ToArray();
+
+ // If the user also prefers default streams, try limit selection within default tracks in the same language.
+ // If there is no default stream in the preferred language, allow selection on all default streams to match the "Play default audio track regardless of language" setting.
+ candidateAudioStreams = defaultStreamsInPreferredLanguage.Length > 0
+ ? defaultStreamsInPreferredLanguage
+ : item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.IsDefault).ToArray();
+ }
+ }
+ else if (item.DefaultAudioIndexSource.HasFlag(AudioIndexSource.Default))
+ {
+ // If user prefers default streams, only allow stream selection on default streams.
+ candidateAudioStreams = item.MediaStreams.Where(stream => stream.Type == MediaStreamType.Audio && stream.IsDefault).ToArray();
}
}
@@ -797,7 +824,7 @@ namespace MediaBrowser.Model.Dlna
options.SubtitleStreamIndex,
playlistItem.PlayMethod,
playlistItem.TranscodeReasons,
- playlistItem.ToUrl("media:", "<token>"));
+ playlistItem.ToUrl("media:", "<token>", null));
item.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
return playlistItem;
@@ -810,6 +837,10 @@ namespace MediaBrowser.Model.Dlna
MediaStream? audioStream,
StreamInfo playlistItem)
{
+ var mediaSource = playlistItem.MediaSource;
+
+ ArgumentNullException.ThrowIfNull(mediaSource);
+
if (!(item.SupportsTranscoding || item.SupportsDirectStream))
{
return (null, null);
@@ -824,17 +855,7 @@ namespace MediaBrowser.Model.Dlna
}
var videoCodec = videoStream?.Codec;
- float videoFramerate = videoStream?.ReferenceFrameRate ?? 0;
- TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp;
- int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
- int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
-
var audioCodec = audioStream?.Codec;
- var audioProfile = audioStream?.Profile;
- var audioChannels = audioStream?.Channels;
- var audioBitrate = audioStream?.BitRate;
- var audioSampleRate = audioStream?.SampleRate;
- var audioBitDepth = audioStream?.BitDepth;
var analyzedProfiles = transcodingProfiles
.Select(transcodingProfile =>
@@ -843,24 +864,16 @@ namespace MediaBrowser.Model.Dlna
var container = transcodingProfile.Container;
- if (options.AllowVideoStreamCopy)
+ if (videoStream is not null
+ && options.AllowVideoStreamCopy
+ && ContainerHelper.ContainsContainer(transcodingProfile.VideoCodec, videoCodec))
{
- if (ContainerHelper.ContainsContainer(transcodingProfile.VideoCodec, videoCodec))
- {
- var appliedVideoConditions = options.Profile.CodecProfiles
- .Where(i => i.Type == CodecType.Video &&
- i.ContainsAnyCodec(videoCodec, container) &&
- i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)))
- .Select(i =>
- i.Conditions.All(condition => ConditionProcessor.IsVideoConditionSatisfied(condition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
-
- // An empty appliedVideoConditions means that the codec has no conditions for the current video stream
- var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied);
- rank.Video = conditionsSatisfied ? 1 : 2;
- }
+ var failures = GetCompatibilityVideoCodec(options, mediaSource, container, videoStream);
+ rank.Video = failures == 0 ? 1 : 2;
}
- if (options.AllowAudioStreamCopy)
+ if (audioStream is not null
+ && options.AllowAudioStreamCopy)
{
// For Audio stream, we prefer the audio codec that can be directly copied, then the codec that can otherwise satisfies
// the transcoding conditions, then the one does not satisfy the transcoding conditions.
@@ -870,19 +883,11 @@ namespace MediaBrowser.Model.Dlna
foreach (var transcodingAudioCodec in transcodingAudioCodecs)
{
- var appliedVideoConditions = options.Profile.CodecProfiles
- .Where(i => i.Type == CodecType.VideoAudio &&
- i.ContainsAnyCodec(transcodingAudioCodec, container) &&
- i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, false)))
- .Select(i =>
- i.Conditions.All(condition => ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, false)));
-
- // An empty appliedVideoConditions means that the codec has no conditions for the current audio stream
- var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied);
+ var failures = GetCompatibilityAudioCodec(options, mediaSource, container, audioStream, transcodingAudioCodec, true, false);
var rankAudio = 3;
- if (conditionsSatisfied)
+ if (failures == 0)
{
rankAudio = string.Equals(transcodingAudioCodec, audioCodec, StringComparison.OrdinalIgnoreCase) ? 1 : 2;
}
@@ -984,22 +989,12 @@ namespace MediaBrowser.Model.Dlna
var channelsExceedsLimit = audioStreamWithSupportedCodec is not null && audioStreamWithSupportedCodec.Channels > (playlistItem.TranscodingMaxAudioChannels ?? int.MaxValue);
- var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
- && options.Profile.CodecProfiles
- .Where(i => i.Type == CodecType.VideoAudio
- && i.ContainsAnyCodec(audioStreamWithSupportedCodec.Codec, container)
- && i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioStreamWithSupportedCodec.Channels, audioStreamWithSupportedCodec.BitRate, audioStreamWithSupportedCodec.SampleRate, audioStreamWithSupportedCodec.BitDepth, audioStreamWithSupportedCodec.Profile, false)))
- .Select(i => i.Conditions.All(condition =>
- {
- var satisfied = ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioStreamWithSupportedCodec.Channels, audioStreamWithSupportedCodec.BitRate, audioStreamWithSupportedCodec.SampleRate, audioStreamWithSupportedCodec.BitDepth, audioStreamWithSupportedCodec.Profile, false);
- if (!satisfied)
- {
- playlistItem.TranscodeReasons |= GetTranscodeReasonForFailedCondition(condition);
- }
+ var directAudioFailures = audioStreamWithSupportedCodec is null ? default : GetCompatibilityAudioCodec(options, item, container ?? string.Empty, audioStreamWithSupportedCodec, null, true, false);
+
+ playlistItem.TranscodeReasons |= directAudioFailures;
- return satisfied;
- }))
- .All(satisfied => satisfied);
+ var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
+ && directAudioFailures == 0;
directAudioStreamSatisfied = directAudioStreamSatisfied && !playlistItem.TranscodeReasons.HasFlag(TranscodeReason.ContainerBitrateExceedsLimit);
@@ -1051,6 +1046,7 @@ namespace MediaBrowser.Model.Dlna
int? packetLength = videoStream?.PacketLength;
int? refFrames = videoStream?.RefFrames;
+ int numStreams = item.MediaStreams.Count;
int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
@@ -1059,7 +1055,7 @@ namespace MediaBrowser.Model.Dlna
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
i.ContainsAnyCodec(playlistItem.VideoCodecs, container, useSubContainer) &&
- i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)))
+ i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)))
// Reverse codec profiles for backward compatibility - first codec profile has higher priority
.Reverse();
foreach (var condition in appliedVideoConditions)
@@ -1289,52 +1285,14 @@ namespace MediaBrowser.Model.Dlna
DeviceProfile profile = options.Profile;
string container = mediaSource.Container;
- // Video
- int? width = videoStream?.Width;
- int? height = videoStream?.Height;
- int? bitDepth = videoStream?.BitDepth;
- int? videoBitrate = videoStream?.BitRate;
- double? videoLevel = videoStream?.Level;
- string? videoProfile = videoStream?.Profile;
- VideoRangeType? videoRangeType = videoStream?.VideoRangeType;
- float videoFramerate = videoStream is null ? 0 : videoStream.ReferenceFrameRate ?? 0;
- bool? isAnamorphic = videoStream?.IsAnamorphic;
- bool? isInterlaced = videoStream?.IsInterlaced;
- string? videoCodecTag = videoStream?.CodecTag;
- bool? isAvc = videoStream?.IsAVC;
-
- TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
- int? packetLength = videoStream?.PacketLength;
- int? refFrames = videoStream?.RefFrames;
-
- int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
- int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
-
- var checkVideoConditions = (ProfileCondition[] conditions) =>
- conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
-
// 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)));
+ var containerProfileReasons = GetCompatibilityContainer(options, mediaSource, container, videoStream);
// Check video conditions
- var videoCodecProfileReasons = AggregateFailureConditions(
- mediaSource,
- profile,
- "VideoCodecProfile",
- profile.CodecProfiles
- .Where(codecProfile => codecProfile.Type == CodecType.Video &&
- codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
- !checkVideoConditions(codecProfile.ApplyConditions).Any())
- .SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
+ var videoCodecProfileReasons = videoStream is null ? default : GetCompatibilityVideoCodec(options, mediaSource, container, videoStream);
// Check audio candidates profile conditions
- var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream));
+ var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => GetCompatibilityAudioCodecDirect(options, mediaSource, container, audioStream, true, mediaSource.IsSecondaryAudio(audioStream) ?? false));
TranscodeReason subtitleProfileReasons = 0;
if (subtitleStream is not null)
@@ -1447,20 +1405,6 @@ namespace MediaBrowser.Model.Dlna
return (Profile: null, PlayMethod: null, AudioStreamIndex: null, TranscodeReasons: failureReasons);
}
- private TranscodeReason CheckVideoAudioStreamDirectPlay(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
- {
- var profile = options.Profile;
- var audioFailureConditions = GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioStream.Codec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, mediaSource.IsSecondaryAudio(audioStream));
-
- 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<ProfileCondition, TranscodeReason>(0, (reasons, i) =>
@@ -1934,6 +1878,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength:
+ case ProfileConditionValue.NumStreams:
case ProfileConditionValue.NumAudioStreams:
case ProfileConditionValue.NumVideoStreams:
case ProfileConditionValue.IsSecondaryAudio:
@@ -2315,5 +2260,141 @@ namespace MediaBrowser.Model.Dlna
return index;
}
+
+ /// <summary>
+ /// Check the profile conditions.
+ /// </summary>
+ /// <param name="conditions">Profile conditions.</param>
+ /// <param name="mediaSource">Media source.</param>
+ /// <param name="videoStream">Video stream.</param>
+ /// <returns>Failed profile conditions.</returns>
+ private IEnumerable<ProfileCondition> CheckVideoConditions(ProfileCondition[] conditions, MediaSourceInfo mediaSource, MediaStream? videoStream)
+ {
+ int? width = videoStream?.Width;
+ int? height = videoStream?.Height;
+ int? bitDepth = videoStream?.BitDepth;
+ int? videoBitrate = videoStream?.BitRate;
+ double? videoLevel = videoStream?.Level;
+ string? videoProfile = videoStream?.Profile;
+ VideoRangeType? videoRangeType = videoStream?.VideoRangeType;
+ float videoFramerate = videoStream is null ? 0 : videoStream.ReferenceFrameRate ?? 0;
+ bool? isAnamorphic = videoStream?.IsAnamorphic;
+ bool? isInterlaced = videoStream?.IsInterlaced;
+ string? videoCodecTag = videoStream?.CodecTag;
+ bool? isAvc = videoStream?.IsAVC;
+
+ TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
+ int? packetLength = videoStream?.PacketLength;
+ int? refFrames = videoStream?.RefFrames;
+
+ int numStreams = mediaSource.MediaStreams.Count;
+ int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
+ int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
+
+ return conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
+ }
+
+ /// <summary>
+ /// Check the compatibility of the container.
+ /// </summary>
+ /// <param name="options">Media options.</param>
+ /// <param name="mediaSource">Media source.</param>
+ /// <param name="container">Container.</param>
+ /// <param name="videoStream">Video stream.</param>
+ /// <returns>Transcode reasons if the container is not fully compatible.</returns>
+ private TranscodeReason GetCompatibilityContainer(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream? videoStream)
+ {
+ var profile = options.Profile;
+
+ var failures = AggregateFailureConditions(
+ mediaSource,
+ profile,
+ "VideoCodecProfile",
+ profile.ContainerProfiles
+ .Where(containerProfile => containerProfile.Type == DlnaProfileType.Video && containerProfile.ContainsContainer(container))
+ .SelectMany(containerProfile => CheckVideoConditions(containerProfile.Conditions, mediaSource, videoStream)));
+
+ return failures;
+ }
+
+ /// <summary>
+ /// Check the compatibility of the video codec.
+ /// </summary>
+ /// <param name="options">Media options.</param>
+ /// <param name="mediaSource">Media source.</param>
+ /// <param name="container">Container.</param>
+ /// <param name="videoStream">Video stream.</param>
+ /// <returns>Transcode reasons if the video stream is not fully compatible.</returns>
+ private TranscodeReason GetCompatibilityVideoCodec(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream videoStream)
+ {
+ var profile = options.Profile;
+
+ string videoCodec = videoStream.Codec;
+
+ var failures = AggregateFailureConditions(
+ mediaSource,
+ profile,
+ "VideoCodecProfile",
+ profile.CodecProfiles
+ .Where(codecProfile => codecProfile.Type == CodecType.Video &&
+ codecProfile.ContainsAnyCodec(videoCodec, container) &&
+ !CheckVideoConditions(codecProfile.ApplyConditions, mediaSource, videoStream).Any())
+ .SelectMany(codecProfile => CheckVideoConditions(codecProfile.Conditions, mediaSource, videoStream)));
+
+ return failures;
+ }
+
+ /// <summary>
+ /// Check the compatibility of the audio codec.
+ /// </summary>
+ /// <param name="options">Media options.</param>
+ /// <param name="mediaSource">Media source.</param>
+ /// <param name="container">Container.</param>
+ /// <param name="audioStream">Audio stream.</param>
+ /// <param name="transcodingAudioCodec">Override audio codec.</param>
+ /// <param name="isVideo">The media source is video.</param>
+ /// <param name="isSecondaryAudio">The audio stream is secondary.</param>
+ /// <returns>Transcode reasons if the audio stream is not fully compatible.</returns>
+ private TranscodeReason GetCompatibilityAudioCodec(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream, string? transcodingAudioCodec, bool isVideo, bool isSecondaryAudio)
+ {
+ var profile = options.Profile;
+
+ var audioCodec = transcodingAudioCodec ?? audioStream.Codec;
+ var audioProfile = audioStream.Profile;
+ var audioChannels = audioStream.Channels;
+ var audioBitrate = audioStream.BitRate;
+ var audioSampleRate = audioStream.SampleRate;
+ var audioBitDepth = audioStream.BitDepth;
+
+ var audioFailureConditions = isVideo
+ ? GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioCodec, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)
+ : GetProfileConditionsForAudio(profile.CodecProfiles, container, audioCodec, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, true);
+
+ var failures = AggregateFailureConditions(mediaSource, profile, "AudioCodecProfile", audioFailureConditions);
+
+ return failures;
+ }
+
+ /// <summary>
+ /// Check the compatibility of the audio codec for direct playback.
+ /// </summary>
+ /// <param name="options">Media options.</param>
+ /// <param name="mediaSource">Media source.</param>
+ /// <param name="container">Container.</param>
+ /// <param name="audioStream">Audio stream.</param>
+ /// <param name="isVideo">The media source is video.</param>
+ /// <param name="isSecondaryAudio">The audio stream is secondary.</param>
+ /// <returns>Transcode reasons if the audio stream is not fully compatible for direct playback.</returns>
+ private TranscodeReason GetCompatibilityAudioCodecDirect(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream, bool isVideo, bool isSecondaryAudio)
+ {
+ var failures = GetCompatibilityAudioCodec(options, mediaSource, container, audioStream, null, isVideo, isSecondaryAudio);
+
+ if (audioStream.IsExternal)
+ {
+ failures |= TranscodeReason.AudioIsExternal;
+ }
+
+ return failures;
+ }
}
}