فهرست منبع

Move UniversalAudioService to Jellyfin.Api

David 4 سال پیش
والد
کامیت
a3dcca3826

+ 3 - 3
Jellyfin.Api/Controllers/AudioController.cs

@@ -197,8 +197,8 @@ namespace Jellyfin.Api.Controllers
             [FromQuery] string? transcodingReasons,
             [FromQuery] int? audioStreamIndex,
             [FromQuery] int? videoStreamIndex,
-            [FromQuery] EncodingContext context,
-            [FromQuery] Dictionary<string, string> streamOptions)
+            [FromQuery] EncodingContext? context,
+            [FromQuery] Dictionary<string, string>? streamOptions)
         {
             bool isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
 
@@ -253,7 +253,7 @@ namespace Jellyfin.Api.Controllers
                 TranscodeReasons = transcodingReasons,
                 AudioStreamIndex = audioStreamIndex,
                 VideoStreamIndex = videoStreamIndex,
-                Context = context,
+                Context = context ?? EncodingContext.Static,
                 StreamOptions = streamOptions
             };
 

+ 366 - 0
Jellyfin.Api/Controllers/UniversalAudioController.cs

@@ -0,0 +1,366 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Jellyfin.Api.Helpers;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Api.Controllers
+{
+    /// <summary>
+    /// The universal audio controller.
+    /// </summary>
+    public class UniversalAudioController : BaseJellyfinApiController
+    {
+        private readonly ILoggerFactory _loggerFactory;
+        private readonly IUserManager _userManager;
+        private readonly ILibraryManager _libraryManager;
+        private readonly IDeviceManager _deviceManager;
+        private readonly IDlnaManager _dlnaManager;
+        private readonly IMediaEncoder _mediaEncoder;
+        private readonly IFileSystem _fileSystem;
+        private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IAuthorizationContext _authorizationContext;
+        private readonly INetworkManager _networkManager;
+        private readonly IServerConfigurationManager _serverConfigurationManager;
+        private readonly TranscodingJobHelper _transcodingJobHelper;
+        private readonly IConfiguration _configuration;
+        private readonly ISubtitleEncoder _subtitleEncoder;
+        private readonly IStreamHelper _streamHelper;
+
+        public UniversalAudioController(
+            ILoggerFactory loggerFactory,
+            IServerConfigurationManager serverConfigurationManager,
+            IUserManager userManager,
+            ILibraryManager libraryManager,
+            IMediaEncoder mediaEncoder,
+            IFileSystem fileSystem,
+            IDlnaManager dlnaManager,
+            IDeviceManager deviceManager,
+            IMediaSourceManager mediaSourceManager,
+            IAuthorizationContext authorizationContext,
+            INetworkManager networkManager,
+            TranscodingJobHelper transcodingJobHelper,
+            IConfiguration configuration,
+            ISubtitleEncoder subtitleEncoder,
+            IStreamHelper streamHelper)
+        {
+            _userManager = userManager;
+            _libraryManager = libraryManager;
+            _mediaEncoder = mediaEncoder;
+            _fileSystem = fileSystem;
+            _dlnaManager = dlnaManager;
+            _deviceManager = deviceManager;
+            _mediaSourceManager = mediaSourceManager;
+            _authorizationContext = authorizationContext;
+            _networkManager = networkManager;
+            _loggerFactory = loggerFactory;
+            _serverConfigurationManager = serverConfigurationManager;
+            _transcodingJobHelper = transcodingJobHelper;
+            _configuration = configuration;
+            _subtitleEncoder = subtitleEncoder;
+            _streamHelper = streamHelper;
+        }
+
+        [HttpGet("/Audio/{itemId}/universal")]
+        [HttpGet("/Audio/{itemId}/{universal=universal}.{container?}")]
+        [HttpHead("/Audio/{itemId}/universal")]
+        [HttpHead("/Audio/{itemId}/{universal=universal}.{container?}")]
+        public async Task<ActionResult> GetUniversalAudioStream(
+            [FromRoute] Guid itemId,
+            [FromRoute] string? container,
+            [FromQuery] string? mediaSourceId,
+            [FromQuery] string? deviceId,
+            [FromQuery] Guid? userId,
+            [FromQuery] string? audioCodec,
+            [FromQuery] int? maxAudioChannels,
+            [FromQuery] int? transcodingAudioChannels,
+            [FromQuery] long? maxStreamingBitrate,
+            [FromQuery] long? startTimeTicks,
+            [FromQuery] string? transcodingContainer,
+            [FromQuery] string? transcodingProtocol,
+            [FromQuery] int? maxAudioSampleRate,
+            [FromQuery] int? maxAudioBitDepth,
+            [FromQuery] bool? enableRemoteMedia,
+            [FromQuery] bool breakOnNonKeyFrames,
+            [FromQuery] bool enableRedirection = true)
+        {
+            bool isHeadRequest = Request.Method == System.Net.WebRequestMethods.Http.Head;
+            var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels);
+            _authorizationContext.GetAuthorizationInfo(Request).DeviceId = deviceId;
+
+            var mediaInfoController = new MediaInfoController(_mediaSourceManager, _deviceManager, _libraryManager, _networkManager, _mediaEncoder, _userManager, _authorizationContext, _loggerFactory.CreateLogger<MediaInfoController>(), _serverConfigurationManager);
+            var playbackInfoResult = await mediaInfoController.GetPlaybackInfo(itemId, userId).ConfigureAwait(false);
+            var mediaSource = playbackInfoResult.Value.MediaSources[0];
+
+            if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
+            {
+                if (enableRedirection)
+                {
+                    if (mediaSource.IsRemote && enableRemoteMedia.HasValue && enableRemoteMedia.Value)
+                    {
+                        return Redirect(mediaSource.Path);
+                    }
+                }
+            }
+
+            var isStatic = mediaSource.SupportsDirectStream;
+            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+            {
+                // TODO new DynamicHlsController
+                // var dynamicHlsController = new DynamicHlsController();
+                var transcodingProfile = deviceProfile.TranscodingProfiles[0];
+
+                // hls segment container can only be mpegts or fmp4 per ffmpeg documentation
+                // TODO: remove this when we switch back to the segment muxer
+                var supportedHLSContainers = new[] { "mpegts", "fmp4" };
+
+                /*
+                var newRequest = new GetMasterHlsAudioPlaylist
+                {
+                    AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
+                    AudioCodec = transcodingProfile.AudioCodec,
+                    Container = ".m3u8",
+                    DeviceId = request.DeviceId,
+                    Id = request.Id,
+                    MaxAudioChannels = request.MaxAudioChannels,
+                    MediaSourceId = mediaSource.Id,
+                    PlaySessionId = playbackInfoResult.PlaySessionId,
+                    StartTimeTicks = request.StartTimeTicks,
+                    Static = isStatic,
+                    // fallback to mpegts if device reports some weird value unsupported by hls
+                    SegmentContainer = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts",
+                    AudioSampleRate = request.MaxAudioSampleRate,
+                    MaxAudioBitDepth = request.MaxAudioBitDepth,
+                    BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
+                    TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
+                };
+
+                if (isHeadRequest)
+                {
+                    audioController.Request.Method = HttpMethod.Head.Method;
+                    return await service.Head(newRequest).ConfigureAwait(false);
+                }
+
+                return await service.Get(newRequest).ConfigureAwait(false);*/
+                // TODO remove this line
+                return Content(string.Empty);
+            }
+            else
+            {
+                var audioController = new AudioController(
+                    _dlnaManager,
+                    _userManager,
+                    _authorizationContext,
+                    _libraryManager,
+                    _mediaSourceManager,
+                    _serverConfigurationManager,
+                    _mediaEncoder,
+                    _streamHelper,
+                    _fileSystem,
+                    _subtitleEncoder,
+                    _configuration,
+                    _deviceManager,
+                    _transcodingJobHelper,
+                    // TODO HttpClient
+                    new HttpClient());
+
+                if (isHeadRequest)
+                {
+                    audioController.Request.Method = HttpMethod.Head.Method;
+                    return await audioController.GetAudioStream(
+                            itemId,
+                            isStatic ? null : ("." + mediaSource.TranscodingContainer),
+                            isStatic,
+                            null,
+                            null,
+                            null,
+                            playbackInfoResult.Value.PlaySessionId,
+                            null,
+                            null,
+                            null,
+                            mediaSource.Id,
+                            deviceId,
+                            audioCodec,
+                            null,
+                            null,
+                            null,
+                            breakOnNonKeyFrames,
+                            maxAudioSampleRate,
+                            maxAudioBitDepth,
+                            isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)),
+                            null,
+                            maxAudioChannels,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            startTimeTicks,
+                            null,
+                            null,
+                            null,
+                            null,
+                            SubtitleDeliveryMethod.Embed,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            null,
+                            mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
+                            null,
+                            null,
+                            null,
+                            null)
+                        .ConfigureAwait(false);
+                }
+
+                return await audioController.GetAudioStream(
+                        itemId,
+                        isStatic ? null : ("." + mediaSource.TranscodingContainer),
+                        isStatic,
+                        null,
+                        null,
+                        null,
+                        playbackInfoResult.Value.PlaySessionId,
+                        null,
+                        null,
+                        null,
+                        mediaSource.Id,
+                        deviceId,
+                        audioCodec,
+                        null,
+                        null,
+                        null,
+                        breakOnNonKeyFrames,
+                        maxAudioSampleRate,
+                        maxAudioBitDepth,
+                        isStatic ? (int?)null : Convert.ToInt32(Math.Min(maxStreamingBitrate ?? 192000, int.MaxValue)),
+                        null,
+                        maxAudioChannels,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        startTimeTicks,
+                        null,
+                        null,
+                        null,
+                        null,
+                        SubtitleDeliveryMethod.Embed,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray()),
+                        null,
+                        null,
+                        null,
+                        null)
+                    .ConfigureAwait(false);
+            }
+        }
+
+        private DeviceProfile GetDeviceProfile(
+            string? container,
+            string? transcodingContainer,
+            string? audioCodec,
+            string? transcodingProtocol,
+            bool? breakOnNonKeyFrames,
+            int? transcodingAudioChannels,
+            int? maxAudioSampleRate,
+            int? maxAudioBitDepth,
+            int? maxAudioChannels)
+        {
+            var deviceProfile = new DeviceProfile();
+
+            var directPlayProfiles = new List<DirectPlayProfile>();
+
+            var containers = (container ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+            foreach (var cont in containers)
+            {
+                var parts = cont.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+
+                var audioCodecs = parts.Length == 1 ? null : string.Join(",", parts.Skip(1).ToArray());
+
+                directPlayProfiles.Add(new DirectPlayProfile { Type = DlnaProfileType.Audio, Container = parts[0], AudioCodec = audioCodecs });
+            }
+
+            deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
+
+            deviceProfile.TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Type = DlnaProfileType.Audio,
+                    Context = EncodingContext.Streaming,
+                    Container = transcodingContainer,
+                    AudioCodec = audioCodec,
+                    Protocol = transcodingProtocol,
+                    BreakOnNonKeyFrames = breakOnNonKeyFrames ?? false,
+                    MaxAudioChannels = transcodingAudioChannels?.ToString(CultureInfo.InvariantCulture)
+                }
+            };
+
+            var codecProfiles = new List<CodecProfile>();
+            var conditions = new List<ProfileCondition>();
+
+            if (maxAudioSampleRate.HasValue)
+            {
+                // codec profile
+                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioSampleRate, Value = maxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) });
+            }
+
+            if (maxAudioBitDepth.HasValue)
+            {
+                // codec profile
+                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioBitDepth, Value = maxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture) });
+            }
+
+            if (maxAudioChannels.HasValue)
+            {
+                // codec profile
+                conditions.Add(new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, IsRequired = false, Property = ProfileConditionValue.AudioChannels, Value = maxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) });
+            }
+
+            if (conditions.Count > 0)
+            {
+                // codec profile
+                codecProfiles.Add(new CodecProfile { Type = CodecType.Audio, Container = container, Conditions = conditions.ToArray() });
+            }
+
+            deviceProfile.CodecProfiles = codecProfiles.ToArray();
+
+            return deviceProfile;
+        }
+    }
+}

+ 0 - 674
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -1,674 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable SA1402
-#pragma warning disable SA1649
-
-using System;
-using System.Buffers;
-using System.Globalization;
-using System.Linq;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Services;
-using MediaBrowser.Model.Session;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api.Playback
-{
-    public class GetPlaybackInfo : IReturn<PlaybackInfoResponse>
-    {
-        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid Id { get; set; }
-
-        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public Guid UserId { get; set; }
-    }
-
-    public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse>
-    {
-    }
-
-    public class OpenMediaSource : LiveStreamRequest, IReturn<LiveStreamResponse>
-    {
-    }
-
-    public class CloseMediaSource : IReturnVoid
-    {
-        [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public string LiveStreamId { get; set; }
-    }
-
-    public class GetBitrateTestBytes
-    {
-        [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int Size { get; set; }
-
-        public GetBitrateTestBytes()
-        {
-            // 100k
-            Size = 102400;
-        }
-    }
-
-    [Authenticated]
-    public class MediaInfoService : BaseApiService
-    {
-        private readonly IMediaSourceManager _mediaSourceManager;
-        private readonly IDeviceManager _deviceManager;
-        private readonly ILibraryManager _libraryManager;
-        private readonly INetworkManager _networkManager;
-        private readonly IMediaEncoder _mediaEncoder;
-        private readonly IUserManager _userManager;
-        private readonly IAuthorizationContext _authContext;
-
-        public MediaInfoService(
-            ILogger<MediaInfoService> logger,
-            IServerConfigurationManager serverConfigurationManager,
-            IHttpResultFactory httpResultFactory,
-            IMediaSourceManager mediaSourceManager,
-            IDeviceManager deviceManager,
-            ILibraryManager libraryManager,
-            INetworkManager networkManager,
-            IMediaEncoder mediaEncoder,
-            IUserManager userManager,
-            IAuthorizationContext authContext)
-            : base(logger, serverConfigurationManager, httpResultFactory)
-        {
-            _mediaSourceManager = mediaSourceManager;
-            _deviceManager = deviceManager;
-            _libraryManager = libraryManager;
-            _networkManager = networkManager;
-            _mediaEncoder = mediaEncoder;
-            _userManager = userManager;
-            _authContext = authContext;
-        }
-
-        public object Get(GetBitrateTestBytes request)
-        {
-            const int MaxSize = 10_000_000;
-
-            var size = request.Size;
-
-            if (size <= 0)
-            {
-                throw new ArgumentException($"The requested size ({size}) is equal to or smaller than 0.", nameof(request));
-            }
-
-            if (size > MaxSize)
-            {
-                throw new ArgumentException($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).", nameof(request));
-            }
-
-            byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
-            try
-            {
-                new Random().NextBytes(buffer);
-                return ResultFactory.GetResult(null, buffer, "application/octet-stream");
-            }
-            finally
-            {
-                ArrayPool<byte>.Shared.Return(buffer);
-            }
-        }
-
-        public async Task<object> Get(GetPlaybackInfo request)
-        {
-            var result = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }).ConfigureAwait(false);
-            return ToOptimizedResult(result);
-        }
-
-        public async Task<object> Post(OpenMediaSource request)
-        {
-            var result = await OpenMediaSource(request).ConfigureAwait(false);
-
-            return ToOptimizedResult(result);
-        }
-
-        private async Task<LiveStreamResponse> OpenMediaSource(OpenMediaSource request)
-        {
-            var authInfo = _authContext.GetAuthorizationInfo(Request);
-
-            var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false);
-
-            var profile = request.DeviceProfile;
-            if (profile == null)
-            {
-                var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
-                if (caps != null)
-                {
-                    profile = caps.DeviceProfile;
-                }
-            }
-
-            if (profile != null)
-            {
-                var item = _libraryManager.GetItemById(request.ItemId);
-
-                SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
-                    request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
-                    request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, true, true, true);
-            }
-            else
-            {
-                if (!string.IsNullOrWhiteSpace(result.MediaSource.TranscodingUrl))
-                {
-                    result.MediaSource.TranscodingUrl += "&LiveStreamId=" + result.MediaSource.LiveStreamId;
-                }
-            }
-
-            if (result.MediaSource != null)
-            {
-                NormalizeMediaSourceContainer(result.MediaSource, profile, DlnaProfileType.Video);
-            }
-
-            return result;
-        }
-
-        public void Post(CloseMediaSource request)
-        {
-            _mediaSourceManager.CloseLiveStream(request.LiveStreamId).GetAwaiter().GetResult();
-        }
-
-        public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
-        {
-            var authInfo = _authContext.GetAuthorizationInfo(Request);
-
-            var profile = request.DeviceProfile;
-
-            Logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile);
-
-            if (profile == null)
-            {
-                var caps = _deviceManager.GetCapabilities(authInfo.DeviceId);
-                if (caps != null)
-                {
-                    profile = caps.DeviceProfile;
-                }
-            }
-
-            var info = await GetPlaybackInfo(request.Id, request.UserId, new[] { MediaType.Audio, MediaType.Video }, request.MediaSourceId, request.LiveStreamId).ConfigureAwait(false);
-
-            if (profile != null)
-            {
-                var mediaSourceId = request.MediaSourceId;
-
-                SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, true, request.EnableDirectStream, request.EnableTranscoding, request.AllowVideoStreamCopy, request.AllowAudioStreamCopy);
-            }
-
-            if (request.AutoOpenLiveStream)
-            {
-                var mediaSource = string.IsNullOrWhiteSpace(request.MediaSourceId) ? info.MediaSources.FirstOrDefault() : info.MediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId, StringComparison.Ordinal));
-
-                if (mediaSource != null && mediaSource.RequiresOpening && string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
-                {
-                    var openStreamResult = await OpenMediaSource(new OpenMediaSource
-                    {
-                        AudioStreamIndex = request.AudioStreamIndex,
-                        DeviceProfile = request.DeviceProfile,
-                        EnableDirectPlay = request.EnableDirectPlay,
-                        EnableDirectStream = request.EnableDirectStream,
-                        ItemId = request.Id,
-                        MaxAudioChannels = request.MaxAudioChannels,
-                        MaxStreamingBitrate = request.MaxStreamingBitrate,
-                        PlaySessionId = info.PlaySessionId,
-                        StartTimeTicks = request.StartTimeTicks,
-                        SubtitleStreamIndex = request.SubtitleStreamIndex,
-                        UserId = request.UserId,
-                        OpenToken = mediaSource.OpenToken
-                    }).ConfigureAwait(false);
-
-                    info.MediaSources = new[] { openStreamResult.MediaSource };
-                }
-            }
-
-            if (info.MediaSources != null)
-            {
-                foreach (var mediaSource in info.MediaSources)
-                {
-                    NormalizeMediaSourceContainer(mediaSource, profile, DlnaProfileType.Video);
-                }
-            }
-
-            return info;
-        }
-
-        private void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type)
-        {
-            mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type);
-        }
-
-        public async Task<object> Post(GetPostedPlaybackInfo request)
-        {
-            var result = await GetPlaybackInfo(request).ConfigureAwait(false);
-
-            return ToOptimizedResult(result);
-        }
-
-        private async Task<PlaybackInfoResponse> GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null)
-        {
-            var user = _userManager.GetUserById(userId);
-            var item = _libraryManager.GetItemById(id);
-            var result = new PlaybackInfoResponse();
-
-            MediaSourceInfo[] mediaSources;
-            if (string.IsNullOrWhiteSpace(liveStreamId))
-            {
-
-                // TODO handle supportedLiveMediaTypes?
-                var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false);
-
-                if (string.IsNullOrWhiteSpace(mediaSourceId))
-                {
-                    mediaSources = mediaSourcesList.ToArray();
-                }
-                else
-                {
-                    mediaSources = mediaSourcesList
-                        .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
-                        .ToArray();
-                }
-            }
-            else
-            {
-                var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
-
-                mediaSources = new[] { mediaSource };
-            }
-
-            if (mediaSources.Length == 0)
-            {
-                result.MediaSources = Array.Empty<MediaSourceInfo>();
-
-                if (!result.ErrorCode.HasValue)
-                {
-                    result.ErrorCode = PlaybackErrorCode.NoCompatibleStream;
-                }
-            }
-            else
-            {
-                // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it
-                // Should we move this directly into MediaSourceManager?
-                result.MediaSources = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources));
-
-                result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
-            }
-
-            return result;
-        }
-
-        private void SetDeviceSpecificData(
-            Guid itemId,
-            PlaybackInfoResponse result,
-            DeviceProfile profile,
-            AuthorizationInfo auth,
-            long? maxBitrate,
-            long startTimeTicks,
-            string mediaSourceId,
-            int? audioStreamIndex,
-            int? subtitleStreamIndex,
-            int? maxAudioChannels,
-            Guid userId,
-            bool enableDirectPlay,
-            bool forceDirectPlayRemoteMediaSource,
-            bool enableDirectStream,
-            bool enableTranscoding,
-            bool allowVideoStreamCopy,
-            bool allowAudioStreamCopy)
-        {
-            var item = _libraryManager.GetItemById(itemId);
-
-            foreach (var mediaSource in result.MediaSources)
-            {
-                SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId, enableDirectPlay, forceDirectPlayRemoteMediaSource, enableDirectStream, enableTranscoding, allowVideoStreamCopy, allowAudioStreamCopy);
-            }
-
-            SortMediaSources(result, maxBitrate);
-        }
-
-        private void SetDeviceSpecificData(
-            BaseItem item,
-            MediaSourceInfo mediaSource,
-            DeviceProfile profile,
-            AuthorizationInfo auth,
-            long? maxBitrate,
-            long startTimeTicks,
-            string mediaSourceId,
-            int? audioStreamIndex,
-            int? subtitleStreamIndex,
-            int? maxAudioChannels,
-            string playSessionId,
-            Guid userId,
-            bool enableDirectPlay,
-            bool forceDirectPlayRemoteMediaSource,
-            bool enableDirectStream,
-            bool enableTranscoding,
-            bool allowVideoStreamCopy,
-            bool allowAudioStreamCopy)
-        {
-            var streamBuilder = new StreamBuilder(_mediaEncoder, Logger);
-
-            var options = new VideoOptions
-            {
-                MediaSources = new[] { mediaSource },
-                Context = EncodingContext.Streaming,
-                DeviceId = auth.DeviceId,
-                ItemId = item.Id,
-                Profile = profile,
-                MaxAudioChannels = maxAudioChannels
-            };
-
-            if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
-            {
-                options.MediaSourceId = mediaSourceId;
-                options.AudioStreamIndex = audioStreamIndex;
-                options.SubtitleStreamIndex = subtitleStreamIndex;
-            }
-
-            var user = _userManager.GetUserById(userId);
-
-            if (!enableDirectPlay)
-            {
-                mediaSource.SupportsDirectPlay = false;
-            }
-
-            if (!enableDirectStream)
-            {
-                mediaSource.SupportsDirectStream = false;
-            }
-
-            if (!enableTranscoding)
-            {
-                mediaSource.SupportsTranscoding = false;
-            }
-
-            if (item is Audio)
-            {
-                Logger.LogInformation(
-                    "User policy for {0}. EnableAudioPlaybackTranscoding: {1}",
-                    user.Username,
-                    user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
-            }
-            else
-            {
-                Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
-                    user.Username,
-                    user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
-                    user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
-                    user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
-            }
-
-            // Beginning of Playback Determination: Attempt DirectPlay first
-            if (mediaSource.SupportsDirectPlay)
-            {
-                if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
-                {
-                    mediaSource.SupportsDirectPlay = false;
-                }
-                else
-                {
-                    var supportsDirectStream = mediaSource.SupportsDirectStream;
-
-                    // Dummy this up to fool StreamBuilder
-                    mediaSource.SupportsDirectStream = true;
-                    options.MaxBitrate = maxBitrate;
-
-                    if (item is Audio)
-                    {
-                        if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
-                        {
-                            options.ForceDirectPlay = true;
-                        }
-                    }
-                    else if (item is Video)
-                    {
-                        if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
-                            && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
-                            && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
-                        {
-                            options.ForceDirectPlay = true;
-                        }
-                    }
-
-                    // The MediaSource supports direct stream, now test to see if the client supports it
-                    var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
-                        ? streamBuilder.BuildAudioItem(options)
-                        : streamBuilder.BuildVideoItem(options);
-
-                    if (streamInfo == null || !streamInfo.IsDirectStream)
-                    {
-                        mediaSource.SupportsDirectPlay = false;
-                    }
-
-                    // Set this back to what it was
-                    mediaSource.SupportsDirectStream = supportsDirectStream;
-
-                    if (streamInfo != null)
-                    {
-                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
-                    }
-                }
-            }
-
-            if (mediaSource.SupportsDirectStream)
-            {
-                if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
-                {
-                    mediaSource.SupportsDirectStream = false;
-                }
-                else
-                {
-                    options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
-
-                    if (item is Audio)
-                    {
-                        if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
-                        {
-                            options.ForceDirectStream = true;
-                        }
-                    }
-                    else if (item is Video)
-                    {
-                        if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
-                            && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
-                            && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
-                        {
-                            options.ForceDirectStream = true;
-                        }
-                    }
-
-                    // The MediaSource supports direct stream, now test to see if the client supports it
-                    var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
-                        ? streamBuilder.BuildAudioItem(options)
-                        : streamBuilder.BuildVideoItem(options);
-
-                    if (streamInfo == null || !streamInfo.IsDirectStream)
-                    {
-                        mediaSource.SupportsDirectStream = false;
-                    }
-
-                    if (streamInfo != null)
-                    {
-                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
-                    }
-                }
-            }
-
-            if (mediaSource.SupportsTranscoding)
-            {
-                options.MaxBitrate = GetMaxBitrate(maxBitrate, user);
-
-                // The MediaSource supports direct stream, now test to see if the client supports it
-                var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
-                    ? streamBuilder.BuildAudioItem(options)
-                    : streamBuilder.BuildVideoItem(options);
-
-                if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
-                {
-                    if (streamInfo != null)
-                    {
-                        streamInfo.PlaySessionId = playSessionId;
-                        streamInfo.StartPositionTicks = startTimeTicks;
-                        mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
-                        mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
-                        mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
-                        mediaSource.TranscodingContainer = streamInfo.Container;
-                        mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
-
-                        // Do this after the above so that StartPositionTicks is set
-                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
-                    }
-                }
-                else
-                {
-                    if (streamInfo != null)
-                    {
-                        streamInfo.PlaySessionId = playSessionId;
-
-                        if (streamInfo.PlayMethod == PlayMethod.Transcode)
-                        {
-                            streamInfo.StartPositionTicks = startTimeTicks;
-                            mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-');
-
-                            if (!allowVideoStreamCopy)
-                            {
-                                mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
-                            }
-
-                            if (!allowAudioStreamCopy)
-                            {
-                                mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
-                            }
-
-                            mediaSource.TranscodingContainer = streamInfo.Container;
-                            mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
-                        }
-
-                        if (!allowAudioStreamCopy)
-                        {
-                            mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
-                        }
-
-                        mediaSource.TranscodingContainer = streamInfo.Container;
-                        mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
-
-                        // Do this after the above so that StartPositionTicks is set
-                        SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
-                    }
-                }
-            }
-
-            foreach (var attachment in mediaSource.MediaAttachments)
-            {
-                attachment.DeliveryUrl = string.Format(
-                    CultureInfo.InvariantCulture,
-                    "/Videos/{0}/{1}/Attachments/{2}",
-                    item.Id,
-                    mediaSource.Id,
-                    attachment.Index);
-            }
-        }
-
-        private long? GetMaxBitrate(long? clientMaxBitrate, Jellyfin.Data.Entities.User user)
-        {
-            var maxBitrate = clientMaxBitrate;
-            var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0;
-
-            if (remoteClientMaxBitrate <= 0)
-            {
-                remoteClientMaxBitrate = ServerConfigurationManager.Configuration.RemoteClientBitrateLimit;
-            }
-
-            if (remoteClientMaxBitrate > 0)
-            {
-                var isInLocalNetwork = _networkManager.IsInLocalNetwork(Request.RemoteIp);
-
-                Logger.LogInformation("RemoteClientBitrateLimit: {0}, RemoteIp: {1}, IsInLocalNetwork: {2}", remoteClientMaxBitrate, Request.RemoteIp, isInLocalNetwork);
-                if (!isInLocalNetwork)
-                {
-                    maxBitrate = Math.Min(maxBitrate ?? remoteClientMaxBitrate, remoteClientMaxBitrate);
-                }
-            }
-
-            return maxBitrate;
-        }
-
-        private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken)
-        {
-            var profiles = info.GetSubtitleProfiles(_mediaEncoder, false, "-", accessToken);
-            mediaSource.DefaultSubtitleStreamIndex = info.SubtitleStreamIndex;
-
-            mediaSource.TranscodeReasons = info.TranscodeReasons;
-
-            foreach (var profile in profiles)
-            {
-                foreach (var stream in mediaSource.MediaStreams)
-                {
-                    if (stream.Type == MediaStreamType.Subtitle && stream.Index == profile.Index)
-                    {
-                        stream.DeliveryMethod = profile.DeliveryMethod;
-
-                        if (profile.DeliveryMethod == SubtitleDeliveryMethod.External)
-                        {
-                            stream.DeliveryUrl = profile.Url.TrimStart('-');
-                            stream.IsExternalUrl = profile.IsExternalUrl;
-                        }
-                    }
-                }
-            }
-        }
-
-        private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate)
-        {
-            var originalList = result.MediaSources.ToList();
-
-            result.MediaSources = result.MediaSources.OrderBy(i =>
-            {
-                // Nothing beats direct playing a file
-                if (i.SupportsDirectPlay && i.Protocol == MediaProtocol.File)
-                {
-                    return 0;
-                }
-
-                return 1;
-            }).ThenBy(i =>
-            {
-                // Let's assume direct streaming a file is just as desirable as direct playing a remote url
-                if (i.SupportsDirectPlay || i.SupportsDirectStream)
-                {
-                    return 0;
-                }
-
-                return 1;
-            }).ThenBy(i =>
-            {
-                return i.Protocol switch
-                {
-                    MediaProtocol.File => 0,
-                    _ => 1,
-                };
-            }).ThenBy(i =>
-            {
-                if (maxBitrate.HasValue && i.Bitrate.HasValue)
-                {
-                    return i.Bitrate.Value <= maxBitrate.Value ? 0 : 2;
-                }
-
-                return 1;
-            }).ThenBy(originalList.IndexOf)
-            .ToArray();
-        }
-    }
-}

+ 0 - 91
MediaBrowser.Api/Playback/Progressive/AudioService.cs

@@ -1,91 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api.Playback.Progressive
-{
-    /// <summary>
-    /// Class GetAudioStream.
-    /// </summary>
-    public class GetAudioStream : StreamRequest
-    {
-    }
-
-    /// <summary>
-    /// Class AudioService.
-    /// </summary>
-    // TODO: In order to autheneticate this in the future, Dlna playback will require updating
-    //[Authenticated]
-    public class AudioService : BaseProgressiveStreamingService
-    {
-        public AudioService(
-            ILogger<AudioService> logger,
-            IServerConfigurationManager serverConfigurationManager,
-            IHttpResultFactory httpResultFactory,
-            IHttpClient httpClient,
-            IUserManager userManager,
-            ILibraryManager libraryManager,
-            IIsoManager isoManager,
-            IMediaEncoder mediaEncoder,
-            IFileSystem fileSystem,
-            IDlnaManager dlnaManager,
-            IDeviceManager deviceManager,
-            IMediaSourceManager mediaSourceManager,
-            IJsonSerializer jsonSerializer,
-            IAuthorizationContext authorizationContext,
-            EncodingHelper encodingHelper)
-            : base(
-                logger,
-                serverConfigurationManager,
-                httpResultFactory,
-                httpClient,
-                userManager,
-                libraryManager,
-                isoManager,
-                mediaEncoder,
-                fileSystem,
-                dlnaManager,
-                deviceManager,
-                mediaSourceManager,
-                jsonSerializer,
-                authorizationContext,
-                encodingHelper)
-        {
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public Task<object> Get(GetAudioStream request)
-        {
-            return ProcessRequest(request, false);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public Task<object> Head(GetAudioStream request)
-        {
-            return ProcessRequest(request, true);
-        }
-
-        protected override string GetCommandLineArguments(string outputPath, EncodingOptions encodingOptions, StreamState state, bool isEncoding)
-        {
-            return EncodingHelper.GetProgressiveAudioFullCommandLine(state, encodingOptions, outputPath);
-        }
-    }
-}

+ 0 - 401
MediaBrowser.Api/Playback/UniversalAudioService.cs

@@ -1,401 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Api.Playback.Hls;
-using MediaBrowser.Api.Playback.Progressive;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api.Playback
-{
-    public class BaseUniversalRequest
-    {
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid Id { get; set; }
-
-        [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string MediaSourceId { get; set; }
-
-        [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string DeviceId { get; set; }
-
-        public Guid UserId { get; set; }
-
-        public string AudioCodec { get; set; }
-
-        public string Container { get; set; }
-
-        public int? MaxAudioChannels { get; set; }
-
-        public int? TranscodingAudioChannels { get; set; }
-
-        public long? MaxStreamingBitrate { get; set; }
-
-        [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public long? StartTimeTicks { get; set; }
-
-        public string TranscodingContainer { get; set; }
-
-        public string TranscodingProtocol { get; set; }
-
-        public int? MaxAudioSampleRate { get; set; }
-
-        public int? MaxAudioBitDepth { get; set; }
-
-        public bool EnableRedirection { get; set; }
-
-        public bool EnableRemoteMedia { get; set; }
-
-        public bool BreakOnNonKeyFrames { get; set; }
-
-        public BaseUniversalRequest()
-        {
-            EnableRedirection = true;
-        }
-    }
-
-    [Route("/Audio/{Id}/universal.{Container}", "GET", Summary = "Gets an audio stream")]
-    [Route("/Audio/{Id}/universal", "GET", Summary = "Gets an audio stream")]
-    [Route("/Audio/{Id}/universal.{Container}", "HEAD", Summary = "Gets an audio stream")]
-    [Route("/Audio/{Id}/universal", "HEAD", Summary = "Gets an audio stream")]
-    public class GetUniversalAudioStream : BaseUniversalRequest
-    {
-    }
-
-    [Authenticated]
-    public class UniversalAudioService : BaseApiService
-    {
-        private readonly EncodingHelper _encodingHelper;
-        private readonly ILoggerFactory _loggerFactory;
-
-        public UniversalAudioService(
-            ILogger<UniversalAudioService> logger,
-            ILoggerFactory loggerFactory,
-            IServerConfigurationManager serverConfigurationManager,
-            IHttpResultFactory httpResultFactory,
-            IHttpClient httpClient,
-            IUserManager userManager,
-            ILibraryManager libraryManager,
-            IIsoManager isoManager,
-            IMediaEncoder mediaEncoder,
-            IFileSystem fileSystem,
-            IDlnaManager dlnaManager,
-            IDeviceManager deviceManager,
-            IMediaSourceManager mediaSourceManager,
-            IJsonSerializer jsonSerializer,
-            IAuthorizationContext authorizationContext,
-            INetworkManager networkManager,
-            EncodingHelper encodingHelper)
-            : base(logger, serverConfigurationManager, httpResultFactory)
-        {
-            HttpClient = httpClient;
-            UserManager = userManager;
-            LibraryManager = libraryManager;
-            IsoManager = isoManager;
-            MediaEncoder = mediaEncoder;
-            FileSystem = fileSystem;
-            DlnaManager = dlnaManager;
-            DeviceManager = deviceManager;
-            MediaSourceManager = mediaSourceManager;
-            JsonSerializer = jsonSerializer;
-            AuthorizationContext = authorizationContext;
-            NetworkManager = networkManager;
-            _encodingHelper = encodingHelper;
-            _loggerFactory = loggerFactory;
-        }
-
-        protected IHttpClient HttpClient { get; private set; }
-
-        protected IUserManager UserManager { get; private set; }
-
-        protected ILibraryManager LibraryManager { get; private set; }
-
-        protected IIsoManager IsoManager { get; private set; }
-
-        protected IMediaEncoder MediaEncoder { get; private set; }
-
-        protected IFileSystem FileSystem { get; private set; }
-
-        protected IDlnaManager DlnaManager { get; private set; }
-
-        protected IDeviceManager DeviceManager { get; private set; }
-
-        protected IMediaSourceManager MediaSourceManager { get; private set; }
-
-        protected IJsonSerializer JsonSerializer { get; private set; }
-
-        protected IAuthorizationContext AuthorizationContext { get; private set; }
-
-        protected INetworkManager NetworkManager { get; private set; }
-
-        public Task<object> Get(GetUniversalAudioStream request)
-        {
-            return GetUniversalStream(request, false);
-        }
-
-        public Task<object> Head(GetUniversalAudioStream request)
-        {
-            return GetUniversalStream(request, true);
-        }
-
-        private DeviceProfile GetDeviceProfile(GetUniversalAudioStream request)
-        {
-            var deviceProfile = new DeviceProfile();
-
-            var directPlayProfiles = new List<DirectPlayProfile>();
-
-            var containers = (request.Container ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
-
-            foreach (var container in containers)
-            {
-                var parts = container.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
-
-                var audioCodecs = parts.Length == 1 ? null : string.Join(",", parts.Skip(1).ToArray());
-
-                directPlayProfiles.Add(new DirectPlayProfile
-                {
-                    Type = DlnaProfileType.Audio,
-                    Container = parts[0],
-                    AudioCodec = audioCodecs
-                });
-            }
-
-            deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
-
-            deviceProfile.TranscodingProfiles = new[]
-            {
-                new TranscodingProfile
-                {
-                    Type = DlnaProfileType.Audio,
-                    Context = EncodingContext.Streaming,
-                    Container = request.TranscodingContainer,
-                    AudioCodec = request.AudioCodec,
-                    Protocol = request.TranscodingProtocol,
-                    BreakOnNonKeyFrames = request.BreakOnNonKeyFrames,
-                    MaxAudioChannels = request.TranscodingAudioChannels?.ToString(CultureInfo.InvariantCulture)
-                }
-            };
-
-            var codecProfiles = new List<CodecProfile>();
-            var conditions = new List<ProfileCondition>();
-
-            if (request.MaxAudioSampleRate.HasValue)
-            {
-                // codec profile
-                conditions.Add(new ProfileCondition
-                {
-                    Condition = ProfileConditionType.LessThanEqual,
-                    IsRequired = false,
-                    Property = ProfileConditionValue.AudioSampleRate,
-                    Value = request.MaxAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture)
-                });
-            }
-
-            if (request.MaxAudioBitDepth.HasValue)
-            {
-                // codec profile
-                conditions.Add(new ProfileCondition
-                {
-                    Condition = ProfileConditionType.LessThanEqual,
-                    IsRequired = false,
-                    Property = ProfileConditionValue.AudioBitDepth,
-                    Value = request.MaxAudioBitDepth.Value.ToString(CultureInfo.InvariantCulture)
-                });
-            }
-
-            if (request.MaxAudioChannels.HasValue)
-            {
-                // codec profile
-                conditions.Add(new ProfileCondition
-                {
-                    Condition = ProfileConditionType.LessThanEqual,
-                    IsRequired = false,
-                    Property = ProfileConditionValue.AudioChannels,
-                    Value = request.MaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture)
-                });
-            }
-
-            if (conditions.Count > 0)
-            {
-                // codec profile
-                codecProfiles.Add(new CodecProfile
-                {
-                    Type = CodecType.Audio,
-                    Container = request.Container,
-                    Conditions = conditions.ToArray()
-                });
-            }
-
-            deviceProfile.CodecProfiles = codecProfiles.ToArray();
-
-            return deviceProfile;
-        }
-
-        private async Task<object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
-        {
-            var deviceProfile = GetDeviceProfile(request);
-
-            AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
-
-            var mediaInfoService = new MediaInfoService(
-                _loggerFactory.CreateLogger<MediaInfoService>(),
-                ServerConfigurationManager,
-                ResultFactory,
-                MediaSourceManager,
-                DeviceManager,
-                LibraryManager,
-                NetworkManager,
-                MediaEncoder,
-                UserManager,
-                AuthorizationContext)
-            {
-                Request = Request
-            };
-
-            var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
-            {
-                Id = request.Id,
-                MaxAudioChannels = request.MaxAudioChannels,
-                MaxStreamingBitrate = request.MaxStreamingBitrate,
-                StartTimeTicks = request.StartTimeTicks,
-                UserId = request.UserId,
-                DeviceProfile = deviceProfile,
-                MediaSourceId = request.MediaSourceId
-            }).ConfigureAwait(false);
-
-            var mediaSource = playbackInfoResult.MediaSources[0];
-
-            if (mediaSource.SupportsDirectPlay && mediaSource.Protocol == MediaProtocol.Http)
-            {
-                if (request.EnableRedirection)
-                {
-                    if (mediaSource.IsRemote && request.EnableRemoteMedia)
-                    {
-                        return ResultFactory.GetRedirectResult(mediaSource.Path);
-                    }
-                }
-            }
-
-            var isStatic = mediaSource.SupportsDirectStream;
-
-            if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
-            {
-                var service = new DynamicHlsService(
-                    _loggerFactory.CreateLogger<DynamicHlsService>(),
-                    ServerConfigurationManager,
-                    ResultFactory,
-                    UserManager,
-                    LibraryManager,
-                    IsoManager,
-                    MediaEncoder,
-                    FileSystem,
-                    DlnaManager,
-                    DeviceManager,
-                    MediaSourceManager,
-                    JsonSerializer,
-                    AuthorizationContext,
-                    NetworkManager,
-                    _encodingHelper)
-                {
-                    Request = Request
-                };
-
-                var transcodingProfile = deviceProfile.TranscodingProfiles[0];
-
-                // hls segment container can only be mpegts or fmp4 per ffmpeg documentation
-                // TODO: remove this when we switch back to the segment muxer
-                var supportedHLSContainers = new[] { "mpegts", "fmp4" };
-
-                var newRequest = new GetMasterHlsAudioPlaylist
-                {
-                    AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
-                    AudioCodec = transcodingProfile.AudioCodec,
-                    Container = ".m3u8",
-                    DeviceId = request.DeviceId,
-                    Id = request.Id,
-                    MaxAudioChannels = request.MaxAudioChannels,
-                    MediaSourceId = mediaSource.Id,
-                    PlaySessionId = playbackInfoResult.PlaySessionId,
-                    StartTimeTicks = request.StartTimeTicks,
-                    Static = isStatic,
-                    // fallback to mpegts if device reports some weird value unsupported by hls
-                    SegmentContainer = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts",
-                    AudioSampleRate = request.MaxAudioSampleRate,
-                    MaxAudioBitDepth = request.MaxAudioBitDepth,
-                    BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames,
-                    TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
-                };
-
-                if (isHeadRequest)
-                {
-                    return await service.Head(newRequest).ConfigureAwait(false);
-                }
-
-                return await service.Get(newRequest).ConfigureAwait(false);
-            }
-            else
-            {
-                var service = new AudioService(
-                    _loggerFactory.CreateLogger<AudioService>(),
-                    ServerConfigurationManager,
-                    ResultFactory,
-                    HttpClient,
-                    UserManager,
-                    LibraryManager,
-                    IsoManager,
-                    MediaEncoder,
-                    FileSystem,
-                    DlnaManager,
-                    DeviceManager,
-                    MediaSourceManager,
-                    JsonSerializer,
-                    AuthorizationContext,
-                    _encodingHelper)
-                {
-                    Request = Request
-                };
-
-                var newRequest = new GetAudioStream
-                {
-                    AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
-                    AudioCodec = request.AudioCodec,
-                    Container = isStatic ? null : ("." + mediaSource.TranscodingContainer),
-                    DeviceId = request.DeviceId,
-                    Id = request.Id,
-                    MaxAudioChannels = request.MaxAudioChannels,
-                    MediaSourceId = mediaSource.Id,
-                    PlaySessionId = playbackInfoResult.PlaySessionId,
-                    StartTimeTicks = request.StartTimeTicks,
-                    Static = isStatic,
-                    AudioSampleRate = request.MaxAudioSampleRate,
-                    MaxAudioBitDepth = request.MaxAudioBitDepth,
-                    TranscodeReasons = mediaSource.TranscodeReasons == null ? null : string.Join(",", mediaSource.TranscodeReasons.Select(i => i.ToString()).ToArray())
-                };
-
-                if (isHeadRequest)
-                {
-                    return await service.Head(newRequest).ConfigureAwait(false);
-                }
-
-                return await service.Get(newRequest).ConfigureAwait(false);
-            }
-        }
-    }
-}