Extract container, video and audio compatibility checks (#12678)

* Extract container, video and audio compatibility checks

* Extract audio compatibility checks

* Extract CheckVideoConditions

* Simplify direct audio stream check
pull/13583/head
Dmitry Lyzo 3 weeks ago committed by GitHub
parent 296b17bf44
commit 777e0823ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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);
@ -810,6 +810,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 +828,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 +837,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 +856,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 +962,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);
return satisfied;
}))
.All(satisfied => satisfied);
playlistItem.TranscodeReasons |= directAudioFailures;
var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
&& directAudioFailures == 0;
directAudioStreamSatisfied = directAudioStreamSatisfied && !playlistItem.TranscodeReasons.HasFlag(TranscodeReason.ContainerBitrateExceedsLimit);
@ -1289,52 +1257,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 +1377,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) =>
@ -2315,5 +2231,140 @@ 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? 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, 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;
}
}
}

Loading…
Cancel
Save