aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model/Dlna
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model/Dlna')
-rw-r--r--MediaBrowser.Model/Dlna/CodecType.cs16
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs6
-rw-r--r--MediaBrowser.Model/Dlna/EncodingContext.cs12
-rw-r--r--MediaBrowser.Model/Dlna/PlaybackErrorCode.cs16
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionOptions.cs11
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs84
-rw-r--r--MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs12
8 files changed, 141 insertions, 19 deletions
diff --git a/MediaBrowser.Model/Dlna/CodecType.cs b/MediaBrowser.Model/Dlna/CodecType.cs
index c9f090e4cc..12730a76fa 100644
--- a/MediaBrowser.Model/Dlna/CodecType.cs
+++ b/MediaBrowser.Model/Dlna/CodecType.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The codec type of a codec profile.
+ /// </summary>
public enum CodecType
{
+ /// <summary>
+ /// The profile applies to a video codec.
+ /// </summary>
Video = 0,
+
+ /// <summary>
+ /// The profile applies to the audio codec of a video stream.
+ /// </summary>
VideoAudio = 1,
+
+ /// <summary>
+ /// The profile applies to an audio codec.
+ /// </summary>
Audio = 2
}
}
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 79ee683a2d..a6018f369d 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -33,6 +33,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="numAudioStreams">The number of audio streams.</param>
/// <param name="videoCodecTag">The video codec tag.</param>
/// <param name="isAvc">A value indicating whether the video is AVC.</param>
+ /// <param name="videoRotation">The video rotation angle, usually 0 or +-90/180.</param>
/// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsVideoConditionSatisfied(
ProfileCondition condition,
@@ -53,7 +54,8 @@ namespace MediaBrowser.Model.Dlna
int? numVideoStreams,
int? numAudioStreams,
string? videoCodecTag,
- bool? isAvc)
+ bool? isAvc,
+ int? videoRotation)
{
switch (condition.Property)
{
@@ -93,6 +95,8 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, numVideoStreams);
case ProfileConditionValue.VideoTimestamp:
return IsConditionSatisfied(condition, timestamp);
+ case ProfileConditionValue.VideoRotation:
+ return IsConditionSatisfied(condition, videoRotation);
default:
return true;
}
diff --git a/MediaBrowser.Model/Dlna/EncodingContext.cs b/MediaBrowser.Model/Dlna/EncodingContext.cs
index 79ca6366d7..1408333d2e 100644
--- a/MediaBrowser.Model/Dlna/EncodingContext.cs
+++ b/MediaBrowser.Model/Dlna/EncodingContext.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The encoding context.
+ /// </summary>
public enum EncodingContext
{
+ /// <summary>
+ /// The media is transcoded on the fly and delivered as a stream.
+ /// </summary>
Streaming = 0,
+
+ /// <summary>
+ /// The media is transcoded to a static file.
+ /// </summary>
Static = 1
}
}
diff --git a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
index 300fab5c50..a28f422a2b 100644
--- a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
+++ b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The playback error code.
+ /// </summary>
public enum PlaybackErrorCode
{
+ /// <summary>
+ /// Playback of the item is not allowed.
+ /// </summary>
NotAllowed = 0,
+
+ /// <summary>
+ /// No stream compatible with the device profile was found.
+ /// </summary>
NoCompatibleStream = 1,
+
+ /// <summary>
+ /// The rate limit has been exceeded.
+ /// </summary>
RateLimitExceeded = 2
}
}
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
index b66a15840b..c6171c7ab2 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
@@ -28,6 +28,7 @@ namespace MediaBrowser.Model.Dlna
AudioSampleRate = 22,
AudioBitDepth = 23,
VideoRangeType = 24,
- NumStreams = 25
+ NumStreams = 25,
+ VideoRotation = 26
}
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
index 774592abc7..b161b4a1e4 100644
--- a/MediaBrowser.Model/Dlna/ResolutionOptions.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
@@ -1,11 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The resolution constraints.
+ /// </summary>
public class ResolutionOptions
{
+ /// <summary>
+ /// Gets or sets the maximum width.
+ /// </summary>
public int? MaxWidth { get; set; }
+ /// <summary>
+ /// Gets or sets the maximum height.
+ /// </summary>
public int? MaxHeight { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index c9697c685c..59f97d8c7c 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Dlna
internal const TranscodeReason ContainerReasons = TranscodeReason.ContainerNotSupported | TranscodeReason.ContainerBitrateExceedsLimit;
internal const TranscodeReason AudioCodecReasons = TranscodeReason.AudioBitrateNotSupported | TranscodeReason.AudioChannelsNotSupported | TranscodeReason.AudioProfileNotSupported | TranscodeReason.AudioSampleRateNotSupported | TranscodeReason.SecondaryAudioNotSupported | TranscodeReason.AudioBitDepthNotSupported | TranscodeReason.AudioIsExternal;
internal const TranscodeReason AudioReasons = TranscodeReason.AudioCodecNotSupported | AudioCodecReasons;
- internal const TranscodeReason VideoCodecReasons = TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.VideoProfileNotSupported;
+ internal const TranscodeReason VideoCodecReasons = TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.VideoProfileNotSupported | TranscodeReason.VideoRotationNotSupported;
internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | VideoCodecReasons;
internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecTagNotSupported;
@@ -380,6 +380,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoRangeType:
return TranscodeReason.VideoRangeTypeNotSupported;
+ case ProfileConditionValue.VideoRotation:
+ return TranscodeReason.VideoRotationNotSupported;
+
case ProfileConditionValue.VideoTimestamp:
// TODO
return 0;
@@ -572,7 +575,12 @@ namespace MediaBrowser.Model.Dlna
{
foreach (var profile in subtitleProfiles)
{
- if (profile.Method == SubtitleDeliveryMethod.External && string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (profile.Method == SubtitleDeliveryMethod.External
+ && (string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase)
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are exposed as .mks.
+ || (string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && stream.IsVobSubSubtitleStream
+ && (!stream.IsExternal || stream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)))))
{
return stream.Index;
}
@@ -943,6 +951,10 @@ namespace MediaBrowser.Model.Dlna
}
playlistItem.VideoCodecs = videoCodecs;
+ if (videoStream is not null && !ContainerHelper.ContainsContainer(videoCodecs, false, videoStream.Codec))
+ {
+ playlistItem.TranscodeReasons |= TranscodeReason.VideoCodecNotSupported;
+ }
// Copy video codec options as a starting point, this applies to transcode and direct-stream
playlistItem.MaxFramerate = videoStream?.ReferenceFrameRate;
@@ -991,6 +1003,10 @@ namespace MediaBrowser.Model.Dlna
var directAudioFailures = audioStreamWithSupportedCodec is null ? default : GetCompatibilityAudioCodec(options, item, container ?? string.Empty, audioStreamWithSupportedCodec, null, true, false);
playlistItem.TranscodeReasons |= directAudioFailures;
+ if (audioStream is not null && audioStreamWithSupportedCodec is null)
+ {
+ playlistItem.TranscodeReasons |= TranscodeReason.AudioCodecNotSupported;
+ }
var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
&& directAudioFailures == 0;
@@ -1040,6 +1056,7 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced;
string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
+ int? videoRotation = videoStream?.Rotation;
TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp;
int? packetLength = videoStream?.PacketLength;
@@ -1054,7 +1071,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, numStreams, 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, videoRotation)))
// Reverse codec profiles for backward compatibility - first codec profile has higher priority
.Reverse();
foreach (var condition in appliedVideoConditions)
@@ -1447,7 +1464,7 @@ namespace MediaBrowser.Model.Dlna
string? outputContainer,
MediaStreamProtocol? transcodingSubProtocol)
{
- if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || transcodingSubProtocol != MediaStreamProtocol.hls))
+ if (CanConsiderEmbedSubtitle(subtitleStream, playMethod, transcodingSubProtocol, outputContainer))
{
// Look for supported embedded subs of the same format
foreach (var profile in subtitleProfiles)
@@ -1536,6 +1553,19 @@ namespace MediaBrowser.Model.Dlna
return false;
}
+ private static bool CanConsiderEmbedSubtitle(MediaStream subtitleStream, PlayMethod playMethod, MediaStreamProtocol? transcodingSubProtocol, string? outputContainer)
+ {
+ if (subtitleStream.IsExternal)
+ {
+ return playMethod == PlayMethod.Transcode
+ && transcodingSubProtocol != MediaStreamProtocol.hls
+ && IsSubtitleEmbedSupported(outputContainer);
+ }
+
+ return playMethod != PlayMethod.Transcode
+ || transcodingSubProtocol != MediaStreamProtocol.hls;
+ }
+
private static SubtitleProfile? GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
{
foreach (var profile in subtitleProfiles)
@@ -1560,10 +1590,17 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are matched against external .mks delivery profiles.
+ bool isVobSubMksProfile = string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && subtitleStream.IsVobSubSubtitleStream
+ && (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase));
+
+ if ((profile.Method == SubtitleDeliveryMethod.External
+ && (isVobSubMksProfile || subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))) ||
(profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
{
- bool requiresConversion = !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
+ bool requiresConversion = !isVobSubMksProfile
+ && !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
if (!requiresConversion)
{
@@ -2059,6 +2096,38 @@ namespace MediaBrowser.Model.Dlna
break;
}
+ case ProfileConditionValue.VideoRotation:
+ {
+ if (string.IsNullOrEmpty(qualifier))
+ {
+ continue;
+ }
+
+ // change from split by | to comma
+ // strip spaces to avoid having to encode
+ var values = value
+ .Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+
+ if (condition.Condition == ProfileConditionType.Equals)
+ {
+ item.SetOption(qualifier, "rotation", string.Join(',', values));
+ }
+ else if (condition.Condition == ProfileConditionType.EqualsAny)
+ {
+ var currentValue = item.GetOption(qualifier, "rotation");
+ if (!string.IsNullOrEmpty(currentValue) && values.Any(v => string.Equals(v, currentValue, StringComparison.OrdinalIgnoreCase)))
+ {
+ item.SetOption(qualifier, "rotation", currentValue);
+ }
+ else
+ {
+ item.SetOption(qualifier, "rotation", string.Join(',', values));
+ }
+ }
+
+ break;
+ }
+
case ProfileConditionValue.Height:
{
if (!enableNonQualifiedConditions)
@@ -2281,6 +2350,7 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced;
string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
+ int? videoRotation = videoStream?.Rotation;
TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
int? packetLength = videoStream?.PacketLength;
@@ -2290,7 +2360,7 @@ namespace MediaBrowser.Model.Dlna
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));
+ 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, videoRotation));
}
/// <summary>
diff --git a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
index cc0c6069bf..1563ffd17a 100644
--- a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
+++ b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The transcode seek info.
+ /// </summary>
public enum TranscodeSeekInfo
{
+ /// <summary>
+ /// The seek method is chosen automatically.
+ /// </summary>
Auto = 0,
+
+ /// <summary>
+ /// Seeking is performed by byte position.
+ /// </summary>
Bytes = 1
}
}