Explorar o código

Enable nullable for more files

Bond_009 hai 1 ano
pai
achega
5677566a41

+ 31 - 33
Emby.Dlna/Didl/DidlBuilder.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 
 using System;
@@ -45,8 +43,8 @@ namespace Emby.Dlna.Didl
         private readonly DeviceProfile _profile;
         private readonly IImageProcessor _imageProcessor;
         private readonly string _serverAddress;
-        private readonly string _accessToken;
-        private readonly User _user;
+        private readonly string? _accessToken;
+        private readonly User? _user;
         private readonly IUserDataManager _userDataManager;
         private readonly ILocalizationManager _localization;
         private readonly IMediaSourceManager _mediaSourceManager;
@@ -56,10 +54,10 @@ namespace Emby.Dlna.Didl
 
         public DidlBuilder(
             DeviceProfile profile,
-            User user,
+            User? user,
             IImageProcessor imageProcessor,
             string serverAddress,
-            string accessToken,
+            string? accessToken,
             IUserDataManager userDataManager,
             ILocalizationManager localization,
             IMediaSourceManager mediaSourceManager,
@@ -85,7 +83,7 @@ namespace Emby.Dlna.Didl
             return url + "&dlnaheaders=true";
         }
 
-        public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
+        public string GetItemDidl(BaseItem item, User? user, BaseItem? context, string deviceId, Filter filter, StreamInfo streamInfo)
         {
             var settings = new XmlWriterSettings
             {
@@ -140,12 +138,12 @@ namespace Emby.Dlna.Didl
         public void WriteItemElement(
             XmlWriter writer,
             BaseItem item,
-            User user,
-            BaseItem context,
+            User? user,
+            BaseItem? context,
             StubType? contextStubType,
             string deviceId,
             Filter filter,
-            StreamInfo streamInfo = null)
+            StreamInfo? streamInfo = null)
         {
             var clientId = GetClientId(item, null);
 
@@ -190,7 +188,7 @@ namespace Emby.Dlna.Didl
             writer.WriteFullEndElement();
         }
 
-        private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
+        private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo? streamInfo = null)
         {
             if (streamInfo is null)
             {
@@ -203,7 +201,7 @@ namespace Emby.Dlna.Didl
                     Profile = _profile,
                     DeviceId = deviceId,
                     MaxBitrate = _profile.MaxStreamingBitrate
-                });
+                }) ?? throw new InvalidOperationException("No optimal video stream found");
             }
 
             var targetWidth = streamInfo.TargetWidth;
@@ -315,7 +313,7 @@ namespace Emby.Dlna.Didl
 
             var mediaSource = streamInfo.MediaSource;
 
-            if (mediaSource.RunTimeTicks.HasValue)
+            if (mediaSource?.RunTimeTicks.HasValue == true)
             {
                 writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture));
             }
@@ -410,7 +408,7 @@ namespace Emby.Dlna.Didl
             writer.WriteFullEndElement();
         }
 
-        private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
+        private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem? context)
         {
             if (itemStubType.HasValue)
             {
@@ -452,7 +450,7 @@ namespace Emby.Dlna.Didl
         /// <param name="episode">The episode.</param>
         /// <param name="context">Current context.</param>
         /// <returns>Formatted name of the episode.</returns>
-        private string GetEpisodeDisplayName(Episode episode, BaseItem context)
+        private string GetEpisodeDisplayName(Episode episode, BaseItem? context)
         {
             string[] components;
 
@@ -530,7 +528,7 @@ namespace Emby.Dlna.Didl
 
         private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
 
-        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
+        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo? streamInfo = null)
         {
             writer.WriteStartElement(string.Empty, "res", NsDidl);
 
@@ -544,14 +542,14 @@ namespace Emby.Dlna.Didl
                     MediaSources = sources.ToArray(),
                     Profile = _profile,
                     DeviceId = deviceId
-                });
+                }) ?? throw new InvalidOperationException("No optimal audio stream found");
             }
 
             var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
 
             var mediaSource = streamInfo.MediaSource;
 
-            if (mediaSource.RunTimeTicks.HasValue)
+            if (mediaSource?.RunTimeTicks is not null)
             {
                 writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture));
             }
@@ -634,7 +632,7 @@ namespace Emby.Dlna.Didl
                 // Samsung sometimes uses 1 as root
                 || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase);
 
-        public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
+        public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string? requestedId = null)
         {
             writer.WriteStartElement(string.Empty, "container", NsDidl);
 
@@ -678,14 +676,14 @@ namespace Emby.Dlna.Didl
             writer.WriteFullEndElement();
         }
 
-        private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer, StreamInfo streamInfo)
+        private void AddSamsungBookmarkInfo(BaseItem item, User? user, XmlWriter writer, StreamInfo? streamInfo)
         {
             if (!item.SupportsPositionTicksResume || item is Folder)
             {
                 return;
             }
 
-            XmlAttribute secAttribute = null;
+            XmlAttribute? secAttribute = null;
             foreach (var attribute in _profile.XmlRootAttributes)
             {
                 if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
@@ -695,8 +693,8 @@ namespace Emby.Dlna.Didl
                 }
             }
 
-            // Not a samsung device
-            if (secAttribute is null)
+            // Not a samsung device or no user data
+            if (secAttribute is null || user is null)
             {
                 return;
             }
@@ -717,7 +715,7 @@ namespace Emby.Dlna.Didl
         /// <summary>
         /// Adds fields used by both items and folders.
         /// </summary>
-        private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
+        private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter)
         {
             // Don't filter on dc:title because not all devices will include it in the filter
             // MediaMonkey for example won't display content without a title
@@ -795,7 +793,7 @@ namespace Emby.Dlna.Didl
 
             if (item.IsDisplayedAsFolder || stubType.HasValue)
             {
-                string classType = null;
+                string? classType = null;
 
                 if (!_profile.RequiresPlainFolders)
                 {
@@ -899,7 +897,7 @@ namespace Emby.Dlna.Didl
             }
         }
 
-        private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
+        private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter)
         {
             AddCommonFields(item, itemStubType, context, writer, filter);
 
@@ -975,7 +973,7 @@ namespace Emby.Dlna.Didl
 
         private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer)
         {
-            ImageDownloadInfo imageInfo = GetImageInfo(item);
+            ImageDownloadInfo? imageInfo = GetImageInfo(item);
 
             if (imageInfo is null)
             {
@@ -1073,7 +1071,7 @@ namespace Emby.Dlna.Didl
             writer.WriteFullEndElement();
         }
 
-        private ImageDownloadInfo GetImageInfo(BaseItem item)
+        private ImageDownloadInfo? GetImageInfo(BaseItem item)
         {
             if (item.HasImage(ImageType.Primary))
             {
@@ -1118,7 +1116,7 @@ namespace Emby.Dlna.Didl
             return null;
         }
 
-        private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item)
+        private BaseItem? GetFirstParentWithImageBelowUserRoot(BaseItem item)
         {
             if (item is null)
             {
@@ -1148,7 +1146,7 @@ namespace Emby.Dlna.Didl
         private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
         {
             var imageInfo = item.GetImageInfo(type, 0);
-            string tag = null;
+            string? tag = null;
 
             try
             {
@@ -1250,7 +1248,7 @@ namespace Emby.Dlna.Didl
         {
             internal Guid ItemId { get; set; }
 
-            internal string ImageTag { get; set; }
+            internal string? ImageTag { get; set; }
 
             internal ImageType Type { get; set; }
 
@@ -1260,9 +1258,9 @@ namespace Emby.Dlna.Didl
 
             internal bool IsDirectStream { get; set; }
 
-            internal string Format { get; set; }
+            internal required string Format { get; set; }
 
-            internal ItemImageInfo ItemImageInfo { get; set; }
+            internal required ItemImageInfo ItemImageInfo { get; set; }
         }
     }
 }

+ 42 - 74
Emby.Dlna/PlayTo/Device.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 
 using System;
@@ -25,7 +23,7 @@ namespace Emby.Dlna.PlayTo
         private readonly ILogger _logger;
 
         private readonly object _timerLock = new object();
-        private Timer _timer;
+        private Timer? _timer;
         private int _muteVol;
         private int _volume;
         private DateTime _lastVolumeRefresh;
@@ -40,13 +38,13 @@ namespace Emby.Dlna.PlayTo
             _logger = logger;
         }
 
-        public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
+        public event EventHandler<PlaybackStartEventArgs>? PlaybackStart;
 
-        public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+        public event EventHandler<PlaybackProgressEventArgs>? PlaybackProgress;
 
-        public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
+        public event EventHandler<PlaybackStoppedEventArgs>? PlaybackStopped;
 
-        public event EventHandler<MediaChangedEventArgs> MediaChanged;
+        public event EventHandler<MediaChangedEventArgs>? MediaChanged;
 
         public DeviceInfo Properties { get; set; }
 
@@ -75,13 +73,13 @@ namespace Emby.Dlna.PlayTo
 
         public bool IsStopped => TransportState == TransportState.STOPPED;
 
-        public Action OnDeviceUnavailable { get; set; }
+        public Action? OnDeviceUnavailable { get; set; }
 
-        private TransportCommands AvCommands { get; set; }
+        private TransportCommands? AvCommands { get; set; }
 
-        private TransportCommands RendererCommands { get; set; }
+        private TransportCommands? RendererCommands { get; set; }
 
-        public UBaseObject CurrentMediaInfo { get; private set; }
+        public UBaseObject? CurrentMediaInfo { get; private set; }
 
         public void Start()
         {
@@ -131,7 +129,7 @@ namespace Emby.Dlna.PlayTo
                 _volumeRefreshActive = true;
 
                 var time = immediate ? 100 : 10000;
-                _timer.Change(time, Timeout.Infinite);
+                _timer?.Change(time, Timeout.Infinite);
             }
         }
 
@@ -149,7 +147,7 @@ namespace Emby.Dlna.PlayTo
 
                 _volumeRefreshActive = false;
 
-                _timer.Change(Timeout.Infinite, Timeout.Infinite);
+                _timer?.Change(Timeout.Infinite, Timeout.Infinite);
             }
         }
 
@@ -199,7 +197,7 @@ namespace Emby.Dlna.PlayTo
             }
         }
 
-        private DeviceService GetServiceRenderingControl()
+        private DeviceService? GetServiceRenderingControl()
         {
             var services = Properties.Services;
 
@@ -207,7 +205,7 @@ namespace Emby.Dlna.PlayTo
                 services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase));
         }
 
-        private DeviceService GetAvTransportService()
+        private DeviceService? GetAvTransportService()
         {
             var services = Properties.Services;
 
@@ -240,7 +238,7 @@ namespace Emby.Dlna.PlayTo
                     Properties.BaseUrl,
                     service,
                     command.Name,
-                    rendererCommands.BuildPost(command, service.ServiceType, value),
+                    rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above
                     cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
 
@@ -265,12 +263,7 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var service = GetServiceRenderingControl();
-
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
+            var service = GetServiceRenderingControl() ?? throw new InvalidOperationException("Unable to find service");
 
             // Set it early and assume it will succeed
             // Remote control will perform better
@@ -281,7 +274,7 @@ namespace Emby.Dlna.PlayTo
                     Properties.BaseUrl,
                     service,
                     command.Name,
-                    rendererCommands.BuildPost(command, service.ServiceType, value),
+                    rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above
                     cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
         }
@@ -296,26 +289,20 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var service = GetAvTransportService();
-
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
             await new DlnaHttpClient(_logger, _httpClientFactory)
                 .SendCommandAsync(
                     Properties.BaseUrl,
                     service,
                     command.Name,
-                    avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"),
+                    avCommands!.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"), // null checked above
                     cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
 
             RestartTimer(true);
         }
 
-        public async Task SetAvTransport(string url, string header, string metaData, CancellationToken cancellationToken)
+        public async Task SetAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken)
         {
             var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
 
@@ -335,14 +322,8 @@ namespace Emby.Dlna.PlayTo
                 { "CurrentURIMetaData", CreateDidlMeta(metaData) }
             };
 
-            var service = GetAvTransportService();
-
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
-            var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
+            var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above
             await new DlnaHttpClient(_logger, _httpClientFactory)
                 .SendCommandAsync(
                     Properties.BaseUrl,
@@ -372,7 +353,7 @@ namespace Emby.Dlna.PlayTo
          * SetNextAvTransport is used to specify to the DLNA device what is the next track to play.
          * Without that information, the next track command on the device does not work.
          */
-        public async Task SetNextAvTransport(string url, string header, string metaData, CancellationToken cancellationToken = default)
+        public async Task SetNextAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken = default)
         {
             var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
 
@@ -380,7 +361,7 @@ namespace Emby.Dlna.PlayTo
 
             _logger.LogDebug("{PropertyName} - SetNextAvTransport Uri: {Url} DlnaHeaders: {Header}", Properties.Name, url, header);
 
-            var command = avCommands.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase));
+            var command = avCommands?.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase));
             if (command is null)
             {
                 return;
@@ -392,14 +373,8 @@ namespace Emby.Dlna.PlayTo
                 { "NextURIMetaData", CreateDidlMeta(metaData) }
             };
 
-            var service = GetAvTransportService();
-
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
-            var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
+            var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above
             await new DlnaHttpClient(_logger, _httpClientFactory)
                 .SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header, cancellationToken)
                 .ConfigureAwait(false);
@@ -423,12 +398,7 @@ namespace Emby.Dlna.PlayTo
                 return Task.CompletedTask;
             }
 
-            var service = GetAvTransportService();
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
             return new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
                 Properties.BaseUrl,
                 service,
@@ -460,14 +430,13 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var service = GetAvTransportService();
-
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
             await new DlnaHttpClient(_logger, _httpClientFactory)
                 .SendCommandAsync(
                     Properties.BaseUrl,
                     service,
                     command.Name,
-                    avCommands.BuildPost(command, service.ServiceType, 1),
+                    avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above
                     cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
 
@@ -484,14 +453,13 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
-            var service = GetAvTransportService();
-
+            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
             await new DlnaHttpClient(_logger, _httpClientFactory)
                 .SendCommandAsync(
                     Properties.BaseUrl,
                     service,
                     command.Name,
-                    avCommands.BuildPost(command, service.ServiceType, 1),
+                    avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above
                     cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
 
@@ -500,7 +468,7 @@ namespace Emby.Dlna.PlayTo
             RestartTimer(true);
         }
 
-        private async void TimerCallback(object sender)
+        private async void TimerCallback(object? sender)
         {
             if (_disposed)
             {
@@ -623,7 +591,7 @@ namespace Emby.Dlna.PlayTo
                 Properties.BaseUrl,
                 service,
                 command.Name,
-                rendererCommands.BuildPost(command, service.ServiceType),
+                rendererCommands!.BuildPost(command, service.ServiceType), // null checked above
                 cancellationToken: cancellationToken).ConfigureAwait(false);
 
             if (result is null || result.Document is null)
@@ -673,7 +641,7 @@ namespace Emby.Dlna.PlayTo
                 Properties.BaseUrl,
                 service,
                 command.Name,
-                rendererCommands.BuildPost(command, service.ServiceType),
+                rendererCommands!.BuildPost(command, service.ServiceType), // null checked above
                 cancellationToken: cancellationToken).ConfigureAwait(false);
 
             if (result is null || result.Document is null)
@@ -728,7 +696,7 @@ namespace Emby.Dlna.PlayTo
             return null;
         }
 
-        private async Task<UBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
+        private async Task<UBaseObject?> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
         {
             var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
             if (command is null)
@@ -798,7 +766,7 @@ namespace Emby.Dlna.PlayTo
             return null;
         }
 
-        private async Task<(bool Success, UBaseObject Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
+        private async Task<(bool Success, UBaseObject? Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
         {
             var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
             if (command is null)
@@ -871,7 +839,7 @@ namespace Emby.Dlna.PlayTo
                 return (true, null);
             }
 
-            XElement uPnpResponse = null;
+            XElement? uPnpResponse = null;
 
             try
             {
@@ -895,7 +863,7 @@ namespace Emby.Dlna.PlayTo
             return (true, uTrack);
         }
 
-        private XElement ParseResponse(string xml)
+        private XElement? ParseResponse(string xml)
         {
             // Handle different variations sent back by devices.
             try
@@ -929,7 +897,7 @@ namespace Emby.Dlna.PlayTo
             return null;
         }
 
-        private static UBaseObject CreateUBaseObject(XElement container, string trackUri)
+        private static UBaseObject CreateUBaseObject(XElement? container, string? trackUri)
         {
             ArgumentNullException.ThrowIfNull(container);
 
@@ -972,7 +940,7 @@ namespace Emby.Dlna.PlayTo
             return new string[4];
         }
 
-        private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
+        private async Task<TransportCommands?> GetAVProtocolAsync(CancellationToken cancellationToken)
         {
             if (AvCommands is not null)
             {
@@ -1004,7 +972,7 @@ namespace Emby.Dlna.PlayTo
             return AvCommands;
         }
 
-        private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
+        private async Task<TransportCommands?> GetRenderingProtocolAsync(CancellationToken cancellationToken)
         {
             if (RendererCommands is not null)
             {
@@ -1054,7 +1022,7 @@ namespace Emby.Dlna.PlayTo
             return baseUrl + url;
         }
 
-        public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
+        public static async Task<Device?> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
         {
             var ssdpHttpClient = new DlnaHttpClient(logger, httpClientFactory);
 
@@ -1287,7 +1255,7 @@ namespace Emby.Dlna.PlayTo
             }
 
             _timer = null;
-            Properties = null;
+            Properties = null!;
 
             _disposed = true;
         }

+ 2 - 2
Emby.Dlna/PlayTo/PlayToController.cs

@@ -42,7 +42,7 @@ namespace Emby.Dlna.PlayTo
 
         private readonly IDeviceDiscovery _deviceDiscovery;
         private readonly string _serverAddress;
-        private readonly string _accessToken;
+        private readonly string? _accessToken;
 
         private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
         private Device _device;
@@ -59,7 +59,7 @@ namespace Emby.Dlna.PlayTo
             IUserManager userManager,
             IImageProcessor imageProcessor,
             string serverAddress,
-            string accessToken,
+            string? accessToken,
             IDeviceDiscovery deviceDiscovery,
             IUserDataManager userDataManager,
             ILocalizationManager localization,

+ 4 - 6
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 
 using System;
@@ -67,7 +65,7 @@ namespace Emby.Dlna.PlayTo
             _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
         }
 
-        private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
+        private async void OnDeviceDiscoveryDeviceDiscovered(object? sender, GenericEventArgs<UpnpDeviceInfo> e)
         {
             if (_disposed)
             {
@@ -76,12 +74,12 @@ namespace Emby.Dlna.PlayTo
 
             var info = e.Argument;
 
-            if (!info.Headers.TryGetValue("USN", out string usn))
+            if (!info.Headers.TryGetValue("USN", out string? usn))
             {
                 usn = string.Empty;
             }
 
-            if (!info.Headers.TryGetValue("NT", out string nt))
+            if (!info.Headers.TryGetValue("NT", out string? nt))
             {
                 nt = string.Empty;
             }
@@ -161,7 +159,7 @@ namespace Emby.Dlna.PlayTo
             var uri = info.Location;
             _logger.LogDebug("Attempting to create PlayToController from location {0}", uri);
 
-            if (info.Headers.TryGetValue("USN", out string uuid))
+            if (info.Headers.TryGetValue("USN", out string? uuid))
             {
                 uuid = GetUuid(uuid);
             }

+ 1 - 1
Emby.Server.Implementations/Plugins/PluginManager.cs

@@ -677,7 +677,7 @@ namespace Emby.Server.Implementations.Plugins
                 }
                 catch (JsonException ex)
                 {
-                    _logger.LogError(ex, "Error deserializing {Json}.", Encoding.UTF8.GetString(data!));
+                    _logger.LogError(ex, "Error deserializing {Json}.", Encoding.UTF8.GetString(data));
                 }
 
                 if (manifest is not null)

+ 2 - 4
MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs

@@ -1,5 +1,3 @@
-#nullable disable
-
 #pragma warning disable CS1591
 
 using MediaBrowser.Controller.Entities;
@@ -9,12 +7,12 @@ namespace MediaBrowser.Controller.Drawing
 {
     public static class ImageProcessorExtensions
     {
-        public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType)
+        public static string? GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType)
         {
             return processor.GetImageCacheTag(item, imageType, 0);
         }
 
-        public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType, int imageIndex)
+        public static string? GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType, int imageIndex)
         {
             var imageInfo = item.GetImageInfo(imageType, imageIndex);
 

+ 4 - 4
MediaBrowser.Model/Dlna/DeviceProfile.cs

@@ -314,7 +314,7 @@ namespace MediaBrowser.Model.Dlna
         /// <param name="audioSampleRate">The audio sample rate.</param>
         /// <param name="audioBitDepth">The audio bit depth.</param>
         /// <returns>The <see cref="ResponseProfile"/>.</returns>
-        public ResponseProfile? GetAudioMediaProfile(string container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
+        public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
         {
             foreach (var i in ResponseProfiles)
             {
@@ -438,14 +438,14 @@ namespace MediaBrowser.Model.Dlna
         /// <param name="isAvc">True if Avc.</param>
         /// <returns>The <see cref="ResponseProfile"/>.</returns>
         public ResponseProfile? GetVideoMediaProfile(
-            string container,
+            string? container,
             string? audioCodec,
             string? videoCodec,
             int? width,
             int? height,
             int? bitDepth,
             int? videoBitrate,
-            string videoProfile,
+            string? videoProfile,
             VideoRangeType videoRangeType,
             double? videoLevel,
             float? videoFramerate,
@@ -456,7 +456,7 @@ namespace MediaBrowser.Model.Dlna
             int? refFrames,
             int? numVideoStreams,
             int? numAudioStreams,
-            string videoCodecTag,
+            string? videoCodecTag,
             bool? isAvc)
         {
             foreach (var i in ResponseProfiles)

+ 13 - 18
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -179,15 +179,9 @@ namespace MediaBrowser.Model.Dlna
         {
             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))
-                {
-                    mediaSources.Add(mediaSourceInfo);
-                }
-            }
+            var mediaSources = string.IsNullOrEmpty(options.MediaSourceId)
+                ? options.MediaSources
+                : options.MediaSources.Where(x => string.Equals(x.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase));
 
             var streams = new List<StreamInfo>();
             foreach (var mediaSourceInfo in mediaSources)
@@ -216,7 +210,7 @@ namespace MediaBrowser.Model.Dlna
             return streams.OrderBy(i =>
             {
                 // Nothing beats direct playing a file
-                if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File)
+                if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource?.Protocol == MediaProtocol.File)
                 {
                     return 0;
                 }
@@ -235,7 +229,7 @@ namespace MediaBrowser.Model.Dlna
                 }
             }).ThenBy(i =>
             {
-                switch (i.MediaSource.Protocol)
+                switch (i.MediaSource?.Protocol)
                 {
                     case MediaProtocol.File:
                         return 0;
@@ -246,7 +240,7 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (maxBitrate > 0)
                 {
-                    if (i.MediaSource.Bitrate.HasValue)
+                    if (i.MediaSource?.Bitrate is not null)
                     {
                         return Math.Abs(i.MediaSource.Bitrate.Value - maxBitrate);
                     }
@@ -585,10 +579,10 @@ namespace MediaBrowser.Model.Dlna
                 MediaSource = item,
                 RunTimeTicks = item.RunTimeTicks,
                 Context = options.Context,
-                DeviceProfile = options.Profile
+                DeviceProfile = options.Profile,
+                SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles)
             };
 
-            playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles);
             var subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null;
 
             var audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex);
@@ -659,7 +653,8 @@ namespace MediaBrowser.Model.Dlna
                         if (audioStreamIndex.HasValue)
                         {
                             playlistItem.AudioStreamIndex = audioStreamIndex;
-                            playlistItem.AudioCodecs = new[] { item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value)?.Codec };
+                            var audioCodec = item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value)?.Codec;
+                            playlistItem.AudioCodecs = audioCodec is null ? Array.Empty<string>() : new[] { audioCodec };
                         }
                     }
                     else if (directPlay == PlayMethod.DirectStream)
@@ -842,7 +837,7 @@ namespace MediaBrowser.Model.Dlna
 
             if (videoStream is not null && videoStream.Level != 0)
             {
-                playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
+                playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString() ?? string.Empty);
             }
 
             // Prefer matching audio codecs, could do better here
@@ -871,7 +866,7 @@ namespace MediaBrowser.Model.Dlna
 
                 // Copy matching audio codec options
                 playlistItem.AudioSampleRate = audioStream.SampleRate;
-                playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString());
+                playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString() ?? string.Empty);
 
                 if (!string.IsNullOrEmpty(audioStream.Profile))
                 {
@@ -880,7 +875,7 @@ namespace MediaBrowser.Model.Dlna
 
                 if (audioStream.Level != 0)
                 {
-                    playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString());
+                    playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString() ?? string.Empty);
                 }
             }
 

+ 52 - 38
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -1,9 +1,9 @@
-#nullable disable
 #pragma warning disable CS1591
 
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+using System.Linq;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Dto;
@@ -34,9 +34,9 @@ namespace MediaBrowser.Model.Dlna
 
         public DlnaProfileType MediaType { get; set; }
 
-        public string Container { get; set; }
+        public string? Container { get; set; }
 
-        public string SubProtocol { get; set; }
+        public string? SubProtocol { get; set; }
 
         public long StartPositionTicks { get; set; }
 
@@ -80,11 +80,11 @@ namespace MediaBrowser.Model.Dlna
 
         public float? MaxFramerate { get; set; }
 
-        public DeviceProfile DeviceProfile { get; set; }
+        public required DeviceProfile DeviceProfile { get; set; }
 
-        public string DeviceProfileId { get; set; }
+        public string? DeviceProfileId { get; set; }
 
-        public string DeviceId { get; set; }
+        public string? DeviceId { get; set; }
 
         public long? RunTimeTicks { get; set; }
 
@@ -92,21 +92,21 @@ namespace MediaBrowser.Model.Dlna
 
         public bool EstimateContentLength { get; set; }
 
-        public MediaSourceInfo MediaSource { get; set; }
+        public MediaSourceInfo? MediaSource { get; set; }
 
         public string[] SubtitleCodecs { get; set; }
 
         public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
 
-        public string SubtitleFormat { get; set; }
+        public string? SubtitleFormat { get; set; }
 
-        public string PlaySessionId { get; set; }
+        public string? PlaySessionId { get; set; }
 
         public TranscodeReason TranscodeReasons { get; set; }
 
         public Dictionary<string, string> StreamOptions { get; private set; }
 
-        public string MediaSourceId => MediaSource?.Id;
+        public string? MediaSourceId => MediaSource?.Id;
 
         public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay)
             && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay;
@@ -114,12 +114,12 @@ namespace MediaBrowser.Model.Dlna
         /// <summary>
         /// Gets the audio stream that will be used.
         /// </summary>
-        public MediaStream TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex);
+        public MediaStream? TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex);
 
         /// <summary>
         /// Gets the video stream that will be used.
         /// </summary>
-        public MediaStream TargetVideoStream => MediaSource?.VideoStream;
+        public MediaStream? TargetVideoStream => MediaSource?.VideoStream;
 
         /// <summary>
         /// Gets the audio sample rate that will be in the output stream.
@@ -259,7 +259,7 @@ namespace MediaBrowser.Model.Dlna
         /// <summary>
         /// Gets the audio sample rate that will be in the output stream.
         /// </summary>
-        public string TargetVideoProfile
+        public string? TargetVideoProfile
         {
             get
             {
@@ -307,7 +307,7 @@ namespace MediaBrowser.Model.Dlna
         /// Gets the target video codec tag.
         /// </summary>
         /// <value>The target video codec tag.</value>
-        public string TargetVideoCodecTag
+        public string? TargetVideoCodecTag
         {
             get
             {
@@ -364,7 +364,7 @@ namespace MediaBrowser.Model.Dlna
             {
                 var stream = TargetAudioStream;
 
-                string inputCodec = stream?.Codec;
+                string? inputCodec = stream?.Codec;
 
                 if (IsDirectStream)
                 {
@@ -389,7 +389,7 @@ namespace MediaBrowser.Model.Dlna
             {
                 var stream = TargetVideoStream;
 
-                string inputCodec = stream?.Codec;
+                string? inputCodec = stream?.Codec;
 
                 if (IsDirectStream)
                 {
@@ -417,7 +417,7 @@ namespace MediaBrowser.Model.Dlna
             {
                 if (IsDirectStream)
                 {
-                    return MediaSource.Size;
+                    return MediaSource?.Size;
                 }
 
                 if (RunTimeTicks.HasValue)
@@ -580,7 +580,7 @@ namespace MediaBrowser.Model.Dlna
             }
         }
 
-        public void SetOption(string qualifier, string name, string value)
+        public void SetOption(string? qualifier, string name, string value)
         {
             if (string.IsNullOrEmpty(qualifier))
             {
@@ -597,7 +597,7 @@ namespace MediaBrowser.Model.Dlna
             StreamOptions[name] = value;
         }
 
-        public string GetOption(string qualifier, string name)
+        public string? GetOption(string? qualifier, string name)
         {
             var value = GetOption(qualifier + "-" + name);
 
@@ -609,7 +609,7 @@ namespace MediaBrowser.Model.Dlna
             return value;
         }
 
-        public string GetOption(string name)
+        public string? GetOption(string name)
         {
             if (StreamOptions.TryGetValue(name, out var value))
             {
@@ -619,7 +619,7 @@ namespace MediaBrowser.Model.Dlna
             return null;
         }
 
-        public string ToUrl(string baseUrl, string accessToken)
+        public string ToUrl(string baseUrl, string? accessToken)
         {
             ArgumentException.ThrowIfNullOrEmpty(baseUrl);
 
@@ -686,7 +686,7 @@ namespace MediaBrowser.Model.Dlna
             return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
         }
 
-        private static IEnumerable<NameValuePair> BuildParams(StreamInfo item, string accessToken)
+        private static IEnumerable<NameValuePair> BuildParams(StreamInfo item, string? accessToken)
         {
             var list = new List<NameValuePair>();
 
@@ -730,7 +730,7 @@ namespace MediaBrowser.Model.Dlna
             list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
             list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
 
-            string liveStreamId = item.MediaSource?.LiveStreamId;
+            string? liveStreamId = item.MediaSource?.LiveStreamId;
             list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
 
             list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
@@ -772,7 +772,7 @@ namespace MediaBrowser.Model.Dlna
                 list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
             }
 
-            list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
+            list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty));
 
             string subtitleCodecs = item.SubtitleCodecs.Length == 0 ?
                string.Empty :
@@ -816,13 +816,18 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string? accessToken)
         {
             return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
         }
 
-        public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string? accessToken)
         {
+            if (MediaSource is null)
+            {
+                return Enumerable.Empty<SubtitleStreamInfo>();
+            }
+
             var list = new List<SubtitleStreamInfo>();
 
             // HLS will preserve timestamps so we can just grab the full subtitle stream
@@ -856,27 +861,36 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
+        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string? accessToken, long startPositionTicks)
         {
             if (enableAllProfiles)
             {
                 foreach (var profile in DeviceProfile.SubtitleProfiles)
                 {
                     var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
-
-                    list.Add(info);
+                    if (info is not null)
+                    {
+                        list.Add(info);
+                    }
                 }
             }
             else
             {
                 var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
-
-                list.Add(info);
+                if (info is not null)
+                {
+                    list.Add(info);
+                }
             }
         }
 
-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
+        private SubtitleStreamInfo? GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string? accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
         {
+            if (MediaSource is null)
+            {
+                return null;
+            }
+
             var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
             var info = new SubtitleStreamInfo
             {
@@ -920,7 +934,7 @@ namespace MediaBrowser.Model.Dlna
             return info;
         }
 
-        public int? GetTargetVideoBitDepth(string codec)
+        public int? GetTargetVideoBitDepth(string? codec)
         {
             var value = GetOption(codec, "videobitdepth");
 
@@ -932,7 +946,7 @@ namespace MediaBrowser.Model.Dlna
             return null;
         }
 
-        public int? GetTargetAudioBitDepth(string codec)
+        public int? GetTargetAudioBitDepth(string? codec)
         {
             var value = GetOption(codec, "audiobitdepth");
 
@@ -944,7 +958,7 @@ namespace MediaBrowser.Model.Dlna
             return null;
         }
 
-        public double? GetTargetVideoLevel(string codec)
+        public double? GetTargetVideoLevel(string? codec)
         {
             var value = GetOption(codec, "level");
 
@@ -956,7 +970,7 @@ namespace MediaBrowser.Model.Dlna
             return null;
         }
 
-        public int? GetTargetRefFrames(string codec)
+        public int? GetTargetRefFrames(string? codec)
         {
             var value = GetOption(codec, "maxrefframes");
 
@@ -968,7 +982,7 @@ namespace MediaBrowser.Model.Dlna
             return null;
         }
 
-        public int? GetTargetAudioChannels(string codec)
+        public int? GetTargetAudioChannels(string? codec)
         {
             var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
 
@@ -988,7 +1002,7 @@ namespace MediaBrowser.Model.Dlna
 
         private int? GetMediaStreamCount(MediaStreamType type, int limit)
         {
-            var count = MediaSource.GetStreamCount(type);
+            var count = MediaSource?.GetStreamCount(type);
 
             if (count.HasValue)
             {

+ 10 - 10
tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs

@@ -351,11 +351,11 @@ namespace Jellyfin.Model.Tests
                 // Assert.Contains(uri.Extension, containers);
 
                 // Check expected video codec (1)
-                Assert.Contains(targetVideoStream.Codec, streamInfo.TargetVideoCodec);
+                Assert.Contains(targetVideoStream?.Codec, streamInfo.TargetVideoCodec);
                 Assert.Single(streamInfo.TargetVideoCodec);
 
                 // Check expected audio codecs (1)
-                Assert.Contains(targetAudioStream.Codec, streamInfo.TargetAudioCodec);
+                Assert.Contains(targetAudioStream?.Codec, streamInfo.TargetAudioCodec);
                 Assert.Single(streamInfo.TargetAudioCodec);
                 // Assert.Single(val.AudioCodecs);
 
@@ -410,13 +410,13 @@ namespace Jellyfin.Model.Tests
                 else
                 {
                     // Check expected video codec (1)
-                    Assert.Contains(targetVideoStream.Codec, streamInfo.TargetVideoCodec);
+                    Assert.Contains(targetVideoStream?.Codec, streamInfo.TargetVideoCodec);
                     Assert.Single(streamInfo.TargetVideoCodec);
 
                     if (transcodeMode.Equals("DirectStream", StringComparison.Ordinal))
                     {
                         // Check expected audio codecs (1)
-                        if (!targetAudioStream.IsExternal)
+                        if (targetAudioStream?.IsExternal == false)
                         {
                             // Check expected audio codecs (1)
                             if (streamInfo.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported))
@@ -432,7 +432,7 @@ namespace Jellyfin.Model.Tests
                     else if (transcodeMode.Equals("Remux", StringComparison.Ordinal))
                     {
                         // Check expected audio codecs (1)
-                        Assert.Contains(targetAudioStream.Codec, streamInfo.AudioCodecs);
+                        Assert.Contains(targetAudioStream?.Codec, streamInfo.AudioCodecs);
                         Assert.Single(streamInfo.AudioCodecs);
                     }
 
@@ -440,10 +440,10 @@ namespace Jellyfin.Model.Tests
                     var videoStream = targetVideoStream;
                     Assert.False(streamInfo.EstimateContentLength);
                     Assert.Equal(TranscodeSeekInfo.Auto, streamInfo.TranscodeSeekInfo);
-                    Assert.Contains(videoStream.Profile?.ToLowerInvariant() ?? string.Empty, streamInfo.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty<string>());
-                    Assert.Equal(videoStream.Level, streamInfo.TargetVideoLevel);
-                    Assert.Equal(videoStream.BitDepth, streamInfo.TargetVideoBitDepth);
-                    Assert.InRange(streamInfo.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue);
+                    Assert.Contains(videoStream?.Profile?.ToLowerInvariant() ?? string.Empty, streamInfo.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty<string>());
+                    Assert.Equal(videoStream?.Level, streamInfo.TargetVideoLevel);
+                    Assert.Equal(videoStream?.BitDepth, streamInfo.TargetVideoBitDepth);
+                    Assert.InRange(streamInfo.VideoBitrate.GetValueOrDefault(), videoStream?.BitRate.GetValueOrDefault() ?? 0, int.MaxValue);
 
                     // Audio codec not supported
                     if ((why & TranscodeReason.AudioCodecNotSupported) != 0)
@@ -452,7 +452,7 @@ namespace Jellyfin.Model.Tests
                         if (options.AudioStreamIndex >= 0)
                         {
                             // TODO:fixme
-                            if (!targetAudioStream.IsExternal)
+                            if (targetAudioStream?.IsExternal == false)
                             {
                                 Assert.DoesNotContain(targetAudioStream.Codec, streamInfo.AudioCodecs);
                             }