mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-13 08:37:07 +00:00
Merge pull request #16803 from nyanmisaka/new-profile-condition-video-rotation
Add videoRotation profile condition
This commit is contained in:
commit
4f238ca9b3
10 changed files with 310 additions and 41 deletions
|
|
@ -91,6 +91,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
/// <value>The codec tag.</value>
|
||||
public string CodecTag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation.
|
||||
/// </summary>
|
||||
/// <value>The video rotation angle, usually 0 or +-90/180.</value>
|
||||
public string Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the framerate.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2466,6 +2466,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
}
|
||||
}
|
||||
|
||||
var requestedRotations = state.GetRequestedRotations(videoStream.Codec);
|
||||
if (requestedRotations.Length > 0)
|
||||
{
|
||||
var rotation = state.VideoStream?.Rotation ?? 0;
|
||||
if (rotation != 0
|
||||
&& !requestedRotations.Contains(rotation.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Video width must fall within requested value
|
||||
if (request.MaxWidth.HasValue
|
||||
&& (!videoStream.Width.HasValue || videoStream.Width.Value > request.MaxWidth.Value))
|
||||
|
|
|
|||
|
|
@ -571,62 +571,50 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||
|
||||
public string[] GetRequestedProfiles(string codec)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(BaseRequest.Profile))
|
||||
var profile = BaseRequest.Profile;
|
||||
|
||||
if (string.IsNullOrEmpty(profile) && !string.IsNullOrEmpty(codec))
|
||||
{
|
||||
return BaseRequest.Profile.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
profile = BaseRequest.GetOption(codec, "profile");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(codec))
|
||||
{
|
||||
var profile = BaseRequest.GetOption(codec, "profile");
|
||||
|
||||
if (!string.IsNullOrEmpty(profile))
|
||||
{
|
||||
return profile.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.Empty<string>();
|
||||
return (profile ?? string.Empty).Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public string[] GetRequestedRangeTypes(string codec)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(BaseRequest.VideoRangeType))
|
||||
var rangetype = BaseRequest.VideoRangeType;
|
||||
|
||||
if (string.IsNullOrEmpty(rangetype) && !string.IsNullOrEmpty(codec))
|
||||
{
|
||||
return BaseRequest.VideoRangeType.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
rangetype = BaseRequest.GetOption(codec, "rangetype");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(codec))
|
||||
{
|
||||
var rangetype = BaseRequest.GetOption(codec, "rangetype");
|
||||
|
||||
if (!string.IsNullOrEmpty(rangetype))
|
||||
{
|
||||
return rangetype.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.Empty<string>();
|
||||
return (rangetype ?? string.Empty).Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public string[] GetRequestedCodecTags(string codec)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(BaseRequest.CodecTag))
|
||||
var codectag = BaseRequest.CodecTag;
|
||||
|
||||
if (string.IsNullOrEmpty(codectag) && !string.IsNullOrEmpty(codec))
|
||||
{
|
||||
return BaseRequest.CodecTag.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
codectag = BaseRequest.GetOption(codec, "codectag");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(codec))
|
||||
{
|
||||
var codectag = BaseRequest.GetOption(codec, "codectag");
|
||||
return (codectag ?? string.Empty).Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(codectag))
|
||||
{
|
||||
return codectag.Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
public string[] GetRequestedRotations(string codec)
|
||||
{
|
||||
var rotation = BaseRequest.Rotation;
|
||||
|
||||
if (string.IsNullOrEmpty(rotation) && !string.IsNullOrEmpty(codec))
|
||||
{
|
||||
rotation = BaseRequest.GetOption(codec, "rotation");
|
||||
}
|
||||
|
||||
return Array.Empty<string>();
|
||||
return (rotation ?? string.Empty).Split(_separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public string GetRequestedLevel(string codec)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
AudioSampleRate = 22,
|
||||
AudioBitDepth = 23,
|
||||
VideoRangeType = 24,
|
||||
NumStreams = 25
|
||||
NumStreams = 25,
|
||||
VideoRotation = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1040,6 +1043,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 +1058,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)
|
||||
|
|
@ -2059,6 +2063,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 +2317,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 +2327,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>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace MediaBrowser.Model.Session
|
|||
VideoResolutionNotSupported = 1 << 8,
|
||||
VideoBitDepthNotSupported = 1 << 9,
|
||||
VideoFramerateNotSupported = 1 << 10,
|
||||
VideoRotationNotSupported = 1 << 27,
|
||||
RefFramesNotSupported = 1 << 11,
|
||||
AnamorphicVideoNotSupported = 1 << 12,
|
||||
InterlacedVideoNotSupported = 1 << 13,
|
||||
|
|
|
|||
|
|
@ -171,6 +171,9 @@ namespace Jellyfin.Model.Tests
|
|||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("AndroidTVExoPlayer", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.Transcode, TranscodeReason.AudioCodecNotSupported, "Transcode")] // Full transcode because profile only has ts which does not allow vp9
|
||||
[InlineData("AndroidTVExoPlayer", "mp4-hevc-aac-4000k-r180", PlayMethod.DirectPlay)] // #13712
|
||||
// AndroidTV NoHevcRotation
|
||||
[InlineData("AndroidTVExoPlayer-NoHevcRotation", "mp4-hevc-aac-4000k-r180", PlayMethod.Transcode, TranscodeReason.VideoRotationNotSupported, "Transcode")] // #13712
|
||||
// Tizen 3 Stereo
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-aac-vtt-2600k", PlayMethod.DirectPlay)]
|
||||
[InlineData("Tizen3-stereo", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
{
|
||||
"Name": "Jellyfin AndroidTV-ExoPlayer",
|
||||
"EnableAlbumArtInDidl": false,
|
||||
"EnableSingleAlbumArtLimit": false,
|
||||
"EnableSingleSubtitleLimit": false,
|
||||
"SupportedMediaTypes": "Audio,Photo,Video",
|
||||
"MaxAlbumArtWidth": 0,
|
||||
"MaxAlbumArtHeight": 0,
|
||||
"MaxStreamingBitrate": 120000000,
|
||||
"MaxStaticBitrate": 100000000,
|
||||
"MusicStreamingTranscodingBitrate": 192000,
|
||||
"TimelineOffsetSeconds": 0,
|
||||
"RequiresPlainVideoItems": false,
|
||||
"RequiresPlainFolders": false,
|
||||
"EnableMSMediaReceiverRegistrar": false,
|
||||
"IgnoreTranscodeByteRangeRequests": false,
|
||||
"DirectPlayProfiles": [
|
||||
{
|
||||
"Container": "m4v,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,mp4,webm",
|
||||
"AudioCodec": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw",
|
||||
"VideoCodec": "h264,hevc,vp8,vp9,mpeg,mpeg2video",
|
||||
"Type": "Video",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "aac,mp3,mp2,aac_latm,alac,ac3,eac3,dca,dts,mlp,truehd,pcm_alaw,pcm_mulaw,,pa,flac,wav,wma,ogg,oga,webma,ape,opus",
|
||||
"Type": "Audio",
|
||||
"$type": "DirectPlayProfile"
|
||||
},
|
||||
{
|
||||
"Container": "jpg,jpeg,png,gif,web",
|
||||
"Type": "Photo",
|
||||
"$type": "DirectPlayProfile"
|
||||
}
|
||||
],
|
||||
"CodecProfiles": [
|
||||
{
|
||||
"Type": "Video",
|
||||
"Conditions": [
|
||||
{
|
||||
"Condition": "EqualsAny",
|
||||
"Property": "VideoProfile",
|
||||
"Value": "main|main 10",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "LessThanEqual",
|
||||
"Property": "VideoLevel",
|
||||
"Value": "51",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
},
|
||||
{
|
||||
"Condition": "Equals",
|
||||
"Property": "VideoRotation",
|
||||
"Value": "0",
|
||||
"IsRequired": false,
|
||||
"$type": "ProfileCondition"
|
||||
}
|
||||
],
|
||||
"Codec": "hevc",
|
||||
"$type": "CodecProfile"
|
||||
}
|
||||
],
|
||||
"TranscodingProfiles": [
|
||||
{
|
||||
"Container": "ts",
|
||||
"Type": "Video",
|
||||
"VideoCodec": "h264",
|
||||
"AudioCodec": "aac,mp3",
|
||||
"Protocol": "hls",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"$type": "TranscodingProfile"
|
||||
},
|
||||
{
|
||||
"Container": "mp3",
|
||||
"Type": "Audio",
|
||||
"AudioCodec": "mp3",
|
||||
"Protocol": "http",
|
||||
"EstimateContentLength": false,
|
||||
"EnableMpegtsM2TsMode": false,
|
||||
"TranscodeSeekInfo": "Auto",
|
||||
"CopyTimestamps": false,
|
||||
"Context": "Streaming",
|
||||
"EnableSubtitlesInManifest": false,
|
||||
"MinSegments": 0,
|
||||
"SegmentLength": 0,
|
||||
"$type": "TranscodingProfile"
|
||||
}
|
||||
],
|
||||
"SubtitleProfiles": [
|
||||
{
|
||||
"Format": "srt",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "srt",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "subrip",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "subrip",
|
||||
"Method": "External",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ass",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "ssa",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "pgs",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "pgssub",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "dvdsub",
|
||||
"Method": "Encode",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "vtt",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "sub",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
},
|
||||
{
|
||||
"Format": "idx",
|
||||
"Method": "Embed",
|
||||
"$type": "SubtitleProfile"
|
||||
}
|
||||
],
|
||||
"$type": "DeviceProfile"
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"Id": "b7a9e2d4c815f36b0d9241a7e58c3f42",
|
||||
"Path": "/Media/MyVideo-1080p.mp4",
|
||||
"Container": "mov,mp4,m4a,3gp,3g2,mj2",
|
||||
"Size": 1421636271,
|
||||
"Name": "MyVideo-1080p",
|
||||
"ETag": "d8e2a1b5c4f907e8a1d2b3c4e5f6a7b8",
|
||||
"RunTimeTicks": 25801230336,
|
||||
"SupportsTranscoding": true,
|
||||
"SupportsDirectStream": true,
|
||||
"SupportsDirectPlay": true,
|
||||
"SupportsProbing": true,
|
||||
"MediaStreams": [
|
||||
{
|
||||
"Codec": "hevc",
|
||||
"CodecTag": "hvc1",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/11988",
|
||||
"VideoRange": "SDR",
|
||||
"DisplayTitle": "1080p HEVC SDR",
|
||||
"NalLengthSize": "0",
|
||||
"BitRate": 4014613,
|
||||
"BitDepth": 8,
|
||||
"RefFrames": 1,
|
||||
"IsDefault": true,
|
||||
"Height": 1080,
|
||||
"Width": 1920,
|
||||
"AverageFrameRate": 23.976,
|
||||
"RealFrameRate": 23.976,
|
||||
"Profile": "Main",
|
||||
"Type": 1,
|
||||
"AspectRatio": "16:9",
|
||||
"PixelFormat": "yuv420p",
|
||||
"Level": 50,
|
||||
"Rotation": 180
|
||||
},
|
||||
{
|
||||
"Codec": "aac",
|
||||
"CodecTag": "mp4a",
|
||||
"Language": "eng",
|
||||
"TimeBase": "1/48000",
|
||||
"DisplayTitle": "En - AAC - Stereo - Default",
|
||||
"ChannelLayout": "stereo",
|
||||
"BitRate": 125427,
|
||||
"Channels": 2,
|
||||
"SampleRate": 48000,
|
||||
"IsDefault": true,
|
||||
"Profile": "LC",
|
||||
"Index": 1,
|
||||
"Score": 203
|
||||
}
|
||||
],
|
||||
"Bitrate": 4331578,
|
||||
"DefaultAudioStreamIndex": 1,
|
||||
"DefaultSubtitleStreamIndex": 2
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue