@ -1,5 +1,3 @@
#nullable disable
using System ;
using System.Collections.Generic ;
using System.Globalization ;
@ -37,54 +35,37 @@ namespace MediaBrowser.Model.Dlna
_logger = logger ;
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamBuilder"/> class.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/> object.</param>
public StreamBuilder ( ILogger < StreamBuilder > logger )
: this ( new FullTranscoderSupport ( ) , logger )
{
}
/// <summary>
/// Gets the optimal audio stream.
/// </summary>
/// <param name="options">The <see cref="MediaOptions"/> object to get the audio stream from.</param>
/// <returns>The <see cref="StreamInfo"/> of the optimal audio stream.</returns>
public StreamInfo GetOptimalAudioStream ( MediaOptions options )
public StreamInfo ? GetOptimalAudioStream ( MediaOptions options )
{
ValidateMediaOptions ( options , false ) ;
var mediaSources = new List < MediaSource Info> ( ) ;
var streams = new List < Stream Info> ( ) ;
foreach ( var mediaSource in options . MediaSources )
{
if ( string . IsNullOrEmpty ( options . MediaSourceId ) | |
string . Equals ( mediaSource . Id , options . MediaSourceId , StringComparison . OrdinalIgnoreCase ) )
if ( ! ( string . IsNullOrEmpty ( options . MediaSourceId )
| | string . Equals ( mediaSource . Id , options . MediaSourceId , StringComparison . OrdinalIgnoreCase ) ) )
{
mediaSources . Add ( mediaSource ) ;
continue ;
}
}
var streams = new List < StreamInfo > ( ) ;
foreach ( var mediaSourceInfo in mediaSources )
{
StreamInfo streamInfo = GetOptimalAudioStream ( mediaSourceInfo , options ) ;
StreamInfo ? streamInfo = GetOptimalAudioStream ( mediaSource , options ) ;
if ( streamInfo is not null )
{
streamInfo . DeviceId = options . DeviceId ;
streamInfo . DeviceProfileId = options . Profile . Id ;
streams . Add ( streamInfo ) ;
}
}
foreach ( var stream in streams )
{
stream . DeviceId = options . DeviceId ;
stream . DeviceProfileId = options . Profile . Id ;
}
return GetOptimalStream ( streams , options . GetMaxBitrate ( true ) ? ? 0 ) ;
}
private StreamInfo GetOptimalAudioStream ( MediaSourceInfo item , MediaOptions options )
private StreamInfo ? GetOptimalAudioStream ( MediaSourceInfo item , MediaOptions options )
{
var playlistItem = new StreamInfo
{
@ -138,7 +119,7 @@ namespace MediaBrowser.Model.Dlna
}
}
TranscodingProfile transcodingProfile = null ;
TranscodingProfile ? transcodingProfile = null ;
foreach ( var tcProfile in options . Profile . TranscodingProfiles )
{
if ( tcProfile . Type = = playlistItem . MediaType
@ -190,15 +171,15 @@ namespace MediaBrowser.Model.Dlna
/// </summary>
/// <param name="options">The <see cref="MediaOptions"/> object to get the video stream from.</param>
/// <returns>The <see cref="StreamInfo"/> of the optimal video stream.</returns>
public StreamInfo GetOptimalVideoStream ( MediaOptions options )
public StreamInfo ? GetOptimalVideoStream ( MediaOptions options )
{
ValidateMediaOptions ( options , true ) ;
var mediaSources = new List < MediaSourceInfo > ( ) ;
foreach ( var mediaSourceInfo in options . MediaSources )
{
if ( string . IsNullOrEmpty ( options . MediaSourceId ) | |
string . Equals ( mediaSourceInfo . Id , options . MediaSourceId , StringComparison . OrdinalIgnoreCase ) )
if ( string . IsNullOrEmpty ( options . MediaSourceId )
| | string . Equals ( mediaSourceInfo . Id , options . MediaSourceId , StringComparison . OrdinalIgnoreCase ) )
{
mediaSources . Add ( mediaSourceInfo ) ;
}
@ -223,7 +204,7 @@ namespace MediaBrowser.Model.Dlna
return GetOptimalStream ( streams , options . GetMaxBitrate ( false ) ? ? 0 ) ;
}
private static StreamInfo GetOptimalStream ( List < StreamInfo > streams , long maxBitrate )
private static StreamInfo ? GetOptimalStream ( List < StreamInfo > streams , long maxBitrate )
= > SortMediaSources ( streams , maxBitrate ) . FirstOrDefault ( ) ;
private static IOrderedEnumerable < StreamInfo > SortMediaSources ( List < StreamInfo > streams , long maxBitrate )
@ -366,7 +347,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="type">The <see cref="DlnaProfileType"/>.</param>
/// <param name="playProfile">The <see cref="DirectPlayProfile"/> object to get the video stream from.</param>
/// <returns>The the normalized input container.</returns>
public static string NormalizeMediaSourceFormatIntoSingleContainer ( string inputContainer , DeviceProfile profile , DlnaProfileType type , DirectPlayProfile playProfile = null )
public static string ? NormalizeMediaSourceFormatIntoSingleContainer ( string inputContainer , DeviceProfile ? profile , DlnaProfileType type , DirectPlayProfile ? playProfile = null )
{
if ( string . IsNullOrEmpty ( inputContainer ) )
{
@ -394,7 +375,7 @@ namespace MediaBrowser.Model.Dlna
return formats [ 0 ] ;
}
private ( DirectPlayProfile Profile , PlayMethod ? PlayMethod , TranscodeReason TranscodeReasons ) GetAudioDirectPlayProfile ( MediaSourceInfo item , MediaStream audioStream , MediaOptions options )
private ( DirectPlayProfile ? Profile , PlayMethod ? PlayMethod , TranscodeReason TranscodeReasons ) GetAudioDirectPlayProfile ( MediaSourceInfo item , MediaStream audioStream , MediaOptions options )
{
var directPlayProfile = options . Profile . DirectPlayProfiles
. FirstOrDefault ( x = > x . Type = = DlnaProfileType . Audio & & IsAudioDirectPlaySupported ( x , item , audioStream ) ) ;
@ -410,7 +391,6 @@ namespace MediaBrowser.Model.Dlna
return ( null , null , GetTranscodeReasonsFromDirectPlayProfile ( item , null , audioStream , options . Profile . DirectPlayProfiles ) ) ;
}
var playMethods = new List < PlayMethod > ( ) ;
TranscodeReason transcodeReasons = 0 ;
// The profile describes what the device supports
@ -449,7 +429,7 @@ namespace MediaBrowser.Model.Dlna
return ( directPlayProfile , null , transcodeReasons ) ;
}
private static TranscodeReason GetTranscodeReasonsFromDirectPlayProfile ( MediaSourceInfo item , MediaStream videoStream , MediaStream audioStream , IEnumerable < DirectPlayProfile > directPlayProfiles )
private static TranscodeReason GetTranscodeReasonsFromDirectPlayProfile ( MediaSourceInfo item , MediaStream ? videoStream , MediaStream audioStream , IEnumerable < DirectPlayProfile > directPlayProfiles )
{
var mediaType = videoStream is null ? DlnaProfileType . Audio : DlnaProfileType . Video ;
@ -575,7 +555,7 @@ namespace MediaBrowser.Model.Dlna
}
}
private static void SetStreamInfoOptionsFromDirectPlayProfile ( MediaOptions options , MediaSourceInfo item , StreamInfo playlistItem , DirectPlayProfile directPlayProfile )
private static void SetStreamInfoOptionsFromDirectPlayProfile ( MediaOptions options , MediaSourceInfo item , StreamInfo playlistItem , DirectPlayProfile ? directPlayProfile )
{
var container = NormalizeMediaSourceFormatIntoSingleContainer ( item . Container , options . Profile , DlnaProfileType . Video , directPlayProfile ) ;
var protocol = "http" ;
@ -587,7 +567,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem . SubProtocol = protocol ;
playlistItem . VideoCodecs = new [ ] { item . VideoStream . Codec } ;
playlistItem . AudioCodecs = ContainerProfile . SplitValue ( directPlayProfile .AudioCodec ) ;
playlistItem . AudioCodecs = ContainerProfile . SplitValue ( directPlayProfile ? .AudioCodec ) ;
}
private StreamInfo BuildVideoItem ( MediaSourceInfo item , MediaOptions options )
@ -634,6 +614,12 @@ namespace MediaBrowser.Model.Dlna
var isEligibleForDirectStream = options . EnableDirectStream & & ( options . ForceDirectStream | | ! bitrateLimitExceeded ) ;
TranscodeReason transcodeReasons = 0 ;
// Force transcode or remux for BD/DVD folders
if ( item . VideoType = = VideoType . Dvd | | item . VideoType = = VideoType . BluRay )
{
isEligibleForDirectPlay = false ;
}
if ( bitrateLimitExceeded )
{
transcodeReasons = TranscodeReason . ContainerBitrateExceedsLimit ;
@ -646,7 +632,7 @@ namespace MediaBrowser.Model.Dlna
isEligibleForDirectPlay ,
isEligibleForDirectStream ) ;
DirectPlayProfile directPlayProfile = null ;
DirectPlayProfile ? directPlayProfile = null ;
if ( isEligibleForDirectPlay | | isEligibleForDirectStream )
{
// See if it can be direct played
@ -677,16 +663,16 @@ namespace MediaBrowser.Model.Dlna
playlistItem . AudioStreamIndex = audioStream ? . Index ;
if ( audioStream is not null )
{
playlistItem . AudioCodecs = ContainerProfile . SplitValue ( directPlayProfile .AudioCodec ) ;
playlistItem . AudioCodecs = ContainerProfile . SplitValue ( directPlayProfile ? .AudioCodec ) ;
}
SetStreamInfoOptionsFromDirectPlayProfile ( options , item , playlistItem , directPlayProfile ) ;
BuildStreamVideoItem ( playlistItem , options , item , videoStream , audioStream , candidateAudioStreams , directPlayProfile .Container , directPlayProfile .VideoCodec , directPlayProfile . AudioCodec ) ;
BuildStreamVideoItem ( playlistItem , options , item , videoStream , audioStream , candidateAudioStreams , directPlayProfile ? .Container , directPlayProfile ? .VideoCodec , directPlayProfile ? . AudioCodec ) ;
}
if ( subtitleStream is not null )
{
var subtitleProfile = GetSubtitleProfile ( item , subtitleStream , options . Profile . SubtitleProfiles , directPlay . Value , _transcoderSupport , directPlayProfile .Container , null ) ;
var subtitleProfile = GetSubtitleProfile ( item , subtitleStream , options . Profile . SubtitleProfiles , directPlay . Value , _transcoderSupport , directPlayProfile ? .Container , null ) ;
playlistItem . SubtitleDeliveryMethod = subtitleProfile . Method ;
playlistItem . SubtitleFormat = subtitleProfile . Format ;
@ -748,7 +734,14 @@ namespace MediaBrowser.Model.Dlna
return playlistItem ;
}
private TranscodingProfile GetVideoTranscodeProfile ( MediaSourceInfo item , MediaOptions options , MediaStream videoStream , MediaStream audioStream , IEnumerable < MediaStream > candidateAudioStreams , MediaStream subtitleStream , StreamInfo playlistItem )
private TranscodingProfile ? GetVideoTranscodeProfile (
MediaSourceInfo item ,
MediaOptions options ,
MediaStream ? videoStream ,
MediaStream ? audioStream ,
IEnumerable < MediaStream > candidateAudioStreams ,
MediaStream ? subtitleStream ,
StreamInfo playlistItem )
{
if ( ! ( item . SupportsTranscoding | | item . SupportsDirectStream ) )
{
@ -795,7 +788,16 @@ namespace MediaBrowser.Model.Dlna
return transcodingProfiles . FirstOrDefault ( ) ;
}
private void BuildStreamVideoItem ( StreamInfo playlistItem , MediaOptions options , MediaSourceInfo item , MediaStream videoStream , MediaStream audioStream , IEnumerable < MediaStream > candidateAudioStreams , string container , string videoCodec , string audioCodec )
private void BuildStreamVideoItem (
StreamInfo playlistItem ,
MediaOptions options ,
MediaSourceInfo item ,
MediaStream ? videoStream ,
MediaStream ? audioStream ,
IEnumerable < MediaStream > candidateAudioStreams ,
string? container ,
string? videoCodec ,
string? audioCodec )
{
// Prefer matching video codecs
var videoCodecs = ContainerProfile . SplitValue ( videoCodec ) ;
@ -862,12 +864,12 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream ? . BitDepth ;
int? videoBitrate = videoStream ? . BitRate ;
double? videoLevel = videoStream ? . Level ;
string videoProfile = videoStream ? . Profile ;
string videoRangeType = videoStream ? . VideoRangeType ;
string ? videoProfile = videoStream ? . Profile ;
string ? videoRangeType = videoStream ? . VideoRangeType ;
float videoFramerate = videoStream is null ? 0 : videoStream . AverageFrameRate ? ? videoStream . AverageFrameRate ? ? 0 ;
bool? isAnamorphic = videoStream ? . IsAnamorphic ;
bool? isInterlaced = videoStream ? . IsInterlaced ;
string videoCodecTag = videoStream ? . CodecTag ;
string ? videoCodecTag = videoStream ? . CodecTag ;
bool? isAvc = videoStream ? . IsAVC ;
TransportStreamTimestamp ? timestamp = videoStream is null ? TransportStreamTimestamp . None : item . Timestamp ;
@ -903,11 +905,11 @@ namespace MediaBrowser.Model.Dlna
playlistItem . AudioBitrate = Math . Min ( playlistItem . AudioBitrate ? ? audioBitrate , audioBitrate ) ;
bool? isSecondaryAudio = audioStream is null ? null : item . IsSecondaryAudio ( audioStream ) ;
int? inputAudioBitrate = audioStream is null ? null : audioStream . BitRate ;
int? audioChannels = audioStream is null ? null : audioStream . Channels ;
string audioProfile = audioStream is null ? null : audioStream . Profile ;
int? inputAudioSampleRate = audioStream is null ? null : audioStream . SampleRate ;
int? inputAudioBitDepth = audioStream is null ? null : audioStream . BitDepth ;
int? inputAudioBitrate = audioStream ? . BitRate ;
int? audioChannels = audioStream ? . Channels ;
string ? audioProfile = audioStream ? . Profile ;
int? inputAudioSampleRate = audioStream ? . SampleRate ;
int? inputAudioBitDepth = audioStream ? . BitDepth ;
var appliedAudioConditions = options . Profile . CodecProfiles
. Where ( i = > i . Type = = CodecType . VideoAudio & &
@ -955,7 +957,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem ? . TranscodeReasons ) ;
}
private static int GetDefaultAudioBitrate ( string audioCodec , int? audioChannels )
private static int GetDefaultAudioBitrate ( string ? audioCodec , int? audioChannels )
{
if ( ! string . IsNullOrEmpty ( audioCodec ) )
{
@ -988,9 +990,9 @@ namespace MediaBrowser.Model.Dlna
return 192000 ;
}
private static int GetAudioBitrate ( long maxTotalBitrate , string [ ] targetAudioCodecs , MediaStream audioStream , StreamInfo item )
private static int GetAudioBitrate ( long maxTotalBitrate , string [ ] targetAudioCodecs , MediaStream ? audioStream , StreamInfo item )
{
string targetAudioCodec = targetAudioCodecs . Length = = 0 ? null : targetAudioCodecs [ 0 ] ;
string ? targetAudioCodec = targetAudioCodecs . Length = = 0 ? null : targetAudioCodecs [ 0 ] ;
int? targetAudioChannels = item . GetTargetAudioChannels ( targetAudioCodec ) ;
@ -1081,13 +1083,13 @@ namespace MediaBrowser.Model.Dlna
return 7168000 ;
}
private ( DirectPlayProfile Profile , PlayMethod ? PlayMethod , int? AudioStreamIndex , TranscodeReason TranscodeReasons ) GetVideoDirectPlayProfile (
private ( DirectPlayProfile ? Profile , PlayMethod ? PlayMethod , int? AudioStreamIndex , TranscodeReason TranscodeReasons ) GetVideoDirectPlayProfile (
MediaOptions options ,
MediaSourceInfo mediaSource ,
MediaStream videoStream ,
MediaStream audioStream ,
MediaStream ? videoStream ,
MediaStream ? audioStream ,
ICollection < MediaStream > candidateAudioStreams ,
MediaStream subtitleStream ,
MediaStream ? subtitleStream ,
bool isEligibleForDirectPlay ,
bool isEligibleForDirectStream )
{
@ -1110,12 +1112,12 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream ? . BitDepth ;
int? videoBitrate = videoStream ? . BitRate ;
double? videoLevel = videoStream ? . Level ;
string videoProfile = videoStream ? . Profile ;
string videoRangeType = videoStream ? . VideoRangeType ;
string ? videoProfile = videoStream ? . Profile ;
string ? videoRangeType = videoStream ? . VideoRangeType ;
float videoFramerate = videoStream is null ? 0 : videoStream . AverageFrameRate ? ? videoStream . AverageFrameRate ? ? 0 ;
bool? isAnamorphic = videoStream ? . IsAnamorphic ;
bool? isInterlaced = videoStream ? . IsInterlaced ;
string videoCodecTag = videoStream ? . CodecTag ;
string ? videoCodecTag = videoStream ? . CodecTag ;
bool? isAvc = videoStream ? . IsAVC ;
TransportStreamTimestamp ? timestamp = videoStream is null ? TransportStreamTimestamp . None : mediaSource . Timestamp ;
@ -1203,14 +1205,14 @@ namespace MediaBrowser.Model.Dlna
}
// Check video codec
string videoCodec = videoStream ? . Codec ;
string ? videoCodec = videoStream ? . Codec ;
if ( ! directPlayProfile . SupportsVideoCodec ( videoCodec ) )
{
directPlayProfileReasons | = TranscodeReason . VideoCodecNotSupported ;
}
// Check audio codec
MediaStream selectedAudioStream = null ;
MediaStream ? selectedAudioStream = null ;
if ( candidateAudioStreams . Any ( ) )
{
selectedAudioStream = candidateAudioStreams . FirstOrDefault ( audioStream = > directPlayProfile . SupportsAudioCodec ( audioStream . Codec ) ) ;
@ -1331,8 +1333,8 @@ namespace MediaBrowser.Model.Dlna
SubtitleProfile [ ] subtitleProfiles ,
PlayMethod playMethod ,
ITranscoderSupport transcoderSupport ,
string outputContainer ,
string transcodingSubProtocol )
string ? outputContainer ,
string ? transcodingSubProtocol )
{
if ( ! subtitleStream . IsExternal & & ( playMethod ! = PlayMethod . Transcode | | ! string . Equals ( transcodingSubProtocol , "hls" , StringComparison . OrdinalIgnoreCase ) ) )
{
@ -1405,7 +1407,7 @@ namespace MediaBrowser.Model.Dlna
} ;
}
private static bool IsSubtitleEmbedSupported ( string transcodingContainer )
private static bool IsSubtitleEmbedSupported ( string ? transcodingContainer )
{
if ( ! string . IsNullOrEmpty ( transcodingContainer ) )
{
@ -1427,7 +1429,7 @@ namespace MediaBrowser.Model.Dlna
return false ;
}
private static SubtitleProfile GetExternalSubtitleProfile ( MediaSourceInfo mediaSource , MediaStream subtitleStream , SubtitleProfile [ ] subtitleProfiles , PlayMethod playMethod , ITranscoderSupport transcoderSupport , bool allowConversion )
private static SubtitleProfile ? GetExternalSubtitleProfile ( MediaSourceInfo mediaSource , MediaStream subtitleStream , SubtitleProfile [ ] subtitleProfiles , PlayMethod playMethod , ITranscoderSupport transcoderSupport , bool allowConversion )
{
foreach ( var profile in subtitleProfiles )
{
@ -1560,7 +1562,7 @@ namespace MediaBrowser.Model.Dlna
private static IEnumerable < ProfileCondition > GetProfileConditionsForAudio (
IEnumerable < CodecProfile > codecProfiles ,
string container ,
string codec ,
string ? codec ,
int? audioChannels ,
int? audioBitrate ,
int? audioSampleRate ,
@ -1580,7 +1582,7 @@ namespace MediaBrowser.Model.Dlna
return conditions . Where ( condition = > ! ConditionProcessor . IsAudioConditionSatisfied ( condition , audioChannels , audioBitrate , audioSampleRate , audioBitDepth ) ) ;
}
private void ApplyTranscodingConditions ( StreamInfo item , IEnumerable < ProfileCondition > conditions , string qualifier , bool enableQualifiedConditions , bool enableNonQualifiedConditions )
private void ApplyTranscodingConditions ( StreamInfo item , IEnumerable < ProfileCondition > conditions , string ? qualifier , bool enableQualifiedConditions , bool enableNonQualifiedConditions )
{
foreach ( ProfileCondition condition in conditions )
{
@ -2056,7 +2058,7 @@ namespace MediaBrowser.Model.Dlna
}
// Check audio codec
string audioCodec = audioStream ? . Codec ;
string ? audioCodec = audioStream ? . Codec ;
if ( ! profile . SupportsAudioCodec ( audioCodec ) )
{
return false ;