فهرست منبع

Merge pull request #2534 from MediaBrowser/dev

Dev
Luke 8 سال پیش
والد
کامیت
22f8b2122d

+ 60 - 19
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -3204,6 +3204,40 @@ namespace Emby.Server.Implementations.Data
             }
         }
 
+        private bool IsAlphaNumeric(string str)
+        {
+            if (string.IsNullOrWhiteSpace(str))
+                return false;
+
+            for (int i = 0; i < str.Length; i++)
+            {
+                if (!(char.IsLetter(str[i])) && (!(char.IsNumber(str[i]))))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private bool IsValidType(string value)
+        {
+            return IsAlphaNumeric(value);
+        }
+
+        private bool IsValidMediaType(string value)
+        {
+            return IsAlphaNumeric(value);
+        }
+
+        private bool IsValidId(string value)
+        {
+            return IsAlphaNumeric(value);
+        }
+
+        private bool IsValidPersonType(string value)
+        {
+            return IsAlphaNumeric(value);
+        }
+
         private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "")
         {
             if (query.IsResumable ?? false)
@@ -3423,9 +3457,9 @@ namespace Emby.Server.Implementations.Data
                     statement.TryBind("@ChannelId", query.ChannelIds[0]);
                 }
             }
-            if (query.ChannelIds.Length > 1)
+            else if (query.ChannelIds.Length > 1)
             {
-                var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i + "'").ToArray());
+                var inClause = string.Join(",", query.ChannelIds.Where(IsValidId).Select(i => "'" + i + "'").ToArray());
                 whereClauses.Add(string.Format("ChannelId in ({0})", inClause));
             }
 
@@ -4157,17 +4191,18 @@ namespace Emby.Server.Implementations.Data
                     whereClauses.Add("(IsVirtualItem=0 OR PremiereDate < DATETIME('now'))");
                 }
             }
-            if (query.MediaTypes.Length == 1)
+            var queryMediaTypes = query.MediaTypes.Where(IsValidMediaType).ToArray();
+            if (queryMediaTypes.Length == 1)
             {
                 whereClauses.Add("MediaType=@MediaTypes");
                 if (statement != null)
                 {
-                    statement.TryBind("@MediaTypes", query.MediaTypes[0]);
+                    statement.TryBind("@MediaTypes", queryMediaTypes[0]);
                 }
             }
-            if (query.MediaTypes.Length > 1)
+            else if (queryMediaTypes.Length > 1)
             {
-                var val = string.Join(",", query.MediaTypes.Select(i => "'" + i + "'").ToArray());
+                var val = string.Join(",", queryMediaTypes.Select(i => "'" + i + "'").ToArray());
 
                 whereClauses.Add("MediaType in (" + val + ")");
             }
@@ -4273,7 +4308,9 @@ namespace Emby.Server.Implementations.Data
             //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0;
             var enableItemsByName = query.IncludeItemsByName ?? false;
 
-            if (query.TopParentIds.Length == 1)
+            var queryTopParentIds = query.TopParentIds.Where(IsValidId).ToArray();
+
+            if (queryTopParentIds.Length == 1)
             {
                 if (enableItemsByName)
                 {
@@ -4289,12 +4326,12 @@ namespace Emby.Server.Implementations.Data
                 }
                 if (statement != null)
                 {
-                    statement.TryBind("@TopParentId", query.TopParentIds[0]);
+                    statement.TryBind("@TopParentId", queryTopParentIds[0]);
                 }
             }
-            if (query.TopParentIds.Length > 1)
+            else if (queryTopParentIds.Length > 1)
             {
-                var val = string.Join(",", query.TopParentIds.Select(i => "'" + i + "'").ToArray());
+                var val = string.Join(",", queryTopParentIds.Select(i => "'" + i + "'").ToArray());
 
                 if (enableItemsByName)
                 {
@@ -4544,7 +4581,7 @@ namespace Emby.Server.Implementations.Data
                 return result;
             }
 
-            return new[] { value };
+            return new[] { value }.Where(IsValidType);
         }
 
         public async Task DeleteItem(Guid id, CancellationToken cancellationToken)
@@ -4696,31 +4733,35 @@ namespace Emby.Server.Implementations.Data
                     statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
                 }
             }
-            if (query.PersonTypes.Count == 1)
+            var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
+
+            if (queryPersonTypes.Count == 1)
             {
                 whereClauses.Add("PersonType=@PersonType");
                 if (statement != null)
                 {
-                    statement.TryBind("@PersonType", query.PersonTypes[0]);
+                    statement.TryBind("@PersonType", queryPersonTypes[0]);
                 }
             }
-            if (query.PersonTypes.Count > 1)
+            else if (queryPersonTypes.Count > 1)
             {
-                var val = string.Join(",", query.PersonTypes.Select(i => "'" + i + "'").ToArray());
+                var val = string.Join(",", queryPersonTypes.Select(i => "'" + i + "'").ToArray());
 
                 whereClauses.Add("PersonType in (" + val + ")");
             }
-            if (query.ExcludePersonTypes.Count == 1)
+            var queryExcludePersonTypes = query.ExcludePersonTypes.Where(IsValidPersonType).ToList();
+
+            if (queryExcludePersonTypes.Count == 1)
             {
                 whereClauses.Add("PersonType<>@PersonType");
                 if (statement != null)
                 {
-                    statement.TryBind("@PersonType", query.ExcludePersonTypes[0]);
+                    statement.TryBind("@PersonType", queryExcludePersonTypes[0]);
                 }
             }
-            if (query.ExcludePersonTypes.Count > 1)
+            else if (queryExcludePersonTypes.Count > 1)
             {
-                var val = string.Join(",", query.ExcludePersonTypes.Select(i => "'" + i + "'").ToArray());
+                var val = string.Join(",", queryExcludePersonTypes.Select(i => "'" + i + "'").ToArray());
 
                 whereClauses.Add("PersonType not in (" + val + ")");
             }

+ 0 - 2
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -85,7 +85,6 @@
     <Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
     <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
     <Compile Include="HttpServer\FileWriter.cs" />
-    <Compile Include="HttpServer\GetSwaggerResource.cs" />
     <Compile Include="HttpServer\HttpListenerHost.cs" />
     <Compile Include="HttpServer\HttpResultFactory.cs" />
     <Compile Include="HttpServer\LoggerUtils.cs" />
@@ -103,7 +102,6 @@
     <Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
     <Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
     <Compile Include="HttpServer\StreamWriter.cs" />
-    <Compile Include="HttpServer\SwaggerService.cs" />
     <Compile Include="Images\BaseDynamicImageProvider.cs" />
     <Compile Include="IO\FileRefresher.cs" />
     <Compile Include="IO\MbLinkShortcutHandler.cs" />

+ 0 - 17
Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs

@@ -1,17 +0,0 @@
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    /// <summary>
-    /// Class GetDashboardResource
-    /// </summary>
-    [Route("/swagger-ui/{ResourceName*}", "GET")]
-    public class GetSwaggerResource
-    {
-        /// <summary>
-        /// Gets or sets the name.
-        /// </summary>
-        /// <value>The name.</value>
-        public string ResourceName { get; set; }
-    }
-}

+ 0 - 47
Emby.Server.Implementations/HttpServer/SwaggerService.cs

@@ -1,47 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Net;
-using System.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-
-namespace Emby.Server.Implementations.HttpServer
-{
-    public class SwaggerService : IService, IRequiresRequest
-    {
-        private readonly IServerApplicationPaths _appPaths;
-        private readonly IFileSystem _fileSystem;
-
-        public SwaggerService(IServerApplicationPaths appPaths, IFileSystem fileSystem, IHttpResultFactory resultFactory)
-        {
-            _appPaths = appPaths;
-            _fileSystem = fileSystem;
-            _resultFactory = resultFactory;
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public object Get(GetSwaggerResource request)
-        {
-            var swaggerDirectory = Path.Combine(_appPaths.ApplicationResourcesPath, "swagger-ui");
-
-            var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', _fileSystem.DirectorySeparatorChar));
-
-            return _resultFactory.GetStaticFileResult(Request, requestedFile).Result;
-        }
-
-        /// <summary>
-        /// Gets or sets the result factory.
-        /// </summary>
-        /// <value>The result factory.</value>
-        private readonly IHttpResultFactory _resultFactory;
-
-        /// <summary>
-        /// Gets or sets the request context.
-        /// </summary>
-        /// <value>The request context.</value>
-        public IRequest Request { get; set; }
-    }
-}

+ 4 - 2
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs

@@ -109,11 +109,13 @@ namespace MediaBrowser.Api.Playback.Hls
         public Task<object> Get(GetHlsVideoSegmentLegacy request)
         {
             var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
-            file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file);
+
+            var transcodeFolderPath = _config.ApplicationPaths.TranscodingTempPath;
+            file = Path.Combine(transcodeFolderPath, file);
 
             var normalizedPlaylistId = request.PlaylistId;
 
-            var playlistPath = _fileSystem.GetFilePaths(_config.ApplicationPaths.TranscodingTempPath)
+            var playlistPath = _fileSystem.GetFilePaths(transcodeFolderPath)
                 .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
 
             return GetFileResult(file, playlistPath);

+ 0 - 3
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -15,9 +15,6 @@ using System.Globalization;
 using System.IO;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Services;
 
 namespace MediaBrowser.Api.Playback.Progressive

+ 1 - 183
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -1,4 +1,3 @@
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Dlna;
@@ -7,15 +6,8 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Serialization;
-using System;
-using System.IO;
-using System.Linq;
 using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Services;
 
 namespace MediaBrowser.Api.Playback.Progressive
@@ -100,181 +92,7 @@ namespace MediaBrowser.Api.Playback.Progressive
         {
             var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
 
-            // Get the output codec name
-            var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
-
-            var format = string.Empty;
-            var keyFrame = string.Empty;
-
-            if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase))
-            {
-                // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
-                format = " -f mp4 -movflags frag_keyframe+empty_moov";
-            }
-
-            var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
-
-            var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
-
-            var subtitleArguments = state.SubtitleStream != null &&
-                                    state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed
-                ? GetSubtitleArguments(state)
-                : string.Empty;
-
-            return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
-                inputModifier,
-                EncodingHelper.GetInputArgument(state, encodingOptions),
-                keyFrame,
-                EncodingHelper.GetMapArgs(state),
-                GetVideoArguments(state, videoCodec),
-                threads,
-                GetAudioArguments(state),
-                subtitleArguments,
-                format,
-                outputPath
-                ).Trim();
-        }
-
-        private string GetSubtitleArguments(StreamState state)
-        {
-            var format = state.SupportedSubtitleCodecs.FirstOrDefault();
-            string codec;
-
-            if (string.IsNullOrWhiteSpace(format) || string.Equals(format, state.SubtitleStream.Codec, StringComparison.OrdinalIgnoreCase))
-            {
-                codec = "copy";
-            }
-            else
-            {
-                codec = format;
-            }
-
-            return " -codec:s:0 " + codec;
-        }
-
-        /// <summary>
-        /// Gets video arguments to pass to ffmpeg
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <param name="videoCodec">The video codec.</param>
-        /// <returns>System.String.</returns>
-        private string GetVideoArguments(StreamState state, string videoCodec)
-        {
-            var args = "-codec:v:0 " + videoCodec;
-
-            if (state.EnableMpegtsM2TsMode)
-            {
-                args += " -mpegts_m2ts_mode 1";
-            }
-
-            if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
-                {
-                    args += " -bsf:v h264_mp4toannexb";
-                }
-
-                if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps)
-                {
-                    args += " -copyts -avoid_negative_ts disabled -start_at_zero";
-                }
-
-                if (!state.RunTimeTicks.HasValue)
-                {
-                    args += " -flags -global_header -fflags +genpts";
-                }
-
-                return args;
-            }
-
-            var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
-                5.ToString(UsCulture));
-
-            args += keyFrameArg;
-
-            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
-
-            var hasCopyTs = false;
-            // Add resolution params, if specified
-            if (!hasGraphicalSubs)
-            {
-                var outputSizeParam = EncodingHelper.GetOutputSizeParam(state, videoCodec);
-                args += outputSizeParam;
-                hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
-            }
-
-            if (state.RunTimeTicks.HasValue && state.VideoRequest.CopyTimestamps)
-            {
-                if (!hasCopyTs)
-                {
-                    args += " -copyts";
-                }
-                args += " -avoid_negative_ts disabled -start_at_zero";
-            }
-
-            var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
-            var qualityParam = EncodingHelper.GetVideoQualityParam(state, videoCodec, encodingOptions, GetDefaultH264Preset());
-
-            if (!string.IsNullOrEmpty(qualityParam))
-            {
-                args += " " + qualityParam.Trim();
-            }
-
-            // This is for internal graphical subs
-            if (hasGraphicalSubs)
-            {
-                args += EncodingHelper.GetGraphicalSubtitleParam(state, videoCodec);
-            }
-
-            if (!state.RunTimeTicks.HasValue)
-            {
-                args += " -flags -global_header";
-            }
-
-            return args;
-        }
-
-        /// <summary>
-        /// Gets audio arguments to pass to ffmpeg
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <returns>System.String.</returns>
-        private string GetAudioArguments(StreamState state)
-        {
-            // If the video doesn't have an audio stream, return a default.
-            if (state.AudioStream == null && state.VideoStream != null)
-            {
-                return string.Empty;
-            }
-
-            // Get the output codec name
-            var codec = EncodingHelper.GetAudioEncoder(state);
-
-            var args = "-codec:a:0 " + codec;
-
-            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                return args;
-            }
-
-            // Add the number of audio channels
-            var channels = state.OutputAudioChannels;
-
-            if (channels.HasValue)
-            {
-                args += " -ac " + channels.Value;
-            }
-
-            var bitrate = state.OutputAudioBitrate;
-
-            if (bitrate.HasValue)
-            {
-                args += " -ab " + bitrate.Value.ToString(UsCulture);
-            }
-
-            args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), false);
-
-            return args;
+            return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
         }
     }
 }

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

@@ -32,8 +32,6 @@ namespace MediaBrowser.Api.Playback
         [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; }
 
-        public string SubtitleCodec { 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; }
 

+ 0 - 4
MediaBrowser.Api/Playback/StreamState.cs

@@ -120,7 +120,6 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
-        public List<string> SupportedSubtitleCodecs { get; set; }
         public string UserAgent { get; set; }
         public TranscodingJobType TranscodingType { get; set; }
 
@@ -129,14 +128,12 @@ namespace MediaBrowser.Api.Playback
         {
             _mediaSourceManager = mediaSourceManager;
             _logger = logger;
-            SupportedSubtitleCodecs = new List<string>();
             TranscodingType = transcodingType;
         }
 
         public string MimeType { get; set; }
 
         public bool EstimateContentLength { get; set; }
-        public bool EnableMpegtsM2TsMode { get; set; }
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
 
         public long? EncodingDurationTicks { get; set; }
@@ -209,7 +206,6 @@ namespace MediaBrowser.Api.Playback
         }
 
         public string OutputFilePath { get; set; }
-        public int? OutputAudioBitrate;
 
         public string ActualOutputVideoCodec
         {

+ 182 - 0
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -1736,5 +1736,187 @@ namespace MediaBrowser.Controller.MediaEncoding
 
             return threads;
         }
+
+        public string GetSubtitleEmbedArguments(EncodingJobInfo state)
+        {
+            if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
+            {
+                return string.Empty;
+            }
+
+            var format = state.SupportedSubtitleCodecs.FirstOrDefault();
+            string codec;
+
+            if (string.IsNullOrWhiteSpace(format) || string.Equals(format, state.SubtitleStream.Codec, StringComparison.OrdinalIgnoreCase))
+            {
+                codec = "copy";
+            }
+            else
+            {
+                codec = format;
+            }
+
+            // Muxing in dvbsub via either copy or -codec dvbsub does not seem to work
+            // It doesn't throw any errors but vlc on android will not render them
+            // They will need to be converted to an alternative format
+            // TODO: This is incorrectly assuming that dvdsub will be supported by the player
+            // The api will need to be expanded to accomodate this.
+            if (string.Equals(state.SubtitleStream.Codec, "DVBSUB", StringComparison.OrdinalIgnoreCase))
+            {
+                codec = "dvdsub";
+            }
+
+            var args = " -codec:s:0 " + codec;
+
+            args += " -disposition:s:0 default";
+
+            return args;
+        }
+
+        public string GetProgressiveVideoFullCommandLine(EncodingJobInfo state, EncodingOptions encodingOptions, string outputPath, string defaultH264Preset)
+        {
+            // Get the output codec name
+            var videoCodec = GetVideoEncoder(state, encodingOptions);
+
+            var format = string.Empty;
+            var keyFrame = string.Empty;
+
+            if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
+                state.BaseRequest.Context == EncodingContext.Streaming)
+            {
+                // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
+                format = " -f mp4 -movflags frag_keyframe+empty_moov";
+            }
+
+            var threads = GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
+
+            var inputModifier = GetInputModifier(state, encodingOptions);
+
+            return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7}{8} -y \"{9}\"",
+                inputModifier,
+                GetInputArgument(state, encodingOptions),
+                keyFrame,
+                GetMapArgs(state),
+                GetProgressiveVideoArguments(state, encodingOptions, videoCodec, defaultH264Preset),
+                threads,
+                GetProgressiveVideoAudioArguments(state, encodingOptions),
+                GetSubtitleEmbedArguments(state),
+                format,
+                outputPath
+                ).Trim();
+        }
+
+        public string GetProgressiveVideoArguments(EncodingJobInfo state, EncodingOptions encodingOptions, string videoCodec, string defaultH264Preset)
+        {
+            var args = "-codec:v:0 " + videoCodec;
+
+            if (state.EnableMpegtsM2TsMode)
+            {
+                args += " -mpegts_m2ts_mode 1";
+            }
+
+            if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+            {
+                if (state.VideoStream != null && IsH264(state.VideoStream) && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
+                {
+                    args += " -bsf:v h264_mp4toannexb";
+                }
+
+                if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
+                {
+                    args += " -copyts -avoid_negative_ts disabled -start_at_zero";
+                }
+
+                if (!state.RunTimeTicks.HasValue)
+                {
+                    args += " -flags -global_header -fflags +genpts";
+                }
+
+                return args;
+            }
+
+            var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
+                5.ToString(_usCulture));
+
+            args += keyFrameArg;
+
+            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.BaseRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
+
+            var hasCopyTs = false;
+            // Add resolution params, if specified
+            if (!hasGraphicalSubs)
+            {
+                var outputSizeParam = GetOutputSizeParam(state, videoCodec);
+                args += outputSizeParam;
+                hasCopyTs = outputSizeParam.IndexOf("copyts", StringComparison.OrdinalIgnoreCase) != -1;
+            }
+
+            if (state.RunTimeTicks.HasValue && state.BaseRequest.CopyTimestamps)
+            {
+                if (!hasCopyTs)
+                {
+                    args += " -copyts";
+                }
+                args += " -avoid_negative_ts disabled -start_at_zero";
+            }
+
+            var qualityParam = GetVideoQualityParam(state, videoCodec, encodingOptions, defaultH264Preset);
+
+            if (!string.IsNullOrEmpty(qualityParam))
+            {
+                args += " " + qualityParam.Trim();
+            }
+
+            // This is for internal graphical subs
+            if (hasGraphicalSubs)
+            {
+                args += GetGraphicalSubtitleParam(state, videoCodec);
+            }
+
+            if (!state.RunTimeTicks.HasValue)
+            {
+                args += " -flags -global_header";
+            }
+
+            return args;
+        }
+
+        public string GetProgressiveVideoAudioArguments(EncodingJobInfo state, EncodingOptions encodingOptions)
+        {
+            // If the video doesn't have an audio stream, return a default.
+            if (state.AudioStream == null && state.VideoStream != null)
+            {
+                return string.Empty;
+            }
+
+            // Get the output codec name
+            var codec = GetAudioEncoder(state);
+
+            var args = "-codec:a:0 " + codec;
+
+            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+            {
+                return args;
+            }
+
+            // Add the number of audio channels
+            var channels = state.OutputAudioChannels;
+
+            if (channels.HasValue)
+            {
+                args += " -ac " + channels.Value;
+            }
+
+            var bitrate = state.OutputAudioBitrate;
+
+            if (bitrate.HasValue)
+            {
+                args += " -ab " + bitrate.Value.ToString(_usCulture);
+            }
+            
+            args += " " + GetAudioFilterParam(state, encodingOptions, false);
+
+            return args;
+        }
     }
 }

+ 6 - 1
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs

@@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.MediaEncoding
         public int? OutputVideoBitrate { get; set; }
         public MediaStream SubtitleStream { get; set; }
         public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+        public List<string> SupportedSubtitleCodecs { get; set; }
 
         public int InternalSubtitleStreamOffset { get; set; }
         public MediaSourceInfo MediaSource { get; set; }
@@ -52,6 +53,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         public string InputContainer { get; set; }
         public IsoType? IsoType { get; set; }
 
+        public bool EnableMpegtsM2TsMode { get; set; }
+
         public BaseEncodingJobOptions BaseRequest { get; set; }
 
         public long? StartTimeTicks
@@ -64,6 +67,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             get { return BaseRequest.CopyTimestamps; }
         }
 
+        public int? OutputAudioBitrate;
         public int? OutputAudioChannels;
         public int? OutputAudioSampleRate;
         public bool DeInterlace { get; set; }
@@ -74,8 +78,9 @@ namespace MediaBrowser.Controller.MediaEncoding
             _logger = logger;
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             PlayableStreamFileNames = new List<string>();
+            SupportedAudioCodecs = new List<string>();
             SupportedVideoCodecs = new List<string>();
-            SupportedVideoCodecs = new List<string>();
+            SupportedSubtitleCodecs = new List<string>();
         }
 
         /// <summary>

+ 5 - 1
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs

@@ -14,7 +14,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         public string AudioCodec { get; set; }
 
         public DeviceProfile DeviceProfile { get; set; }
-        public EncodingContext Context { get; set; }
 
         public bool ReadInputAtNativeFramerate { get; set; }
 
@@ -198,6 +197,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string VideoCodec { get; set; }
 
+        public string SubtitleCodec { get; set; }
+
         /// <summary>
         /// Gets or sets the index of the audio stream.
         /// </summary>
@@ -212,9 +213,12 @@ namespace MediaBrowser.Controller.MediaEncoding
         [ApiMember(Name = "VideoStreamIndex", Description = "Optional. The index of the video stream to use. If omitted the first video stream will be used.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? VideoStreamIndex { get; set; }
 
+        public EncodingContext Context { get; set; }
+
         public BaseEncodingJobOptions()
         {
             EnableAutoStreamCopy = true;
+            Context = EncodingContext.Streaming;
         }
     }
 }

+ 2 - 2
MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
         }
 
-        protected override Task<string> GetCommandLineArguments(EncodingJob state)
+        protected override string GetCommandLineArguments(EncodingJob state)
         {
             var audioTranscodeParams = new List<string>();
 
@@ -78,7 +78,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 mapArgs,
                 metadata).Trim();
 
-            return Task.FromResult(result);
+            return result;
         }
 
         protected override string GetOutputFileExtension(EncodingJob state)

+ 3 - 3
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -66,7 +66,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             IProgress<double> progress,
             CancellationToken cancellationToken)
         {
-            var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager)
+            var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager, ConfigurationManager, MediaEncoder)
                 .CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
 
             encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
@@ -76,7 +76,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false);
 
-            var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false);
+            var commandLineArgs = GetCommandLineArguments(encodingJob);
 
             var process = ProcessFactory.Create(new ProcessOptions
             {
@@ -265,7 +265,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
         }
 
-        protected abstract Task<string> GetCommandLineArguments(EncodingJob job);
+        protected abstract string GetCommandLineArguments(EncodingJob job);
 
         private string GetOutputFilePath(EncodingJob state)
         {

+ 0 - 2
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
         public string MimeType { get; set; }
         public bool EstimateContentLength { get; set; }
-        public bool EnableMpegtsM2TsMode { get; set; }
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
         public long? EncodingDurationTicks { get; set; }
         public string LiveStreamId { get; set; }
@@ -109,7 +108,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
         }
 
         public string OutputFilePath { get; set; }
-        public int? OutputAudioBitrate;
 
         public string ActualOutputVideoCodec
         {

+ 10 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs

@@ -22,15 +22,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly ILibraryManager _libraryManager;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IConfigurationManager _config;
+        private readonly IMediaEncoder _mediaEncoder;
 
         protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         
-        public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config)
+        public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, IConfigurationManager config, IMediaEncoder mediaEncoder)
         {
             _logger = logger;
             _libraryManager = libraryManager;
             _mediaSourceManager = mediaSourceManager;
             _config = config;
+            _mediaEncoder = mediaEncoder;
         }
 
         public async Task<EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken)
@@ -61,6 +63,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault();
             }
 
+            if (!string.IsNullOrWhiteSpace(request.SubtitleCodec))
+            {
+                state.SupportedSubtitleCodecs = request.SubtitleCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+                request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToSubtitleCodec(i))
+                    ?? state.SupportedSubtitleCodecs.FirstOrDefault();
+            }
+
             var item = _libraryManager.GetItemById(request.ItemId);
             state.ItemType = item.GetType().Name;
 

+ 2 - 133
MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs

@@ -18,143 +18,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
         {
         }
 
-        protected override async Task<string> GetCommandLineArguments(EncodingJob state)
+        protected override string GetCommandLineArguments(EncodingJob state)
         {
             // Get the output codec name
             var encodingOptions = GetEncodingOptions();
-            var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
 
-            var format = string.Empty;
-            var keyFrame = string.Empty;
-
-            if (string.Equals(Path.GetExtension(state.OutputFilePath), ".mp4", StringComparison.OrdinalIgnoreCase) &&
-                state.Options.Context == EncodingContext.Streaming)
-            {
-                // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
-                format = " -f mp4 -movflags frag_keyframe+empty_moov";
-            }
-
-            var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase));
-
-            var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
-
-            var videoArguments = await GetVideoArguments(state, videoCodec).ConfigureAwait(false);
-
-            return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
-                inputModifier,
-                EncodingHelper.GetInputArgument(state, encodingOptions),
-                keyFrame,
-                EncodingHelper.GetMapArgs(state),
-                videoArguments,
-                threads,
-                GetAudioArguments(state),
-                format,
-                state.OutputFilePath
-                ).Trim();
-        }
-
-        /// <summary>
-        /// Gets video arguments to pass to ffmpeg
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <param name="videoCodec">The video codec.</param>
-        /// <returns>System.String.</returns>
-        private async Task<string> GetVideoArguments(EncodingJob state, string videoCodec)
-        {
-            var args = "-codec:v:0 " + videoCodec;
-
-            if (state.EnableMpegtsM2TsMode)
-            {
-                args += " -mpegts_m2ts_mode 1";
-            }
-
-            var isOutputMkv = string.Equals(state.Options.OutputContainer, "mkv", StringComparison.OrdinalIgnoreCase);
-
-            if (state.RunTimeTicks.HasValue)
-            {
-                //args += " -copyts -avoid_negative_ts disabled -start_at_zero";
-            }
-
-            if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                if (state.VideoStream != null && EncodingHelper.IsH264(state.VideoStream) && string.Equals(state.Options.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
-                {
-                    args += " -bsf:v h264_mp4toannexb";
-                }
-
-                return args;
-            }
-
-            var keyFrameArg = string.Format(" -force_key_frames \"expr:gte(t,n_forced*{0})\"",
-                5.ToString(UsCulture));
-
-            args += keyFrameArg;
-
-            var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.Options.SubtitleMethod == SubtitleDeliveryMethod.Encode;
-
-            // Add resolution params, if specified
-            if (!hasGraphicalSubs)
-            {
-                args += EncodingHelper.GetOutputSizeParam(state, videoCodec);
-            }
-
-            var qualityParam = EncodingHelper.GetVideoQualityParam(state, videoCodec, GetEncodingOptions(), "superfast");
-
-            if (!string.IsNullOrEmpty(qualityParam))
-            {
-                args += " " + qualityParam.Trim();
-            }
-
-            // This is for internal graphical subs
-            if (hasGraphicalSubs)
-            {
-                args += EncodingHelper.GetGraphicalSubtitleParam(state, videoCodec);
-            }
-
-            return args;
-        }
-
-        /// <summary>
-        /// Gets audio arguments to pass to ffmpeg
-        /// </summary>
-        /// <param name="state">The state.</param>
-        /// <returns>System.String.</returns>
-        private string GetAudioArguments(EncodingJob state)
-        {
-            // If the video doesn't have an audio stream, return a default.
-            if (state.AudioStream == null && state.VideoStream != null)
-            {
-                return string.Empty;
-            }
-
-            // Get the output codec name
-            var codec = EncodingHelper.GetAudioEncoder(state);
-
-            var args = "-codec:a:0 " + codec;
-
-            if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                return args;
-            }
-
-            // Add the number of audio channels
-            var channels = state.OutputAudioChannels;
-
-            if (channels.HasValue)
-            {
-                args += " -ac " + channels.Value;
-            }
-
-            var bitrate = state.OutputAudioBitrate;
-
-            if (bitrate.HasValue)
-            {
-                args += " -ab " + bitrate.Value.ToString(UsCulture);
-            }
-
-            args += " " + EncodingHelper.GetAudioFilterParam(state, GetEncodingOptions(), false);
-
-            return args;
+            return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, state.OutputFilePath, "superfast");
         }
 
         protected override string GetOutputFileExtension(EncodingJob state)

+ 1 - 1
SharedVersion.cs

@@ -1,3 +1,3 @@
 using System.Reflection;
 
-[assembly: AssemblyVersion("3.2.8.4")]
+[assembly: AssemblyVersion("3.2.8.5")]