2020-04-05 16:10:56 +00:00
#nullable disable
2020-02-04 00:49:27 +00:00
#pragma warning disable CS1591
2019-01-13 20:02:23 +00:00
using System ;
2018-12-27 23:27:57 +00:00
using System.Collections.Generic ;
2024-01-06 21:11:08 +00:00
using System.ComponentModel ;
2019-01-13 19:26:15 +00:00
using System.Globalization ;
2019-03-14 21:31:51 +00:00
using System.Linq ;
using System.Text ;
2024-07-16 16:57:02 +00:00
using System.Text.Json.Serialization ;
2023-06-15 11:28:01 +00:00
using Jellyfin.Data.Enums ;
2022-03-04 15:44:55 +00:00
using Jellyfin.Extensions ;
2018-12-27 23:27:57 +00:00
using MediaBrowser.Model.Dlna ;
using MediaBrowser.Model.Extensions ;
using MediaBrowser.Model.MediaInfo ;
namespace MediaBrowser.Model.Entities
{
/// <summary>
2020-06-15 22:37:52 +00:00
/// Class MediaStream.
2018-12-27 23:27:57 +00:00
/// </summary>
public class MediaStream
{
2022-03-03 23:55:20 +00:00
private static readonly string [ ] _specialCodes =
{
// Uncoded languages.
"mis" ,
// Multiple languages.
"mul" ,
// Undetermined.
"und" ,
// No linguistic content; not applicable.
"zxx"
} ;
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets the codec.
/// </summary>
/// <value>The codec.</value>
public string Codec { get ; set ; }
/// <summary>
/// Gets or sets the codec tag.
/// </summary>
/// <value>The codec tag.</value>
public string CodecTag { get ; set ; }
/// <summary>
/// Gets or sets the language.
/// </summary>
/// <value>The language.</value>
public string Language { get ; set ; }
2020-07-24 16:57:34 +00:00
/// <summary>
/// Gets or sets the color range.
/// </summary>
/// <value>The color range.</value>
public string ColorRange { get ; set ; }
/// <summary>
/// Gets or sets the color space.
/// </summary>
/// <value>The color space.</value>
public string ColorSpace { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
/// Gets or sets the color transfer.
/// </summary>
/// <value>The color transfer.</value>
2018-12-27 23:27:57 +00:00
public string ColorTransfer { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
/// Gets or sets the color primaries.
/// </summary>
/// <value>The color primaries.</value>
2018-12-27 23:27:57 +00:00
public string ColorPrimaries { get ; set ; }
2020-05-14 16:44:51 +00:00
2022-06-27 00:55:36 +00:00
/// <summary>
/// Gets or sets the Dolby Vision version major.
/// </summary>
/// <value>The Dolby Vision version major.</value>
public int? DvVersionMajor { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision version minor.
/// </summary>
/// <value>The Dolby Vision version minor.</value>
public int? DvVersionMinor { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision profile.
/// </summary>
/// <value>The Dolby Vision profile.</value>
public int? DvProfile { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision level.
/// </summary>
/// <value>The Dolby Vision level.</value>
public int? DvLevel { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision rpu present flag.
/// </summary>
/// <value>The Dolby Vision rpu present flag.</value>
public int? RpuPresentFlag { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision el present flag.
/// </summary>
/// <value>The Dolby Vision el present flag.</value>
public int? ElPresentFlag { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision bl present flag.
/// </summary>
/// <value>The Dolby Vision bl present flag.</value>
public int? BlPresentFlag { get ; set ; }
/// <summary>
/// Gets or sets the Dolby Vision bl signal compatibility id.
/// </summary>
/// <value>The Dolby Vision bl signal compatibility id.</value>
public int? DvBlSignalCompatibilityId { get ; set ; }
2024-03-29 21:55:15 +00:00
/// <summary>
2024-08-27 19:20:04 +00:00
/// Gets or sets the Rotation in degrees.
2024-03-29 21:55:15 +00:00
/// </summary>
/// <value>The video rotation.</value>
public int? Rotation { get ; set ; }
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets the comment.
/// </summary>
/// <value>The comment.</value>
public string Comment { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
/// Gets or sets the time base.
/// </summary>
/// <value>The time base.</value>
2018-12-27 23:27:57 +00:00
public string TimeBase { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
/// Gets or sets the codec time base.
/// </summary>
/// <value>The codec time base.</value>
2018-12-27 23:27:57 +00:00
public string CodecTimeBase { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
2018-12-27 23:27:57 +00:00
public string Title { get ; set ; }
2020-05-14 16:44:51 +00:00
/// <summary>
2021-02-20 22:13:04 +00:00
/// Gets the video range.
2020-05-14 16:44:51 +00:00
/// </summary>
/// <value>The video range.</value>
2023-06-15 11:28:01 +00:00
public VideoRange VideoRange
2018-12-27 23:27:57 +00:00
{
get
{
2022-06-17 16:01:06 +00:00
var ( videoRange , _ ) = GetVideoColorRange ( ) ;
2018-12-27 23:27:57 +00:00
2022-06-17 16:01:06 +00:00
return videoRange ;
}
}
2021-05-18 10:37:00 +00:00
2022-06-17 16:01:06 +00:00
/// <summary>
/// Gets the video range type.
/// </summary>
/// <value>The video range type.</value>
2023-06-15 11:28:01 +00:00
public VideoRangeType VideoRangeType
2022-06-17 16:01:06 +00:00
{
get
{
var ( _ , videoRangeType ) = GetVideoColorRange ( ) ;
2021-05-18 10:37:00 +00:00
2022-06-17 16:01:06 +00:00
return videoRangeType ;
2018-12-27 23:27:57 +00:00
}
}
2022-06-27 00:55:36 +00:00
/// <summary>
/// Gets the video dovi title.
/// </summary>
/// <value>The video dovi title.</value>
public string VideoDoViTitle
{
get
{
var dvProfile = DvProfile ;
var rpuPresentFlag = RpuPresentFlag = = 1 ;
var blPresentFlag = BlPresentFlag = = 1 ;
var dvBlCompatId = DvBlSignalCompatibilityId ;
if ( rpuPresentFlag
& & blPresentFlag
& & ( dvProfile = = 4
| | dvProfile = = 5
| | dvProfile = = 7
| | dvProfile = = 8
2024-09-07 17:18:18 +00:00
| | dvProfile = = 9
| | dvProfile = = 10 ) )
2022-06-27 00:55:36 +00:00
{
2024-07-28 23:44:13 +00:00
var title = "Dolby Vision Profile " + dvProfile ;
2022-06-27 00:55:36 +00:00
if ( dvBlCompatId > 0 )
{
title + = "." + dvBlCompatId ;
}
return dvBlCompatId switch
{
1 = > title + " (HDR10)" ,
2 = > title + " (SDR)" ,
4 = > title + " (HLG)" ,
2024-07-28 23:44:13 +00:00
6 = > title + " (HDR10)" , // Technically means Blu-ray, but practically always HDR10
2022-06-27 00:55:36 +00:00
_ = > title
} ;
}
return null ;
}
}
2024-01-06 21:11:08 +00:00
/// <summary>
/// Gets the audio spatial format.
/// </summary>
/// <value>The audio spatial format.</value>
[DefaultValue(AudioSpatialFormat.None)]
public AudioSpatialFormat AudioSpatialFormat
{
get
{
if ( Type ! = MediaStreamType . Audio | | string . IsNullOrEmpty ( Profile ) )
{
return AudioSpatialFormat . None ;
}
return
Profile . Contains ( "Dolby Atmos" , StringComparison . OrdinalIgnoreCase ) ? AudioSpatialFormat . DolbyAtmos :
Profile . Contains ( "DTS:X" , StringComparison . OrdinalIgnoreCase ) ? AudioSpatialFormat . DTSX :
AudioSpatialFormat . None ;
}
}
2021-02-20 22:13:04 +00:00
public string LocalizedUndefined { get ; set ; }
2020-05-14 16:44:51 +00:00
2021-02-20 22:13:04 +00:00
public string LocalizedDefault { get ; set ; }
2020-05-14 16:44:51 +00:00
2021-02-20 22:13:04 +00:00
public string LocalizedForced { get ; set ; }
2019-03-14 21:31:51 +00:00
2022-03-10 21:20:35 +00:00
public string LocalizedExternal { get ; set ; }
2022-10-07 10:38:05 +00:00
public string LocalizedHearingImpaired { get ; set ; }
2018-12-27 23:27:57 +00:00
public string DisplayTitle
{
get
{
2020-04-06 17:49:35 +00:00
switch ( Type )
2018-12-27 23:27:57 +00:00
{
2020-04-06 17:49:35 +00:00
case MediaStreamType . Audio :
2018-12-27 23:27:57 +00:00
{
2020-04-06 17:49:35 +00:00
var attributes = new List < string > ( ) ;
2022-03-03 23:55:20 +00:00
// Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded).
2022-03-04 15:44:55 +00:00
if ( ! string . IsNullOrEmpty ( Language ) & & ! _specialCodes . Contains ( Language , StringComparison . OrdinalIgnoreCase ) )
2020-04-06 17:49:35 +00:00
{
2020-04-08 22:40:38 +00:00
// Get full language string i.e. eng -> English. Will not work for some languages which use ISO 639-2/B instead of /T codes.
string fullLanguage = CultureInfo
. GetCultures ( CultureTypes . NeutralCultures )
. FirstOrDefault ( r = > r . ThreeLetterISOLanguageName . Equals ( Language , StringComparison . OrdinalIgnoreCase ) )
? . DisplayName ;
attributes . Add ( StringHelper . FirstToUpper ( fullLanguage ? ? Language ) ) ;
2020-04-06 17:49:35 +00:00
}
2024-05-21 12:55:42 +00:00
if ( ! string . IsNullOrEmpty ( Profile ) & & ! string . Equals ( Profile , "lc" , StringComparison . OrdinalIgnoreCase ) )
2020-04-06 17:49:35 +00:00
{
2024-05-21 12:55:42 +00:00
attributes . Add ( Profile ) ;
2020-04-06 17:49:35 +00:00
}
2024-05-21 12:55:42 +00:00
else if ( ! string . IsNullOrEmpty ( Codec ) )
2020-04-06 17:49:35 +00:00
{
2024-05-21 12:55:42 +00:00
attributes . Add ( AudioCodec . GetFriendlyName ( Codec ) ) ;
2020-04-06 17:49:35 +00:00
}
if ( ! string . IsNullOrEmpty ( ChannelLayout ) )
{
2020-04-08 22:40:38 +00:00
attributes . Add ( StringHelper . FirstToUpper ( ChannelLayout ) ) ;
2020-04-06 17:49:35 +00:00
}
else if ( Channels . HasValue )
{
attributes . Add ( Channels . Value . ToString ( CultureInfo . InvariantCulture ) + " ch" ) ;
}
if ( IsDefault )
{
2021-02-20 22:13:04 +00:00
attributes . Add ( string . IsNullOrEmpty ( LocalizedDefault ) ? "Default" : LocalizedDefault ) ;
2020-04-06 17:49:35 +00:00
}
2022-03-10 21:20:35 +00:00
if ( IsExternal )
{
attributes . Add ( string . IsNullOrEmpty ( LocalizedExternal ) ? "External" : LocalizedExternal ) ;
}
2020-04-06 17:49:35 +00:00
if ( ! string . IsNullOrEmpty ( Title ) )
{
2020-07-23 23:13:19 +00:00
var result = new StringBuilder ( Title ) ;
foreach ( var tag in attributes )
{
// Keep Tags that are not already in Title.
2021-05-05 12:44:53 +00:00
if ( ! Title . Contains ( tag , StringComparison . OrdinalIgnoreCase ) )
2020-07-23 23:13:19 +00:00
{
result . Append ( " - " ) . Append ( tag ) ;
}
}
return result . ToString ( ) ;
2020-04-06 17:49:35 +00:00
}
2020-04-08 22:40:38 +00:00
return string . Join ( " - " , attributes ) ;
2018-12-27 23:27:57 +00:00
}
2020-04-06 17:49:35 +00:00
case MediaStreamType . Video :
2018-12-27 23:27:57 +00:00
{
2020-04-06 17:49:35 +00:00
var attributes = new List < string > ( ) ;
var resolutionText = GetResolutionText ( ) ;
if ( ! string . IsNullOrEmpty ( resolutionText ) )
{
attributes . Add ( resolutionText ) ;
}
if ( ! string . IsNullOrEmpty ( Codec ) )
{
attributes . Add ( Codec . ToUpperInvariant ( ) ) ;
}
2024-07-28 23:44:13 +00:00
if ( VideoDoViTitle is not null )
{
attributes . Add ( VideoDoViTitle ) ;
}
else if ( VideoRange ! = VideoRange . Unknown )
2020-11-16 13:15:42 +00:00
{
2023-06-15 11:28:01 +00:00
attributes . Add ( VideoRange . ToString ( ) ) ;
2020-11-16 13:15:42 +00:00
}
2020-04-06 17:49:35 +00:00
if ( ! string . IsNullOrEmpty ( Title ) )
{
2020-07-23 23:13:02 +00:00
var result = new StringBuilder ( Title ) ;
foreach ( var tag in attributes )
{
// Keep Tags that are not already in Title.
2021-05-05 12:44:53 +00:00
if ( ! Title . Contains ( tag , StringComparison . OrdinalIgnoreCase ) )
2020-07-23 23:13:02 +00:00
{
result . Append ( " - " ) . Append ( tag ) ;
}
}
return result . ToString ( ) ;
2020-04-06 17:49:35 +00:00
}
2021-02-12 23:39:18 +00:00
return string . Join ( ' ' , attributes ) ;
2018-12-27 23:27:57 +00:00
}
2020-04-06 17:49:35 +00:00
case MediaStreamType . Subtitle :
2018-12-27 23:27:57 +00:00
{
2020-04-06 17:49:35 +00:00
var attributes = new List < string > ( ) ;
if ( ! string . IsNullOrEmpty ( Language ) )
{
2020-04-08 22:50:25 +00:00
// Get full language string i.e. eng -> English. Will not work for some languages which use ISO 639-2/B instead of /T codes.
string fullLanguage = CultureInfo
. GetCultures ( CultureTypes . NeutralCultures )
. FirstOrDefault ( r = > r . ThreeLetterISOLanguageName . Equals ( Language , StringComparison . OrdinalIgnoreCase ) )
? . DisplayName ;
attributes . Add ( StringHelper . FirstToUpper ( fullLanguage ? ? Language ) ) ;
2020-04-06 17:49:35 +00:00
}
else
{
2021-02-20 22:13:04 +00:00
attributes . Add ( string . IsNullOrEmpty ( LocalizedUndefined ) ? "Und" : LocalizedUndefined ) ;
2020-04-06 17:49:35 +00:00
}
2022-10-07 10:38:05 +00:00
if ( IsHearingImpaired )
{
attributes . Add ( string . IsNullOrEmpty ( LocalizedHearingImpaired ) ? "Hearing Impaired" : LocalizedHearingImpaired ) ;
}
2020-04-06 17:49:35 +00:00
if ( IsDefault )
{
2021-02-20 22:13:04 +00:00
attributes . Add ( string . IsNullOrEmpty ( LocalizedDefault ) ? "Default" : LocalizedDefault ) ;
2020-04-06 17:49:35 +00:00
}
if ( IsForced )
{
2021-02-20 22:13:04 +00:00
attributes . Add ( string . IsNullOrEmpty ( LocalizedForced ) ? "Forced" : LocalizedForced ) ;
2020-04-06 17:49:35 +00:00
}
2021-09-01 16:59:59 +00:00
if ( ! string . IsNullOrEmpty ( Codec ) )
{
attributes . Add ( Codec . ToUpperInvariant ( ) ) ;
}
2022-03-10 21:20:35 +00:00
if ( IsExternal )
{
attributes . Add ( string . IsNullOrEmpty ( LocalizedExternal ) ? "External" : LocalizedExternal ) ;
}
2020-04-06 17:49:35 +00:00
if ( ! string . IsNullOrEmpty ( Title ) )
{
2020-08-03 18:32:45 +00:00
var result = new StringBuilder ( Title ) ;
2020-07-23 23:12:52 +00:00
foreach ( var tag in attributes )
{
// Keep Tags that are not already in Title.
2021-08-28 22:32:50 +00:00
if ( ! Title . Contains ( tag , StringComparison . OrdinalIgnoreCase ) )
2020-07-23 23:12:52 +00:00
{
result . Append ( " - " ) . Append ( tag ) ;
}
}
return result . ToString ( ) ;
2020-04-06 17:49:35 +00:00
}
2020-08-03 18:32:45 +00:00
return string . Join ( " - " , attributes ) ;
2018-12-27 23:27:57 +00:00
}
2020-04-06 17:49:35 +00:00
default :
return null ;
2018-12-27 23:27:57 +00:00
}
}
}
public string NalLengthSize { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether this instance is interlaced.
/// </summary>
/// <value><c>true</c> if this instance is interlaced; otherwise, <c>false</c>.</value>
public bool IsInterlaced { get ; set ; }
public bool? IsAVC { get ; set ; }
/// <summary>
/// Gets or sets the channel layout.
/// </summary>
/// <value>The channel layout.</value>
public string ChannelLayout { get ; set ; }
/// <summary>
/// Gets or sets the bit rate.
/// </summary>
/// <value>The bit rate.</value>
public int? BitRate { get ; set ; }
/// <summary>
/// Gets or sets the bit depth.
/// </summary>
/// <value>The bit depth.</value>
public int? BitDepth { get ; set ; }
/// <summary>
/// Gets or sets the reference frames.
/// </summary>
/// <value>The reference frames.</value>
public int? RefFrames { get ; set ; }
/// <summary>
/// Gets or sets the length of the packet.
/// </summary>
/// <value>The length of the packet.</value>
public int? PacketLength { get ; set ; }
/// <summary>
/// Gets or sets the channels.
/// </summary>
/// <value>The channels.</value>
public int? Channels { get ; set ; }
/// <summary>
/// Gets or sets the sample rate.
/// </summary>
/// <value>The sample rate.</value>
public int? SampleRate { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether this instance is default.
/// </summary>
/// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
public bool IsDefault { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether this instance is forced.
/// </summary>
/// <value><c>true</c> if this instance is forced; otherwise, <c>false</c>.</value>
public bool IsForced { get ; set ; }
2022-10-07 10:38:05 +00:00
/// <summary>
/// Gets or sets a value indicating whether this instance is for the hearing impaired.
/// </summary>
/// <value><c>true</c> if this instance is for the hearing impaired; otherwise, <c>false</c>.</value>
public bool IsHearingImpaired { get ; set ; }
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
public int? Height { get ; set ; }
/// <summary>
/// Gets or sets the width.
/// </summary>
/// <value>The width.</value>
public int? Width { get ; set ; }
/// <summary>
/// Gets or sets the average frame rate.
/// </summary>
/// <value>The average frame rate.</value>
public float? AverageFrameRate { get ; set ; }
/// <summary>
/// Gets or sets the real frame rate.
/// </summary>
/// <value>The real frame rate.</value>
public float? RealFrameRate { get ; set ; }
2024-09-07 17:16:23 +00:00
/// <summary>
/// Gets the framerate used as reference.
/// Prefer AverageFrameRate, if that is null or an unrealistic value
/// then fallback to RealFrameRate.
/// </summary>
/// <value>The reference frame rate.</value>
public float? ReferenceFrameRate
{
get
{
// In some cases AverageFrameRate for videos will be read as 1000fps even if it is not.
// This is probably due to a library compatability issue.
// See https://github.com/jellyfin/jellyfin/pull/12603#discussion_r1748044018 for more info.
return AverageFrameRate < 1000 ? AverageFrameRate : RealFrameRate ;
}
}
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets the profile.
/// </summary>
/// <value>The profile.</value>
public string Profile { get ; set ; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public MediaStreamType Type { get ; set ; }
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
/// <value>The aspect ratio.</value>
public string AspectRatio { get ; set ; }
/// <summary>
/// Gets or sets the index.
/// </summary>
/// <value>The index.</value>
public int Index { get ; set ; }
/// <summary>
/// Gets or sets the score.
/// </summary>
/// <value>The score.</value>
public int? Score { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether this instance is external.
/// </summary>
/// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value>
public bool IsExternal { get ; set ; }
/// <summary>
/// Gets or sets the method.
/// </summary>
/// <value>The method.</value>
public SubtitleDeliveryMethod ? DeliveryMethod { get ; set ; }
2020-09-28 20:04:31 +00:00
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets the delivery URL.
/// </summary>
/// <value>The delivery URL.</value>
public string DeliveryUrl { get ; set ; }
2020-09-28 20:04:31 +00:00
2018-12-27 23:27:57 +00:00
/// <summary>
/// Gets or sets a value indicating whether this instance is external URL.
/// </summary>
/// <value><c>null</c> if [is external URL] contains no value, <c>true</c> if [is external URL]; otherwise, <c>false</c>.</value>
public bool? IsExternalUrl { get ; set ; }
public bool IsTextSubtitleStream
{
get
{
2020-06-20 08:35:29 +00:00
if ( Type ! = MediaStreamType . Subtitle )
{
return false ;
}
2018-12-27 23:27:57 +00:00
if ( string . IsNullOrEmpty ( Codec ) & & ! IsExternal )
{
return false ;
}
return IsTextFormat ( Codec ) ;
}
}
2024-07-16 16:57:02 +00:00
[JsonIgnore]
2024-07-15 12:48:09 +00:00
public bool IsPgsSubtitleStream
{
get
{
if ( Type ! = MediaStreamType . Subtitle )
{
return false ;
}
if ( string . IsNullOrEmpty ( Codec ) & & ! IsExternal )
{
return false ;
}
return IsPgsFormat ( Codec ) ;
}
}
/// <summary>
/// Gets a value indicating whether this is a subtitle steam that is extractable by ffmpeg.
/// All text-based and pgs subtitles can be extracted.
/// </summary>
/// <value><c>true</c> if this is a extractable subtitle steam otherwise, <c>false</c>.</value>
2024-07-16 16:57:02 +00:00
[JsonIgnore]
2024-07-15 12:48:09 +00:00
public bool IsExtractableSubtitleStream = > IsTextSubtitleStream | | IsPgsSubtitleStream ;
2021-02-20 22:13:04 +00:00
/// <summary>
/// Gets or sets a value indicating whether [supports external stream].
/// </summary>
/// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
public bool SupportsExternalStream { get ; set ; }
/// <summary>
/// Gets or sets the filename.
/// </summary>
/// <value>The filename.</value>
public string Path { get ; set ; }
/// <summary>
/// Gets or sets the pixel format.
/// </summary>
/// <value>The pixel format.</value>
public string PixelFormat { get ; set ; }
/// <summary>
/// Gets or sets the level.
/// </summary>
/// <value>The level.</value>
public double? Level { get ; set ; }
/// <summary>
/// Gets or sets whether this instance is anamorphic.
/// </summary>
/// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
public bool? IsAnamorphic { get ; set ; }
2021-07-20 17:31:47 +00:00
internal string GetResolutionText ( )
2021-02-20 22:13:04 +00:00
{
2021-07-04 08:22:17 +00:00
if ( ! Width . HasValue | | ! Height . HasValue )
2021-02-20 22:13:04 +00:00
{
2021-07-02 23:32:57 +00:00
return null ;
2021-02-20 22:13:04 +00:00
}
2021-07-04 08:22:17 +00:00
return Width switch
2021-07-02 23:32:57 +00:00
{
2022-07-20 23:29:08 +00:00
// 256x144 (16:9 square pixel format)
< = 256 when Height < = 144 = > IsInterlaced ? "144i" : "144p" ,
// 426x240 (16:9 square pixel format)
< = 426 when Height < = 240 = > IsInterlaced ? "240i" : "240p" ,
// 640x360 (16:9 square pixel format)
< = 640 when Height < = 360 = > IsInterlaced ? "360i" : "360p" ,
2022-08-14 01:46:34 +00:00
// 682x384 (16:9 square pixel format)
< = 682 when Height < = 384 = > IsInterlaced ? "384i" : "384p" ,
// 720x404 (16:9 square pixel format)
< = 720 when Height < = 404 = > IsInterlaced ? "404i" : "404p" ,
2022-07-20 23:29:08 +00:00
// 854x480 (16:9 square pixel format)
< = 854 when Height < = 480 = > IsInterlaced ? "480i" : "480p" ,
// 960x544 (16:9 square pixel format)
2021-07-04 08:22:17 +00:00
< = 960 when Height < = 544 = > IsInterlaced ? "540i" : "540p" ,
2022-07-20 23:29:08 +00:00
// 1024x576 (16:9 square pixel format)
< = 1024 when Height < = 576 = > IsInterlaced ? "576i" : "576p" ,
2021-07-02 23:32:57 +00:00
// 1280x720
2021-07-04 08:22:17 +00:00
< = 1280 when Height < = 962 = > IsInterlaced ? "720i" : "720p" ,
2022-08-15 10:48:34 +00:00
// 2560x1080 (FHD ultra wide 21:9) using 1440px width to accommodate WQHD
2022-07-20 23:29:08 +00:00
< = 2560 when Height < = 1440 = > IsInterlaced ? "1080i" : "1080p" ,
2021-07-02 23:32:57 +00:00
// 4K
2021-07-04 08:22:17 +00:00
< = 4096 when Height < = 3072 = > "4K" ,
2021-07-02 23:32:57 +00:00
// 8K
2021-07-04 08:22:17 +00:00
< = 8192 when Height < = 6144 = > "8K" ,
2021-07-02 23:32:57 +00:00
_ = > null
} ;
2021-02-20 22:13:04 +00:00
}
2018-12-27 23:27:57 +00:00
public static bool IsTextFormat ( string format )
{
string codec = format ? ? string . Empty ;
2024-06-23 15:40:59 +00:00
// microdvd and dvdsub/vobsub share the ".sub" file extension, but it's text-based.
return codec . Contains ( "microdvd" , StringComparison . OrdinalIgnoreCase )
| | ( ! codec . Contains ( "pgs" , StringComparison . OrdinalIgnoreCase )
& & ! codec . Contains ( "dvdsub" , StringComparison . OrdinalIgnoreCase )
& & ! codec . Contains ( "dvbsub" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( codec , "sup" , StringComparison . OrdinalIgnoreCase )
& & ! string . Equals ( codec , "sub" , StringComparison . OrdinalIgnoreCase ) ) ;
2018-12-27 23:27:57 +00:00
}
2024-07-15 12:48:09 +00:00
public static bool IsPgsFormat ( string format )
{
string codec = format ? ? string . Empty ;
2018-12-27 23:27:57 +00:00
2024-07-15 12:48:09 +00:00
return codec . Contains ( "pgs" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codec , "sup" , StringComparison . OrdinalIgnoreCase ) ;
2018-12-27 23:27:57 +00:00
}
public bool SupportsSubtitleConversionTo ( string toCodec )
{
if ( ! IsTextSubtitleStream )
{
return false ;
}
var fromCodec = Codec ;
2019-01-07 23:27:46 +00:00
// Can't convert from this
2020-01-09 16:07:13 +00:00
if ( string . Equals ( fromCodec , "ass" , StringComparison . OrdinalIgnoreCase ) )
2018-12-27 23:27:57 +00:00
{
return false ;
}
2020-06-15 21:43:52 +00:00
2020-01-09 16:07:13 +00:00
if ( string . Equals ( fromCodec , "ssa" , StringComparison . OrdinalIgnoreCase ) )
2018-12-27 23:27:57 +00:00
{
return false ;
}
2019-01-07 23:27:46 +00:00
// Can't convert to this
2020-01-09 16:07:13 +00:00
if ( string . Equals ( toCodec , "ass" , StringComparison . OrdinalIgnoreCase ) )
2018-12-27 23:27:57 +00:00
{
return false ;
}
2020-06-15 21:43:52 +00:00
2020-01-09 16:07:13 +00:00
if ( string . Equals ( toCodec , "ssa" , StringComparison . OrdinalIgnoreCase ) )
2018-12-27 23:27:57 +00:00
{
return false ;
}
return true ;
}
2022-06-17 16:01:06 +00:00
2023-06-15 11:28:01 +00:00
public ( VideoRange VideoRange , VideoRangeType VideoRangeType ) GetVideoColorRange ( )
2022-06-17 16:01:06 +00:00
{
if ( Type ! = MediaStreamType . Video )
{
2023-06-15 11:28:01 +00:00
return ( VideoRange . Unknown , VideoRangeType . Unknown ) ;
2022-06-17 16:01:06 +00:00
}
var codecTag = CodecTag ;
2022-06-27 00:55:36 +00:00
var dvProfile = DvProfile ;
var rpuPresentFlag = RpuPresentFlag = = 1 ;
var blPresentFlag = BlPresentFlag = = 1 ;
var dvBlCompatId = DvBlSignalCompatibilityId ;
2024-09-07 17:18:18 +00:00
var isDoViProfile = dvProfile = = 5 | | dvProfile = = 7 | | dvProfile = = 8 | | dvProfile = = 10 ;
2024-03-23 13:39:49 +00:00
var isDoViFlag = rpuPresentFlag & & blPresentFlag & & ( dvBlCompatId = = 0 | | dvBlCompatId = = 1 | | dvBlCompatId = = 4 | | dvBlCompatId = = 2 | | dvBlCompatId = = 6 ) ;
2022-06-17 16:01:06 +00:00
2024-03-23 13:39:49 +00:00
if ( ( isDoViProfile & & isDoViFlag )
2022-06-27 00:55:36 +00:00
| | string . Equals ( codecTag , "dovi" , StringComparison . OrdinalIgnoreCase )
2022-06-17 16:01:06 +00:00
| | string . Equals ( codecTag , "dvh1" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codecTag , "dvhe" , StringComparison . OrdinalIgnoreCase )
| | string . Equals ( codecTag , "dav1" , StringComparison . OrdinalIgnoreCase ) )
{
2024-03-23 13:39:49 +00:00
return dvProfile switch
{
5 = > ( VideoRange . HDR , VideoRangeType . DOVI ) ,
8 = > dvBlCompatId switch
{
1 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHDR10 ) ,
4 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHLG ) ,
2 = > ( VideoRange . SDR , VideoRangeType . DOVIWithSDR ) ,
2024-04-10 19:33:24 +00:00
// While not in Dolby Spec, Profile 8 CCid 6 media are possible to create, and since CCid 6 stems from Bluray (Profile 7 originally) an HDR10 base layer is guaranteed to exist.
6 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHDR10 ) ,
2024-03-23 13:39:49 +00:00
// There is no other case to handle here as per Dolby Spec. Default case included for completeness and linting purposes
_ = > ( VideoRange . SDR , VideoRangeType . SDR )
} ,
7 = > ( VideoRange . HDR , VideoRangeType . HDR10 ) ,
2024-09-07 17:18:18 +00:00
10 = > dvBlCompatId switch
{
0 = > ( VideoRange . HDR , VideoRangeType . DOVI ) ,
1 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHDR10 ) ,
2 = > ( VideoRange . SDR , VideoRangeType . DOVIWithSDR ) ,
4 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHLG ) ,
// While not in Dolby Spec, Profile 8 CCid 6 media are possible to create, and since CCid 6 stems from Bluray (Profile 7 originally) an HDR10 base layer is guaranteed to exist.
6 = > ( VideoRange . HDR , VideoRangeType . DOVIWithHDR10 ) ,
// There is no other case to handle here as per Dolby Spec. Default case included for completeness and linting purposes
_ = > ( VideoRange . SDR , VideoRangeType . SDR )
} ,
2024-03-23 13:39:49 +00:00
_ = > ( VideoRange . SDR , VideoRangeType . SDR )
} ;
}
var colorTransfer = ColorTransfer ;
if ( string . Equals ( colorTransfer , "smpte2084" , StringComparison . OrdinalIgnoreCase ) )
{
return ( VideoRange . HDR , VideoRangeType . HDR10 ) ;
}
else if ( string . Equals ( colorTransfer , "arib-std-b67" , StringComparison . OrdinalIgnoreCase ) )
{
return ( VideoRange . HDR , VideoRangeType . HLG ) ;
2022-06-17 16:01:06 +00:00
}
2023-06-15 11:28:01 +00:00
return ( VideoRange . SDR , VideoRangeType . SDR ) ;
2022-06-17 16:01:06 +00:00
}
2018-12-27 23:27:57 +00:00
}
}