aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2017-03-19 14:59:46 -0400
committerGitHub <noreply@github.com>2017-03-19 14:59:46 -0400
commit22f8b2122d93c158b11fa56dce35e7c584d36789 (patch)
tree70cd8a375ebd2465f59968a1505508ab045c860d
parent796096976f4af5b96b76812457faa2be5e657e18 (diff)
parent5b3b18b51dec8421d3ffc8c00874e22b026586b6 (diff)
Merge pull request #2534 from MediaBrowser/dev
Dev
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs79
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs17
-rw-r--r--Emby.Server.Implementations/HttpServer/SwaggerService.cs47
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs6
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs3
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs184
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs2
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs182
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs7
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs11
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs135
-rw-r--r--SharedVersion.cs2
18 files changed, 276 insertions, 423 deletions
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index e65ebeb04..c5ba6c892 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/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 + ")");
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index ecd86d507..afd437fe8 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/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" />
diff --git a/Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs b/Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs
deleted file mode 100644
index 819ede1ab..000000000
--- a/Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs
+++ /dev/null
@@ -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; }
- }
-} \ No newline at end of file
diff --git a/Emby.Server.Implementations/HttpServer/SwaggerService.cs b/Emby.Server.Implementations/HttpServer/SwaggerService.cs
deleted file mode 100644
index d41946645..000000000
--- a/Emby.Server.Implementations/HttpServer/SwaggerService.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index bf78e16b3..03291670b 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/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);
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 23a84e480..441fff849 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/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
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index bc600d3ea..8394e016c 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/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());
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index f49fd87e0..551dbf378 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/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; }
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 22a0fa7c9..24ba0606d 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/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
{
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index c73170fcf..939a473c5 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/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;
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index a18b86432..6f566515e 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/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>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index 6baf87a04..73be78dc9 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/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;
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
index a4db8f3b8..05b3ca5fc 100644
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ b/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)
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index b0b37f2d6..ef68fb90a 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/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)
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
index f6895696a..449c31ace 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ b/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
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
index 4b336e671..3e99d68ce 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ b/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;
diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
index 52ef4d834..96c126923 100644
--- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
+++ b/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)
diff --git a/SharedVersion.cs b/SharedVersion.cs
index f818da847..cae8e0a25 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,3 +1,3 @@
using System.Reflection;
-[assembly: AssemblyVersion("3.2.8.4")]
+[assembly: AssemblyVersion("3.2.8.5")]