Kaynağa Gözat

Revert back to 10e57ce8d21b4516733894075001819f3cd6db6b for MediaEncoding
Remove some duplicate code that were causing warnings

Mathieu Velten 6 yıl önce
ebeveyn
işleme
da16de48aa

+ 0 - 23
MediaBrowser.Api/Playback/StreamRequest.cs

@@ -9,29 +9,6 @@ namespace MediaBrowser.Api.Playback
     /// </summary>
     public class StreamRequest : BaseEncodingJobOptions
     {
-        /// <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; }
-
-        [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Container { get; set; }
-
-        /// <summary>
-        /// Gets or sets the audio codec.
-        /// </summary>
-        /// <value>The audio codec.</value>
-        [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string AudioCodec { get; set; }
-
         [ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string DeviceProfileId { get; set; }
 

+ 15 - 77
MediaBrowser.Api/Playback/StreamState.cs

@@ -92,76 +92,60 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
-        public int HlsListSize => 0;
-
         public string UserAgent { get; set; }
 
         public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
-            : base(logger, mediaSourceManager, transcodingType)
+            : base(transcodingType)
         {
             _mediaSourceManager = mediaSourceManager;
             _logger = logger;
         }
 
-        public string MimeType { get; set; }
-
         public bool EstimateContentLength { get; set; }
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
 
-        public long? EncodingDurationTicks { get; set; }
-
-        public string GetMimeType(string outputPath, bool enableStreamDefault = true)
-        {
-            if (!string.IsNullOrEmpty(MimeType))
-            {
-                return MimeType;
-            }
-
-            return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
-        }
-
         public bool EnableDlnaHeaders { get; set; }
 
-        public void Dispose()
+        public override void Dispose()
         {
             DisposeTranscodingThrottler();
-            DisposeLiveStream();
             DisposeLogStream();
+            DisposeLiveStream();
 
             TranscodingJob = null;
         }
 
-        private void DisposeLogStream()
+        private void DisposeTranscodingThrottler()
         {
-            if (LogFileStream != null)
+            if (TranscodingThrottler != null)
             {
                 try
                 {
-                    LogFileStream.Dispose();
+                    TranscodingThrottler.Dispose();
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError(ex, "Error disposing log stream");
+                    _logger.LogError(ex, "Error disposing TranscodingThrottler");
                 }
 
-                LogFileStream = null;
+                TranscodingThrottler = null;
             }
         }
 
-        private void DisposeTranscodingThrottler()
+        private void DisposeLogStream()
         {
-            if (TranscodingThrottler != null)
+            if (LogFileStream != null)
             {
                 try
                 {
-                    TranscodingThrottler.Dispose();
+                    LogFileStream.Dispose();
                 }
                 catch (Exception ex)
                 {
-                    _logger.LogError(ex, "Error disposing TranscodingThrottler");
+                    _logger.LogError(ex, "Error disposing log stream");
                 }
 
-                TranscodingThrottler = null;
+                LogFileStream = null;
             }
         }
 
@@ -180,58 +164,12 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
-        public string OutputFilePath { get; set; }
-
-        public string ActualOutputVideoCodec
-        {
-            get
-            {
-                var codec = OutputVideoCodec;
-
-                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
-                {
-                    var stream = VideoStream;
-
-                    if (stream != null)
-                    {
-                        return stream.Codec;
-                    }
-
-                    return null;
-                }
-
-                return codec;
-            }
-        }
-
-        public string ActualOutputAudioCodec
-        {
-            get
-            {
-                var codec = OutputAudioCodec;
-
-                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
-                {
-                    var stream = AudioStream;
-
-                    if (stream != null)
-                    {
-                        return stream.Codec;
-                    }
-
-                    return null;
-                }
-
-                return codec;
-            }
-        }
-
         public DeviceProfile DeviceProfile { get; set; }
 
         public TranscodingJob TranscodingJob;
-        public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
+        public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
         {
-            ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, 0, percentComplete, 0, bitRate);
+            ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
         }
     }
 }

+ 191 - 76
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using System.Threading;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dlna;
@@ -21,6 +22,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IFileSystem _fileSystem;
         private readonly ISubtitleEncoder _subtitleEncoder;
+        // private readonly IApplicationPaths _appPaths;
+        // private readonly IAssemblyInfo _assemblyInfo;
 
         public EncodingHelper(IMediaEncoder mediaEncoder, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder)
         {
@@ -40,56 +43,55 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
                 var hwType = encodingOptions.HardwareAccelerationType;
 
-                if (!encodingOptions.EnableHardwareEncoding)
+                var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
                 {
-                    hwType = null;
-                }
-
-                if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
-                    string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_qsv", defaultEncoder);
-                }
+                    {"qsv",          "h264_qsv"},
+                    {"h264_qsv",     "h264_qsv"},
+                    {"nvenc",        "h264_nvenc"},
+                    {"amf",          "h264_amf"},
+                    {"omx",          "h264_omx"},
+                    {"h264_v4l2m2m", "h264_v4l2m2m"},
+                    {"mediacodec",   "h264_mediacodec"},
+                    {"vaapi",        "h264_vaapi"}
+                };
 
-                if (string.Equals(hwType, "nvenc", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_nvenc", defaultEncoder);
-                }
-                if (string.Equals(hwType, "amf", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_amf", defaultEncoder);
-                }
-                if (string.Equals(hwType, "omx", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_omx", defaultEncoder);
-                }
-                if (string.Equals(hwType, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_v4l2m2m", defaultEncoder);
-                }
-                if (string.Equals(hwType, "mediacodec", StringComparison.OrdinalIgnoreCase))
-                {
-                    return GetAvailableEncoder("h264_mediacodec", defaultEncoder);
-                }
-                if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(encodingOptions.VaapiDevice))
+                if (!string.IsNullOrEmpty(hwType)
+                    && encodingOptions.EnableHardwareEncoding && codecMap.ContainsKey(hwType))
                 {
-                    if (IsVaapiSupported(state))
+                    if (CheckVaapi(state, hwType, encodingOptions))
                     {
-                        return GetAvailableEncoder("h264_vaapi", defaultEncoder);
+                        var preferredEncoder = codecMap[hwType];
+
+                        if (_mediaEncoder.SupportsEncoder(preferredEncoder))
+                        {
+                            return preferredEncoder;
+                        }
                     }
                 }
+
             }
 
+            // Avoid performing a second attempt when the first one
+            // hasn't tried hardware encoding anyway.
+            encodingOptions.EnableHardwareEncoding = false;
             return defaultEncoder;
         }
 
-        private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder)
+        private bool CheckVaapi(EncodingJobInfo state, string hwType, EncodingOptions encodingOptions)
         {
-            if (_mediaEncoder.SupportsEncoder(preferredEncoder))
+            if (!string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase))
             {
-                return preferredEncoder;
+                // No vaapi requested, return OK.
+                return true;
             }
-            return defaultEncoder;
+
+            if (string.IsNullOrEmpty(encodingOptions.VaapiDevice))
+            {
+                // No device specified, return OK.
+                return true;
+            }
+
+            return IsVaapiSupported(state);
         }
 
         private bool IsVaapiSupported(EncodingJobInfo state)
@@ -340,7 +342,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public int GetVideoProfileScore(string profile)
         {
-            string[] list =
+            var list = new[]
             {
                 "ConstrainedBaseline",
                 "Baseline",
@@ -538,14 +540,54 @@ namespace MediaBrowser.Controller.MediaEncoding
                 ? string.Empty
                 : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture));
 
-            string fallbackFontParam = string.Empty;
+            // TODO
+            // var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf");
+            // string fallbackFontParam = string.Empty;
+
+            // if (!_fileSystem.FileExists(fallbackFontPath))
+            // {
+            //     _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath));
+            //     using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf"))
+            //     {
+            //         using (var fileStream = _fileSystem.GetFileStream(fallbackFontPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+            //         {
+            //             stream.CopyTo(fileStream);
+            //         }
+            //     }
+            // }
+
+            // fallbackFontParam = string.Format(":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
+
+            if (state.SubtitleStream.IsExternal)
+            {
+                var subtitlePath = state.SubtitleStream.Path;
+
+                var charsetParam = string.Empty;
+
+                if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
+                {
+                    var charenc = _subtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language, state.MediaSource.Protocol, CancellationToken.None).Result;
+
+                    if (!string.IsNullOrEmpty(charenc))
+                    {
+                        charsetParam = ":charenc=" + charenc;
+                    }
+                }
+
+                // TODO: Perhaps also use original_size=1920x800 ??
+                return string.Format("subtitles=filename='{0}'{1}{2}{3}",
+                    _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
+                    charsetParam,
+                    // fallbackFontParam,
+                    setPtsParam);
+            }
 
             var mediaPath = state.MediaPath ?? string.Empty;
 
-            return string.Format("subtitles='{0}:si={1}'{2}{3}",
+            return string.Format("subtitles='{0}:si={1}'{2}",
                 _mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
                 state.InternalSubtitleStreamOffset.ToString(_usCulture),
-                fallbackFontParam,
+                // fallbackFontParam,
                 setPtsParam);
         }
 
@@ -1039,51 +1081,72 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
             var bitrate = request.VideoBitRate;
 
+            // If specific values were requested, then force the caller to supply a bitrate as well
+            if (request.Height.HasValue && request.Width.HasValue)
+            {
+                return bitrate;
+            }
+
             if (videoStream != null)
             {
-                var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
-                                   request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
-                    request.Width.Value > videoStream.Width.Value;
+                if (bitrate.HasValue && videoStream.BitRate.HasValue)
+                {
+                    bitrate = Math.Min(videoStream.BitRate.Value, bitrate.Value);
+                }
 
-                // Don't allow bitrate increases unless upscaling
-                if (!isUpscaling)
+                if (bitrate.HasValue)
                 {
-                    if (bitrate.HasValue && videoStream.BitRate.HasValue)
+                    var inputVideoCodec = videoStream.Codec;
+                    bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+
+                    // If a max bitrate was requested, don't let the scaled bitrate exceed it
+                    if (request.VideoBitRate.HasValue)
                     {
-                        bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value);
+                        bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
                     }
                 }
             }
 
-            if (bitrate.HasValue)
-            {
-                var inputVideoCodec = videoStream == null ? null : videoStream.Codec;
-                bitrate = ResolutionNormalizer.ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+            return bitrate;
+        }
 
-                // If a max bitrate was requested, don't let the scaled bitrate exceed it
-                if (request.VideoBitRate.HasValue)
-                {
-                    bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
-                }
+        private static double GetVideoBitrateScaleFactor(string codec)
+        {
+            if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
+                StringHelper.EqualsIgnoreCase(codec, "hevc") ||
+                StringHelper.EqualsIgnoreCase(codec, "vp9"))
+            {
+                return .5;
             }
-
-            return bitrate;
+            return 1;
         }
 
-        private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
+        private static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
         {
-            if (sourceBitrate <= 2000000)
+            var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
+            var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
+            var scaleFactor = outputScaleFactor / inputScaleFactor;
+
+            if (bitrate <= 500000)
+            {
+                scaleFactor = Math.Max(scaleFactor, 4);
+            }
+            else if (bitrate <= 1000000)
+            {
+                scaleFactor = Math.Max(scaleFactor, 3);
+            }
+            else if (bitrate <= 2000000)
             {
-                sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
+                scaleFactor = Math.Max(scaleFactor, 2.5);
             }
-            else if (sourceBitrate <= 3000000)
+            else if (bitrate <= 3000000)
             {
-                sourceBitrate = Convert.ToInt32(sourceBitrate * 2);
+                scaleFactor = Math.Max(scaleFactor, 2);
             }
 
-            var bitrate = Math.Min(sourceBitrate, requestedBitrate);
+            var newBitrate = scaleFactor * bitrate;
 
-            return bitrate;
+            return Convert.ToInt32(newBitrate);
         }
 
         public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
@@ -1405,7 +1468,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                 videoSizeParam);
         }
 
-        private Tuple<int?, int?> GetFixedOutputSize(int? videoWidth,
+        private ValueTuple<int?, int?> GetFixedOutputSize(int? videoWidth,
             int? videoHeight,
             int? requestedWidth,
             int? requestedHeight,
@@ -1414,11 +1477,11 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
             if (!videoWidth.HasValue && !requestedWidth.HasValue)
             {
-                return new Tuple<int?, int?>(null, null);
+                return new ValueTuple<int?, int?>(null, null);
             }
             if (!videoHeight.HasValue && !requestedHeight.HasValue)
             {
-                return new Tuple<int?, int?>(null, null);
+                return new ValueTuple<int?, int?>(null, null);
             }
 
             decimal inputWidth = Convert.ToDecimal(videoWidth ?? requestedWidth);
@@ -1438,7 +1501,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             outputWidth = 2 * Math.Truncate(outputWidth / 2);
             outputHeight = 2 * Math.Truncate(outputHeight / 2);
 
-            return new Tuple<int?, int?>(Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
+            return new ValueTuple<int?, int?>(Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
         }
 
         public List<string> GetScalingFilters(int? videoWidth,
@@ -1669,7 +1732,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             var inputHeight = videoStream == null ? null : videoStream.Height;
             var threeDFormat = state.MediaSource.Video3DFormat;
 
-            var videoDecoder = GetVideoDecoder(state, options);
+            var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, options);
 
             filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight));
 
@@ -1851,7 +1914,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                 inputModifier += " -fflags " + string.Join("", flags.ToArray());
             }
 
-            var videoDecoder = GetVideoDecoder(state, encodingOptions);
+            var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
             if (!string.IsNullOrEmpty(videoDecoder))
             {
                 inputModifier += " " + videoDecoder;
@@ -1893,7 +1956,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             if (state.MediaSource.RequiresLooping)
             {
-                inputModifier += " -stream_loop -1";
+                inputModifier += " -stream_loop -1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2";
             }
 
             return inputModifier;
@@ -1989,7 +2052,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                 {
                     if (string.IsNullOrEmpty(requestedUrl))
                     {
-                        requestedUrl = "test." + videoRequest.OutputContainer;
+                        requestedUrl = "test." + videoRequest.Container;
                     }
 
                     videoRequest.VideoCodec = InferVideoCodec(requestedUrl);
@@ -2015,6 +2078,49 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
 
             state.MediaSource = mediaSource;
+
+            var request = state.BaseRequest;
+            if (!string.IsNullOrWhiteSpace(request.AudioCodec))
+            {
+                var supportedAudioCodecsList = request.AudioCodec.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+
+                ShiftAudioCodecsIfNeeded(supportedAudioCodecsList, state.AudioStream);
+
+                state.SupportedAudioCodecs = supportedAudioCodecsList.ToArray();
+
+                request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToAudioCodec(i))
+                    ?? state.SupportedAudioCodecs.FirstOrDefault();
+            }
+        }
+
+        private void ShiftAudioCodecsIfNeeded(List<string> audioCodecs, MediaStream audioStream)
+        {
+            // Nothing to do here
+            if (audioCodecs.Count < 2)
+            {
+                return;
+            }
+
+            var inputChannels = audioStream == null ? 6 : audioStream.Channels ?? 6;
+            if (inputChannels >= 6)
+            {
+                return;
+            }
+
+            // Transcoding to 2ch ac3 almost always causes a playback failure
+            // Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used
+            var shiftAudioCodecs = new[] { "ac3", "eac3" };
+            if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparer.OrdinalIgnoreCase)))
+            {
+                return;
+            }
+
+            while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparer.OrdinalIgnoreCase))
+            {
+                var removed = shiftAudioCodecs[0];
+                audioCodecs.RemoveAt(0);
+                audioCodecs.Add(removed);
+            }
         }
 
         private void NormalizeSubtitleEmbed(EncodingJobInfo state)
@@ -2035,17 +2141,17 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <summary>
         /// Gets the name of the output video codec
         /// </summary>
-        protected string GetVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
+        protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
         {
             if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
             {
                 return null;
             }
 
-            return GetVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
+            return this.GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
         }
 
-        public string GetVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
+        public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
         {
             // Only use alternative encoders for video files.
             // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
@@ -2070,6 +2176,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                                 // qsv decoder does not support 10-bit input
                                 if ((videoStream.BitDepth ?? 8) > 8)
                                 {
+                                    encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
                                     return null;
                                 }
                                 return "-c:v h264_qsv ";
@@ -2204,6 +2311,11 @@ namespace MediaBrowser.Controller.MediaEncoding
 
                 else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
                 {
+                    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+                    {
+                        return "-hwaccel dxva2";
+                    }
+
                     switch (videoStream.Codec.ToLower())
                     {
                         case "avc":
@@ -2223,6 +2335,9 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
             }
 
+            // Avoid a second attempt if no hardware acceleration is being used
+            encodingOptions.HardwareDecodingCodecs = Array.Empty<string>();
+
             // leave blank so ffmpeg will decide
             return null;
         }

+ 52 - 21
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Dto;
@@ -12,13 +11,17 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Session;
 using Microsoft.Extensions.Logging;
+using System.IO;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Controller.Library;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.MediaEncoding
 {
     // For now, a common base class until the API and MediaEncoding classes are unified
-    public abstract class EncodingJobInfo
+    public class EncodingJobInfo
     {
-        private readonly ILogger _logger;
+        protected readonly IMediaSourceManager MediaSourceManager;
 
         public MediaStream VideoStream { get; set; }
         public VideoType VideoType { get; set; }
@@ -43,6 +46,21 @@ namespace MediaBrowser.Controller.MediaEncoding
 
         public bool ReadInputAtNativeFramerate { get; set; }
 
+        public string OutputFilePath { get; set; }
+
+        public string MimeType { get; set; }
+        public long? EncodingDurationTicks { get; set; }
+
+        public string GetMimeType(string outputPath, bool enableStreamDefault = true)
+        {
+            if (!string.IsNullOrEmpty(MimeType))
+            {
+                return MimeType;
+            }
+
+            return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
+        }
+
         private TranscodeReason[] _transcodeReasons = null;
         public TranscodeReason[] TranscodeReasons
         {
@@ -266,9 +284,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         public bool IsVideoRequest { get; set; }
         public TranscodingJobType TranscodingType { get; set; }
 
-        public EncodingJobInfo(ILogger logger, IMediaSourceManager unused, TranscodingJobType jobType)
+        public EncodingJobInfo(TranscodingJobType jobType)
         {
-            _logger = logger;
             TranscodingType = jobType;
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             PlayableStreamFileNames = Array.Empty<string>();
@@ -602,6 +619,28 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
         }
 
+        public string ActualOutputAudioCodec
+        {
+            get
+            {
+                var codec = OutputAudioCodec;
+
+                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+                {
+                    var stream = AudioStream;
+
+                    if (stream != null)
+                    {
+                        return stream.Codec;
+                    }
+
+                    return null;
+                }
+
+                return codec;
+            }
+        }
+
         public bool? IsTargetInterlaced
         {
             get
@@ -657,6 +696,14 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
         }
 
+        public int HlsListSize
+        {
+            get
+            {
+                return 0;
+            }
+        }
+
         private int? GetMediaStreamCount(MediaStreamType type, int limit)
         {
             var count = MediaSource.GetStreamCount(type);
@@ -668,22 +715,6 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             return count;
         }
-        protected void DisposeIsoMount()
-        {
-            if (IsoMount != null)
-            {
-                try
-                {
-                    IsoMount.Dispose();
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError(ex, "Error disposing iso mount");
-                }
-
-                IsoMount = null;
-            }
-        }
 
         public IProgress<double> Progress { get; set; }
         public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)

+ 29 - 12
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs

@@ -11,9 +11,8 @@ namespace MediaBrowser.Controller.MediaEncoding
     {
         public string OutputDirectory { get; set; }
         public string ItemId { get; set; }
-        public string MediaSourceId { get; set; }
-        public string AudioCodec { get; set; }
 
+        public string TempDirectory { get; set; }
         public bool ReadInputAtNativeFramerate { get; set; }
 
         /// <summary>
@@ -22,15 +21,16 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
         public bool HasFixedResolution => Width.HasValue || Height.HasValue;
 
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+        public DeviceProfile DeviceProfile { get; set; }
+
         public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
         {
-            OutputContainer = info.Container;
+            Container = info.Container;
             StartTimeTicks = info.StartPositionTicks;
             MaxWidth = info.MaxWidth;
             MaxHeight = info.MaxHeight;
             MaxFramerate = info.MaxFramerate;
-            ItemId = info.ItemId.ToString("");
+            Id = info.ItemId;
             MediaSourceId = info.MediaSourceId;
             AudioCodec = info.TargetAudioCodec.FirstOrDefault();
             MaxAudioChannels = info.GlobalMaxAudioChannels;
@@ -55,6 +55,29 @@ namespace MediaBrowser.Controller.MediaEncoding
     // For now until api and media encoding layers are unified
     public class BaseEncodingJobOptions
     {
+        /// <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; }
+
+        [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string Container { get; set; }
+
+        /// <summary>
+        /// Gets or sets the audio codec.
+        /// </summary>
+        /// <value>The audio codec.</value>
+        [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string AudioCodec { get; set; }
+
         [ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool EnableAutoStreamCopy { get; set; }
 
@@ -180,7 +203,7 @@ namespace MediaBrowser.Controller.MediaEncoding
         public bool RequireNonAnamorphic { get; set; }
         public int? TranscodingMaxAudioChannels { get; set; }
         public int? CpuCoreLimit { get; set; }
-        public string OutputContainer { get; set; }
+
         public string LiveStreamId { get; set; }
 
         public bool EnableMpegtsM2TsMode { get; set; }
@@ -244,11 +267,5 @@ namespace MediaBrowser.Controller.MediaEncoding
             Context = EncodingContext.Streaming;
             StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
-
-        public string TempDirectory { get; set; }
-        public string DeviceId { get; set; }
-        public Guid Id { get; set; }
-        public string Container { get; set; }
-        public DeviceProfile DeviceProfile { get; set; }
     }
 }

+ 11 - 51
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -15,7 +15,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         public bool IsCancelled { get; internal set; }
 
         public Stream LogFileStream { get; set; }
-        public IProgress<double> Progress { get; set; }
         public TaskCompletionSource<bool> TaskCompletionSource;
 
         public EncodingJobOptions Options
@@ -24,34 +23,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
             set => BaseRequest = value;
         }
 
-        public string Id { get; set; }
+        public Guid Id { get; set; }
 
-        public string MimeType { get; set; }
         public bool EstimateContentLength { get; set; }
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
-        public long? EncodingDurationTicks { get; set; }
 
         public string ItemType { get; set; }
 
-        public string GetMimeType(string outputPath)
-        {
-            if (!string.IsNullOrEmpty(MimeType))
-            {
-                return MimeType;
-            }
-
-            return MimeTypes.GetMimeType(outputPath);
-        }
-
         private readonly ILogger _logger;
         private readonly IMediaSourceManager _mediaSourceManager;
 
         public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) :
-            base(logger, mediaSourceManager, TranscodingJobType.Progressive)
+            base(TranscodingJobType.Progressive)
         {
             _logger = logger;
             _mediaSourceManager = mediaSourceManager;
-            Id = Guid.NewGuid().ToString("N");
+            Id = Guid.NewGuid();
 
             _logger = logger;
             TaskCompletionSource = new TaskCompletionSource<bool>();
@@ -61,6 +48,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
             DisposeLiveStream();
             DisposeLogStream();
+            DisposeIsoMount();
         }
 
         private void DisposeLogStream()
@@ -95,49 +83,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
             }
         }
 
-        public string OutputFilePath { get; set; }
 
-        public string ActualOutputVideoCodec
+        private void DisposeIsoMount()
         {
-            get
+            if (IsoMount != null)
             {
-                var codec = OutputVideoCodec;
-
-                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+                try
                 {
-                    var stream = VideoStream;
-
-                    if (stream != null)
-                    {
-                        return stream.Codec;
-                    }
-
-                    return null;
+                    IsoMount.Dispose();
                 }
-
-                return codec;
-            }
-        }
-
-        public string ActualOutputAudioCodec
-        {
-            get
-            {
-                var codec = OutputAudioCodec;
-
-                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+                catch (Exception ex)
                 {
-                    var stream = AudioStream;
-
-                    if (stream != null)
-                    {
-                        return stream.Codec;
-                    }
-
-                    return null;
+                    _logger.LogError("Error disposing iso mount", ex);
                 }
 
-                return codec;
+                IsoMount = null;
             }
         }