| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775 | 
							- using System;
 
- using System.Collections.Generic;
 
- using System.Globalization;
 
- using System.IO;
 
- using System.Linq;
 
- using System.Threading;
 
- using System.Threading.Tasks;
 
- using Jellyfin.Api.Models.StreamingDtos;
 
- using Jellyfin.Extensions;
 
- using MediaBrowser.Common.Configuration;
 
- using MediaBrowser.Common.Extensions;
 
- 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.Dto;
 
- using MediaBrowser.Model.Entities;
 
- using Microsoft.AspNetCore.Http;
 
- using Microsoft.Extensions.Primitives;
 
- using Microsoft.Net.Http.Headers;
 
- namespace Jellyfin.Api.Helpers
 
- {
 
-     /// <summary>
 
-     /// The streaming helpers.
 
-     /// </summary>
 
-     public static class StreamingHelpers
 
-     {
 
-         /// <summary>
 
-         /// Gets the current streaming state.
 
-         /// </summary>
 
-         /// <param name="streamingRequest">The <see cref="StreamingRequestDto"/>.</param>
 
-         /// <param name="httpRequest">The <see cref="HttpRequest"/>.</param>
 
-         /// <param name="authorizationContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
 
-         /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
 
-         /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 
-         /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 
-         /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 
-         /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
 
-         /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
 
-         /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
 
-         /// <param name="deviceManager">Instance of the <see cref="IDeviceManager"/> interface.</param>
 
-         /// <param name="transcodingJobHelper">Initialized <see cref="TranscodingJobHelper"/>.</param>
 
-         /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
 
-         /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
 
-         /// <returns>A <see cref="Task"/> containing the current <see cref="StreamState"/>.</returns>
 
-         public static async Task<StreamState> GetStreamingState(
 
-             StreamingRequestDto streamingRequest,
 
-             HttpRequest httpRequest,
 
-             IAuthorizationContext authorizationContext,
 
-             IMediaSourceManager mediaSourceManager,
 
-             IUserManager userManager,
 
-             ILibraryManager libraryManager,
 
-             IServerConfigurationManager serverConfigurationManager,
 
-             IMediaEncoder mediaEncoder,
 
-             EncodingHelper encodingHelper,
 
-             IDlnaManager dlnaManager,
 
-             IDeviceManager deviceManager,
 
-             TranscodingJobHelper transcodingJobHelper,
 
-             TranscodingJobType transcodingJobType,
 
-             CancellationToken cancellationToken)
 
-         {
 
-             // Parse the DLNA time seek header
 
-             if (!streamingRequest.StartTimeTicks.HasValue)
 
-             {
 
-                 var timeSeek = httpRequest.Headers["TimeSeekRange.dlna.org"];
 
-                 streamingRequest.StartTimeTicks = ParseTimeSeekHeader(timeSeek.ToString());
 
-             }
 
-             if (!string.IsNullOrWhiteSpace(streamingRequest.Params))
 
-             {
 
-                 ParseParams(streamingRequest);
 
-             }
 
-             streamingRequest.StreamOptions = ParseStreamOptions(httpRequest.Query);
 
-             if (httpRequest.Path.Value == null)
 
-             {
 
-                 throw new ResourceNotFoundException(nameof(httpRequest.Path));
 
-             }
 
-             var url = httpRequest.Path.Value.AsSpan().RightPart('.').ToString();
 
-             if (string.IsNullOrEmpty(streamingRequest.AudioCodec))
 
-             {
 
-                 streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url);
 
-             }
 
-             var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) ||
 
-                                     streamingRequest.StreamOptions.ContainsKey("dlnaheaders") ||
 
-                                     string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase);
 
-             var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
 
-             {
 
-                 Request = streamingRequest,
 
-                 RequestedUrl = url,
 
-                 UserAgent = httpRequest.Headers[HeaderNames.UserAgent],
 
-                 EnableDlnaHeaders = enableDlnaHeaders
 
-             };
 
-             var auth = await authorizationContext.GetAuthorizationInfo(httpRequest).ConfigureAwait(false);
 
-             if (!auth.UserId.Equals(default))
 
-             {
 
-                 state.User = userManager.GetUserById(auth.UserId);
 
-             }
 
-             if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.Request.VideoCodec))
 
-             {
 
-                 state.SupportedVideoCodecs = state.Request.VideoCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
 
-                 state.Request.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault();
 
-             }
 
-             if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec))
 
-             {
 
-                 state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
 
-                 state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(mediaEncoder.CanEncodeToAudioCodec)
 
-                                            ?? state.SupportedAudioCodecs.FirstOrDefault();
 
-             }
 
-             if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec))
 
-             {
 
-                 state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',', StringSplitOptions.RemoveEmptyEntries);
 
-                 state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(mediaEncoder.CanEncodeToSubtitleCodec)
 
-                                               ?? state.SupportedSubtitleCodecs.FirstOrDefault();
 
-             }
 
-             var item = libraryManager.GetItemById(streamingRequest.Id);
 
-             state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
 
-             MediaSourceInfo? mediaSource = null;
 
-             if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
 
-             {
 
-                 var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId)
 
-                     ? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId)
 
-                     : null;
 
-                 if (currentJob != null)
 
-                 {
 
-                     mediaSource = currentJob.MediaSource;
 
-                 }
 
-                 if (mediaSource == null)
 
-                 {
 
-                     var mediaSources = await mediaSourceManager.GetPlaybackMediaSources(libraryManager.GetItemById(streamingRequest.Id), null, false, false, cancellationToken).ConfigureAwait(false);
 
-                     mediaSource = string.IsNullOrEmpty(streamingRequest.MediaSourceId)
 
-                         ? mediaSources[0]
 
-                         : mediaSources.Find(i => string.Equals(i.Id, streamingRequest.MediaSourceId, StringComparison.Ordinal));
 
-                     if (mediaSource == null && Guid.Parse(streamingRequest.MediaSourceId).Equals(streamingRequest.Id))
 
-                     {
 
-                         mediaSource = mediaSources[0];
 
-                     }
 
-                 }
 
-             }
 
-             else
 
-             {
 
-                 var liveStreamInfo = await mediaSourceManager.GetLiveStreamWithDirectStreamProvider(streamingRequest.LiveStreamId, cancellationToken).ConfigureAwait(false);
 
-                 mediaSource = liveStreamInfo.Item1;
 
-                 state.DirectStreamProvider = liveStreamInfo.Item2;
 
-             }
 
-             var encodingOptions = serverConfigurationManager.GetEncodingOptions();
 
-             encodingHelper.AttachMediaSourceInfo(state, encodingOptions, mediaSource, url);
 
-             string? containerInternal = Path.GetExtension(state.RequestedUrl);
 
-             if (!string.IsNullOrEmpty(streamingRequest.Container))
 
-             {
 
-                 containerInternal = streamingRequest.Container;
 
-             }
 
-             if (string.IsNullOrEmpty(containerInternal))
 
-             {
 
-                 containerInternal = streamingRequest.Static ?
 
-                     StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, null, DlnaProfileType.Audio)
 
-                     : GetOutputFileExtension(state);
 
-             }
 
-             state.OutputContainer = (containerInternal ?? string.Empty).TrimStart('.');
 
-             state.OutputAudioBitrate = encodingHelper.GetAudioBitrateParam(streamingRequest.AudioBitRate, streamingRequest.AudioCodec, state.AudioStream);
 
-             state.OutputAudioCodec = streamingRequest.AudioCodec;
 
-             state.OutputAudioChannels = encodingHelper.GetNumAudioChannelsParam(state, state.AudioStream, state.OutputAudioCodec);
 
-             if (state.VideoRequest != null)
 
-             {
 
-                 state.OutputVideoCodec = state.Request.VideoCodec;
 
-                 state.OutputVideoBitrate = encodingHelper.GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream, state.OutputVideoCodec);
 
-                 encodingHelper.TryStreamCopy(state);
 
-                 if (!EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.OutputVideoBitrate.HasValue)
 
-                 {
 
-                     var isVideoResolutionNotRequested = !state.VideoRequest.Width.HasValue
 
-                         && !state.VideoRequest.Height.HasValue
 
-                         && !state.VideoRequest.MaxWidth.HasValue
 
-                         && !state.VideoRequest.MaxHeight.HasValue;
 
-                     if (isVideoResolutionNotRequested
 
-                         && state.VideoStream != null
 
-                         && state.VideoRequest.VideoBitRate.HasValue
 
-                         && state.VideoStream.BitRate.HasValue
 
-                         && state.VideoRequest.VideoBitRate.Value >= state.VideoStream.BitRate.Value)
 
-                     {
 
-                         // Don't downscale the resolution if the width/height/MaxWidth/MaxHeight is not requested,
 
-                         // and the requested video bitrate is higher than source video bitrate.
 
-                         if (state.VideoStream.Width.HasValue || state.VideoStream.Height.HasValue)
 
-                         {
 
-                             state.VideoRequest.MaxWidth = state.VideoStream?.Width;
 
-                             state.VideoRequest.MaxHeight = state.VideoStream?.Height;
 
-                         }
 
-                     }
 
-                     else
 
-                     {
 
-                         var resolution = ResolutionNormalizer.Normalize(
 
-                             state.VideoStream?.BitRate,
 
-                             state.OutputVideoBitrate.Value,
 
-                             state.VideoRequest.MaxWidth,
 
-                             state.VideoRequest.MaxHeight);
 
-                         state.VideoRequest.MaxWidth = resolution.MaxWidth;
 
-                         state.VideoRequest.MaxHeight = resolution.MaxHeight;
 
-                     }
 
-                 }
 
-             }
 
-             ApplyDeviceProfileSettings(state, dlnaManager, deviceManager, httpRequest, streamingRequest.DeviceProfileId, streamingRequest.Static);
 
-             var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
 
-                 ? GetOutputFileExtension(state)
 
-                 : ("." + state.OutputContainer);
 
-             state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
 
-             return state;
 
-         }
 
-         /// <summary>
 
-         /// Adds the dlna headers.
 
-         /// </summary>
 
-         /// <param name="state">The state.</param>
 
-         /// <param name="responseHeaders">The response headers.</param>
 
-         /// <param name="isStaticallyStreamed">if set to <c>true</c> [is statically streamed].</param>
 
-         /// <param name="startTimeTicks">The start time in ticks.</param>
 
-         /// <param name="request">The <see cref="HttpRequest"/>.</param>
 
-         /// <param name="dlnaManager">Instance of the <see cref="IDlnaManager"/> interface.</param>
 
-         public static void AddDlnaHeaders(
 
-             StreamState state,
 
-             IHeaderDictionary responseHeaders,
 
-             bool isStaticallyStreamed,
 
-             long? startTimeTicks,
 
-             HttpRequest request,
 
-             IDlnaManager dlnaManager)
 
-         {
 
-             if (!state.EnableDlnaHeaders)
 
-             {
 
-                 return;
 
-             }
 
-             var profile = state.DeviceProfile;
 
-             StringValues transferMode = request.Headers["transferMode.dlna.org"];
 
-             responseHeaders.Add("transferMode.dlna.org", string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode.ToString());
 
-             responseHeaders.Add("realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*");
 
-             if (state.RunTimeTicks.HasValue)
 
-             {
 
-                 if (string.Equals(request.Headers["getMediaInfo.sec"], "1", StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     var ms = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds;
 
-                     responseHeaders.Add("MediaInfo.sec", string.Format(
 
-                         CultureInfo.InvariantCulture,
 
-                         "SEC_Duration={0};",
 
-                         Convert.ToInt32(ms)));
 
-                 }
 
-                 if (!isStaticallyStreamed && profile != null)
 
-                 {
 
-                     AddTimeSeekResponseHeaders(state, responseHeaders, startTimeTicks);
 
-                 }
 
-             }
 
-             profile ??= dlnaManager.GetDefaultProfile();
 
-             var audioCodec = state.ActualOutputAudioCodec;
 
-             if (!state.IsVideoRequest)
 
-             {
 
-                 responseHeaders.Add("contentFeatures.dlna.org", ContentFeatureBuilder.BuildAudioHeader(
 
-                     profile,
 
-                     state.OutputContainer,
 
-                     audioCodec,
 
-                     state.OutputAudioBitrate,
 
-                     state.OutputAudioSampleRate,
 
-                     state.OutputAudioChannels,
 
-                     state.OutputAudioBitDepth,
 
-                     isStaticallyStreamed,
 
-                     state.RunTimeTicks,
 
-                     state.TranscodeSeekInfo));
 
-             }
 
-             else
 
-             {
 
-                 var videoCodec = state.ActualOutputVideoCodec;
 
-                 responseHeaders.Add(
 
-                     "contentFeatures.dlna.org",
 
-                     ContentFeatureBuilder.BuildVideoHeader(profile, state.OutputContainer, videoCodec, audioCodec, state.OutputWidth, state.OutputHeight, state.TargetVideoBitDepth, state.OutputVideoBitrate, state.TargetTimestamp, isStaticallyStreamed, state.RunTimeTicks, state.TargetVideoProfile, state.TargetVideoLevel, state.TargetFramerate, state.TargetPacketLength, state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetInterlaced, state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, state.TargetVideoCodecTag, state.IsTargetAVC).FirstOrDefault() ?? string.Empty);
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// Parses the time seek header.
 
-         /// </summary>
 
-         /// <param name="value">The time seek header string.</param>
 
-         /// <returns>A nullable <see cref="long"/> representing the seek time in ticks.</returns>
 
-         private static long? ParseTimeSeekHeader(ReadOnlySpan<char> value)
 
-         {
 
-             if (value.IsEmpty)
 
-             {
 
-                 return null;
 
-             }
 
-             const string npt = "npt=";
 
-             if (!value.StartsWith(npt, StringComparison.OrdinalIgnoreCase))
 
-             {
 
-                 throw new ArgumentException("Invalid timeseek header");
 
-             }
 
-             var index = value.IndexOf('-');
 
-             value = index == -1
 
-                 ? value.Slice(npt.Length)
 
-                 : value.Slice(npt.Length, index - npt.Length);
 
-             if (value.IndexOf(':') == -1)
 
-             {
 
-                 // Parses npt times in the format of '417.33'
 
-                 if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds))
 
-                 {
 
-                     return TimeSpan.FromSeconds(seconds).Ticks;
 
-                 }
 
-                 throw new ArgumentException("Invalid timeseek header");
 
-             }
 
-             try
 
-             {
 
-                 // Parses npt times in the format of '10:19:25.7'
 
-                 return TimeSpan.Parse(value).Ticks;
 
-             }
 
-             catch
 
-             {
 
-                 throw new ArgumentException("Invalid timeseek header");
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// Parses query parameters as StreamOptions.
 
-         /// </summary>
 
-         /// <param name="queryString">The query string.</param>
 
-         /// <returns>A <see cref="Dictionary{String,String}"/> containing the stream options.</returns>
 
-         private static Dictionary<string, string> ParseStreamOptions(IQueryCollection queryString)
 
-         {
 
-             Dictionary<string, string> streamOptions = new Dictionary<string, string>();
 
-             foreach (var param in queryString)
 
-             {
 
-                 if (char.IsLower(param.Key[0]))
 
-                 {
 
-                     // This was probably not parsed initially and should be a StreamOptions
 
-                     // or the generated URL should correctly serialize it
 
-                     // TODO: This should be incorporated either in the lower framework for parsing requests
 
-                     streamOptions[param.Key] = param.Value;
 
-                 }
 
-             }
 
-             return streamOptions;
 
-         }
 
-         /// <summary>
 
-         /// Adds the dlna time seek headers to the response.
 
-         /// </summary>
 
-         /// <param name="state">The current <see cref="StreamState"/>.</param>
 
-         /// <param name="responseHeaders">The <see cref="IHeaderDictionary"/> of the response.</param>
 
-         /// <param name="startTimeTicks">The start time in ticks.</param>
 
-         private static void AddTimeSeekResponseHeaders(StreamState state, IHeaderDictionary responseHeaders, long? startTimeTicks)
 
-         {
 
-             var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks!.Value).TotalSeconds.ToString(CultureInfo.InvariantCulture);
 
-             var startSeconds = TimeSpan.FromTicks(startTimeTicks ?? 0).TotalSeconds.ToString(CultureInfo.InvariantCulture);
 
-             responseHeaders.Add("TimeSeekRange.dlna.org", string.Format(
 
-                 CultureInfo.InvariantCulture,
 
-                 "npt={0}-{1}/{1}",
 
-                 startSeconds,
 
-                 runtimeSeconds));
 
-             responseHeaders.Add("X-AvailableSeekRange", string.Format(
 
-                 CultureInfo.InvariantCulture,
 
-                 "1 npt={0}-{1}",
 
-                 startSeconds,
 
-                 runtimeSeconds));
 
-         }
 
-         /// <summary>
 
-         /// Gets the output file extension.
 
-         /// </summary>
 
-         /// <param name="state">The state.</param>
 
-         /// <returns>System.String.</returns>
 
-         private static string? GetOutputFileExtension(StreamState state)
 
-         {
 
-             var ext = Path.GetExtension(state.RequestedUrl);
 
-             if (!string.IsNullOrEmpty(ext))
 
-             {
 
-                 return ext;
 
-             }
 
-             // Try to infer based on the desired video codec
 
-             if (state.IsVideoRequest)
 
-             {
 
-                 var videoCodec = state.Request.VideoCodec;
 
-                 if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase) ||
 
-                     string.Equals(videoCodec, "h265", StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".ts";
 
-                 }
 
-                 if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".ogv";
 
-                 }
 
-                 if (string.Equals(videoCodec, "vp8", StringComparison.OrdinalIgnoreCase)
 
-                     || string.Equals(videoCodec, "vp9", StringComparison.OrdinalIgnoreCase)
 
-                     || string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".webm";
 
-                 }
 
-                 if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".asf";
 
-                 }
 
-             }
 
-             // Try to infer based on the desired audio codec
 
-             if (!state.IsVideoRequest)
 
-             {
 
-                 var audioCodec = state.Request.AudioCodec;
 
-                 if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".aac";
 
-                 }
 
-                 if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".mp3";
 
-                 }
 
-                 if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".ogg";
 
-                 }
 
-                 if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
 
-                 {
 
-                     return ".wma";
 
-                 }
 
-             }
 
-             return null;
 
-         }
 
-         /// <summary>
 
-         /// Gets the output file path for transcoding.
 
-         /// </summary>
 
-         /// <param name="state">The current <see cref="StreamState"/>.</param>
 
-         /// <param name="outputFileExtension">The file extension of the output file.</param>
 
-         /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
 
-         /// <param name="deviceId">The device id.</param>
 
-         /// <param name="playSessionId">The play session id.</param>
 
-         /// <returns>The complete file path, including the folder, for the transcoding file.</returns>
 
-         private static string GetOutputFilePath(StreamState state, string outputFileExtension, IServerConfigurationManager serverConfigurationManager, string? deviceId, string? playSessionId)
 
-         {
 
-             var data = $"{state.MediaPath}-{state.UserAgent}-{deviceId!}-{playSessionId!}";
 
-             var filename = data.GetMD5().ToString("N", CultureInfo.InvariantCulture);
 
-             var ext = outputFileExtension?.ToLowerInvariant();
 
-             var folder = serverConfigurationManager.GetTranscodePath();
 
-             return Path.Combine(folder, filename + ext);
 
-         }
 
-         private static void ApplyDeviceProfileSettings(StreamState state, IDlnaManager dlnaManager, IDeviceManager deviceManager, HttpRequest request, string? deviceProfileId, bool? @static)
 
-         {
 
-             if (!string.IsNullOrWhiteSpace(deviceProfileId))
 
-             {
 
-                 state.DeviceProfile = dlnaManager.GetProfile(deviceProfileId);
 
-                 if (state.DeviceProfile == null)
 
-                 {
 
-                     var caps = deviceManager.GetCapabilities(deviceProfileId);
 
-                     state.DeviceProfile = caps == null ? dlnaManager.GetProfile(request.Headers) : caps.DeviceProfile;
 
-                 }
 
-             }
 
-             var profile = state.DeviceProfile;
 
-             if (profile == null)
 
-             {
 
-                 // Don't use settings from the default profile.
 
-                 // Only use a specific profile if it was requested.
 
-                 return;
 
-             }
 
-             var audioCodec = state.ActualOutputAudioCodec;
 
-             var videoCodec = state.ActualOutputVideoCodec;
 
-             var mediaProfile = !state.IsVideoRequest
 
-                 ? profile.GetAudioMediaProfile(state.OutputContainer, audioCodec, state.OutputAudioChannels, state.OutputAudioBitrate, state.OutputAudioSampleRate, state.OutputAudioBitDepth)
 
-                 : profile.GetVideoMediaProfile(
 
-                     state.OutputContainer,
 
-                     audioCodec,
 
-                     videoCodec,
 
-                     state.OutputWidth,
 
-                     state.OutputHeight,
 
-                     state.TargetVideoBitDepth,
 
-                     state.OutputVideoBitrate,
 
-                     state.TargetVideoProfile,
 
-                     state.TargetVideoLevel,
 
-                     state.TargetFramerate,
 
-                     state.TargetPacketLength,
 
-                     state.TargetTimestamp,
 
-                     state.IsTargetAnamorphic,
 
-                     state.IsTargetInterlaced,
 
-                     state.TargetRefFrames,
 
-                     state.TargetVideoStreamCount,
 
-                     state.TargetAudioStreamCount,
 
-                     state.TargetVideoCodecTag,
 
-                     state.IsTargetAVC);
 
-             if (mediaProfile != null)
 
-             {
 
-                 state.MimeType = mediaProfile.MimeType;
 
-             }
 
-             if (!(@static.HasValue && @static.Value))
 
-             {
 
-                 var transcodingProfile = !state.IsVideoRequest ? profile.GetAudioTranscodingProfile(state.OutputContainer, audioCodec) : profile.GetVideoTranscodingProfile(state.OutputContainer, audioCodec, videoCodec);
 
-                 if (transcodingProfile != null)
 
-                 {
 
-                     state.EstimateContentLength = transcodingProfile.EstimateContentLength;
 
-                     // state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
 
-                     state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
 
-                     if (state.VideoRequest != null)
 
-                     {
 
-                         state.VideoRequest.CopyTimestamps = transcodingProfile.CopyTimestamps;
 
-                         state.VideoRequest.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         /// <summary>
 
-         /// Parses the parameters.
 
-         /// </summary>
 
-         /// <param name="request">The request.</param>
 
-         private static void ParseParams(StreamingRequestDto request)
 
-         {
 
-             if (string.IsNullOrEmpty(request.Params))
 
-             {
 
-                 return;
 
-             }
 
-             var vals = request.Params.Split(';');
 
-             var videoRequest = request as VideoRequestDto;
 
-             for (var i = 0; i < vals.Length; i++)
 
-             {
 
-                 var val = vals[i];
 
-                 if (string.IsNullOrWhiteSpace(val))
 
-                 {
 
-                     continue;
 
-                 }
 
-                 switch (i)
 
-                 {
 
-                     case 0:
 
-                         request.DeviceProfileId = val;
 
-                         break;
 
-                     case 1:
 
-                         request.DeviceId = val;
 
-                         break;
 
-                     case 2:
 
-                         request.MediaSourceId = val;
 
-                         break;
 
-                     case 3:
 
-                         request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         break;
 
-                     case 4:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.VideoCodec = val;
 
-                         }
 
-                         break;
 
-                     case 5:
 
-                         request.AudioCodec = val;
 
-                         break;
 
-                     case 6:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.AudioStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 7:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.SubtitleStreamIndex = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 8:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.VideoBitRate = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 9:
 
-                         request.AudioBitRate = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         break;
 
-                     case 10:
 
-                         request.MaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         break;
 
-                     case 11:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.MaxFramerate = float.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 12:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.MaxWidth = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 13:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.MaxHeight = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 14:
 
-                         request.StartTimeTicks = long.Parse(val, CultureInfo.InvariantCulture);
 
-                         break;
 
-                     case 15:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.Level = val;
 
-                         }
 
-                         break;
 
-                     case 16:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.MaxRefFrames = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 17:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.MaxVideoBitDepth = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         }
 
-                         break;
 
-                     case 18:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.Profile = val;
 
-                         }
 
-                         break;
 
-                     case 19:
 
-                         // cabac no longer used
 
-                         break;
 
-                     case 20:
 
-                         request.PlaySessionId = val;
 
-                         break;
 
-                     case 21:
 
-                         // api_key
 
-                         break;
 
-                     case 22:
 
-                         request.LiveStreamId = val;
 
-                         break;
 
-                     case 23:
 
-                         // Duplicating ItemId because of MediaMonkey
 
-                         break;
 
-                     case 24:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.CopyTimestamps = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         }
 
-                         break;
 
-                     case 25:
 
-                         if (!string.IsNullOrWhiteSpace(val) && videoRequest != null)
 
-                         {
 
-                             if (Enum.TryParse(val, out SubtitleDeliveryMethod method))
 
-                             {
 
-                                 videoRequest.SubtitleMethod = method;
 
-                             }
 
-                         }
 
-                         break;
 
-                     case 26:
 
-                         request.TranscodingMaxAudioChannels = int.Parse(val, CultureInfo.InvariantCulture);
 
-                         break;
 
-                     case 27:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         }
 
-                         break;
 
-                     case 28:
 
-                         request.Tag = val;
 
-                         break;
 
-                     case 29:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.RequireAvc = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         }
 
-                         break;
 
-                     case 30:
 
-                         request.SubtitleCodec = val;
 
-                         break;
 
-                     case 31:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.RequireNonAnamorphic = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         }
 
-                         break;
 
-                     case 32:
 
-                         if (videoRequest != null)
 
-                         {
 
-                             videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
 
-                         }
 
-                         break;
 
-                     case 33:
 
-                         request.TranscodeReasons = val;
 
-                         break;
 
-                 }
 
-             }
 
-         }
 
-     }
 
- }
 
 
  |