commit
22f8b2122d
|
@ -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 = "")
|
private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "")
|
||||||
{
|
{
|
||||||
if (query.IsResumable ?? false)
|
if (query.IsResumable ?? false)
|
||||||
|
@ -3423,9 +3457,9 @@ namespace Emby.Server.Implementations.Data
|
||||||
statement.TryBind("@ChannelId", query.ChannelIds[0]);
|
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));
|
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'))");
|
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");
|
whereClauses.Add("MediaType=@MediaTypes");
|
||||||
if (statement != null)
|
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 + ")");
|
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 ?? query.IncludeItemTypes.Length > 0;
|
||||||
var enableItemsByName = query.IncludeItemsByName ?? false;
|
var enableItemsByName = query.IncludeItemsByName ?? false;
|
||||||
|
|
||||||
if (query.TopParentIds.Length == 1)
|
var queryTopParentIds = query.TopParentIds.Where(IsValidId).ToArray();
|
||||||
|
|
||||||
|
if (queryTopParentIds.Length == 1)
|
||||||
{
|
{
|
||||||
if (enableItemsByName)
|
if (enableItemsByName)
|
||||||
{
|
{
|
||||||
|
@ -4289,12 +4326,12 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
if (statement != null)
|
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)
|
if (enableItemsByName)
|
||||||
{
|
{
|
||||||
|
@ -4544,7 +4581,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new[] { value };
|
return new[] { value }.Where(IsValidType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteItem(Guid id, CancellationToken cancellationToken)
|
public async Task DeleteItem(Guid id, CancellationToken cancellationToken)
|
||||||
|
@ -4696,31 +4733,35 @@ namespace Emby.Server.Implementations.Data
|
||||||
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
|
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");
|
whereClauses.Add("PersonType=@PersonType");
|
||||||
if (statement != null)
|
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 + ")");
|
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");
|
whereClauses.Add("PersonType<>@PersonType");
|
||||||
if (statement != null)
|
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 + ")");
|
whereClauses.Add("PersonType not in (" + val + ")");
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,6 @@
|
||||||
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
|
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
|
||||||
<Compile Include="FileOrganization\TvFolderOrganizer.cs" />
|
<Compile Include="FileOrganization\TvFolderOrganizer.cs" />
|
||||||
<Compile Include="HttpServer\FileWriter.cs" />
|
<Compile Include="HttpServer\FileWriter.cs" />
|
||||||
<Compile Include="HttpServer\GetSwaggerResource.cs" />
|
|
||||||
<Compile Include="HttpServer\HttpListenerHost.cs" />
|
<Compile Include="HttpServer\HttpListenerHost.cs" />
|
||||||
<Compile Include="HttpServer\HttpResultFactory.cs" />
|
<Compile Include="HttpServer\HttpResultFactory.cs" />
|
||||||
<Compile Include="HttpServer\LoggerUtils.cs" />
|
<Compile Include="HttpServer\LoggerUtils.cs" />
|
||||||
|
@ -103,7 +102,6 @@
|
||||||
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
|
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
|
||||||
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
|
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
|
||||||
<Compile Include="HttpServer\StreamWriter.cs" />
|
<Compile Include="HttpServer\StreamWriter.cs" />
|
||||||
<Compile Include="HttpServer\SwaggerService.cs" />
|
|
||||||
<Compile Include="Images\BaseDynamicImageProvider.cs" />
|
<Compile Include="Images\BaseDynamicImageProvider.cs" />
|
||||||
<Compile Include="IO\FileRefresher.cs" />
|
<Compile Include="IO\FileRefresher.cs" />
|
||||||
<Compile Include="IO\MbLinkShortcutHandler.cs" />
|
<Compile Include="IO\MbLinkShortcutHandler.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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -109,11 +109,13 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
public Task<object> Get(GetHlsVideoSegmentLegacy request)
|
public Task<object> Get(GetHlsVideoSegmentLegacy request)
|
||||||
{
|
{
|
||||||
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
|
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 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);
|
.FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
|
||||||
|
|
||||||
return GetFileResult(file, playlistPath);
|
return GetFileResult(file, playlistPath);
|
||||||
|
|
|
@ -15,9 +15,6 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Controller.IO;
|
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using MediaBrowser.Common.Net;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
|
@ -7,15 +6,8 @@ using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.IO;
|
|
||||||
using MediaBrowser.Controller.IO;
|
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.Dlna;
|
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
@ -100,181 +92,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
{
|
{
|
||||||
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
|
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
|
||||||
|
|
||||||
// Get the output codec name
|
return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, GetDefaultH264Preset());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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")]
|
[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 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")]
|
[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; }
|
public string DeviceProfileId { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> SupportedSubtitleCodecs { get; set; }
|
|
||||||
public string UserAgent { get; set; }
|
public string UserAgent { get; set; }
|
||||||
public TranscodingJobType TranscodingType { get; set; }
|
public TranscodingJobType TranscodingType { get; set; }
|
||||||
|
|
||||||
|
@ -129,14 +128,12 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
SupportedSubtitleCodecs = new List<string>();
|
|
||||||
TranscodingType = transcodingType;
|
TranscodingType = transcodingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
|
|
||||||
public bool EstimateContentLength { get; set; }
|
public bool EstimateContentLength { get; set; }
|
||||||
public bool EnableMpegtsM2TsMode { get; set; }
|
|
||||||
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
||||||
|
|
||||||
public long? EncodingDurationTicks { get; set; }
|
public long? EncodingDurationTicks { get; set; }
|
||||||
|
@ -209,7 +206,6 @@ namespace MediaBrowser.Api.Playback
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OutputFilePath { get; set; }
|
public string OutputFilePath { get; set; }
|
||||||
public int? OutputAudioBitrate;
|
|
||||||
|
|
||||||
public string ActualOutputVideoCodec
|
public string ActualOutputVideoCodec
|
||||||
{
|
{
|
||||||
|
|
|
@ -1736,5 +1736,187 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
return threads;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public int? OutputVideoBitrate { get; set; }
|
public int? OutputVideoBitrate { get; set; }
|
||||||
public MediaStream SubtitleStream { get; set; }
|
public MediaStream SubtitleStream { get; set; }
|
||||||
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
|
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
|
||||||
|
public List<string> SupportedSubtitleCodecs { get; set; }
|
||||||
|
|
||||||
public int InternalSubtitleStreamOffset { get; set; }
|
public int InternalSubtitleStreamOffset { get; set; }
|
||||||
public MediaSourceInfo MediaSource { get; set; }
|
public MediaSourceInfo MediaSource { get; set; }
|
||||||
|
@ -52,6 +53,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public string InputContainer { get; set; }
|
public string InputContainer { get; set; }
|
||||||
public IsoType? IsoType { get; set; }
|
public IsoType? IsoType { get; set; }
|
||||||
|
|
||||||
|
public bool EnableMpegtsM2TsMode { get; set; }
|
||||||
|
|
||||||
public BaseEncodingJobOptions BaseRequest { get; set; }
|
public BaseEncodingJobOptions BaseRequest { get; set; }
|
||||||
|
|
||||||
public long? StartTimeTicks
|
public long? StartTimeTicks
|
||||||
|
@ -64,6 +67,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
get { return BaseRequest.CopyTimestamps; }
|
get { return BaseRequest.CopyTimestamps; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int? OutputAudioBitrate;
|
||||||
public int? OutputAudioChannels;
|
public int? OutputAudioChannels;
|
||||||
public int? OutputAudioSampleRate;
|
public int? OutputAudioSampleRate;
|
||||||
public bool DeInterlace { get; set; }
|
public bool DeInterlace { get; set; }
|
||||||
|
@ -74,8 +78,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
PlayableStreamFileNames = new List<string>();
|
PlayableStreamFileNames = new List<string>();
|
||||||
|
SupportedAudioCodecs = new List<string>();
|
||||||
SupportedVideoCodecs = new List<string>();
|
SupportedVideoCodecs = new List<string>();
|
||||||
SupportedVideoCodecs = new List<string>();
|
SupportedSubtitleCodecs = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -14,7 +14,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public string AudioCodec { get; set; }
|
public string AudioCodec { get; set; }
|
||||||
|
|
||||||
public DeviceProfile DeviceProfile { get; set; }
|
public DeviceProfile DeviceProfile { get; set; }
|
||||||
public EncodingContext Context { get; set; }
|
|
||||||
|
|
||||||
public bool ReadInputAtNativeFramerate { 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")]
|
[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 VideoCodec { get; set; }
|
||||||
|
|
||||||
|
public string SubtitleCodec { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the index of the audio stream.
|
/// Gets or sets the index of the audio stream.
|
||||||
/// </summary>
|
/// </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")]
|
[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 int? VideoStreamIndex { get; set; }
|
||||||
|
|
||||||
|
public EncodingContext Context { get; set; }
|
||||||
|
|
||||||
public BaseEncodingJobOptions()
|
public BaseEncodingJobOptions()
|
||||||
{
|
{
|
||||||
EnableAutoStreamCopy = true;
|
EnableAutoStreamCopy = true;
|
||||||
|
Context = EncodingContext.Streaming;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>();
|
var audioTranscodeParams = new List<string>();
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
mapArgs,
|
mapArgs,
|
||||||
metadata).Trim();
|
metadata).Trim();
|
||||||
|
|
||||||
return Task.FromResult(result);
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetOutputFileExtension(EncodingJob state)
|
protected override string GetOutputFileExtension(EncodingJob state)
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
IProgress<double> progress,
|
IProgress<double> progress,
|
||||||
CancellationToken cancellationToken)
|
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);
|
.CreateJob(options, EncodingHelper, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
|
encodingJob.OutputFilePath = GetOutputFilePath(encodingJob);
|
||||||
|
@ -76,7 +76,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false);
|
await AcquireResources(encodingJob, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false);
|
var commandLineArgs = GetCommandLineArguments(encodingJob);
|
||||||
|
|
||||||
var process = ProcessFactory.Create(new ProcessOptions
|
var process = ProcessFactory.Create(new ProcessOptions
|
||||||
{
|
{
|
||||||
|
@ -265,7 +265,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Task<string> GetCommandLineArguments(EncodingJob job);
|
protected abstract string GetCommandLineArguments(EncodingJob job);
|
||||||
|
|
||||||
private string GetOutputFilePath(EncodingJob state)
|
private string GetOutputFilePath(EncodingJob state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
public bool EstimateContentLength { get; set; }
|
public bool EstimateContentLength { get; set; }
|
||||||
public bool EnableMpegtsM2TsMode { get; set; }
|
|
||||||
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
|
||||||
public long? EncodingDurationTicks { get; set; }
|
public long? EncodingDurationTicks { get; set; }
|
||||||
public string LiveStreamId { get; set; }
|
public string LiveStreamId { get; set; }
|
||||||
|
@ -109,7 +108,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
|
|
||||||
public string OutputFilePath { get; set; }
|
public string OutputFilePath { get; set; }
|
||||||
public int? OutputAudioBitrate;
|
|
||||||
|
|
||||||
public string ActualOutputVideoCodec
|
public string ActualOutputVideoCodec
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,15 +22,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IMediaSourceManager _mediaSourceManager;
|
private readonly IMediaSourceManager _mediaSourceManager;
|
||||||
private readonly IConfigurationManager _config;
|
private readonly IConfigurationManager _config;
|
||||||
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
|
|
||||||
protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
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;
|
_logger = logger;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_mediaSourceManager = mediaSourceManager;
|
_mediaSourceManager = mediaSourceManager;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_mediaEncoder = mediaEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EncodingJob> CreateJob(EncodingJobOptions options, EncodingHelper encodingHelper, bool isVideoRequest, IProgress<double> progress, CancellationToken cancellationToken)
|
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();
|
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);
|
var item = _libraryManager.GetItemById(request.ItemId);
|
||||||
state.ItemType = item.GetType().Name;
|
state.ItemType = item.GetType().Name;
|
||||||
|
|
||||||
|
|
|
@ -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
|
// Get the output codec name
|
||||||
var encodingOptions = GetEncodingOptions();
|
var encodingOptions = GetEncodingOptions();
|
||||||
var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
|
|
||||||
|
|
||||||
var format = string.Empty;
|
return EncodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, state.OutputFilePath, "superfast");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetOutputFileExtension(EncodingJob state)
|
protected override string GetOutputFileExtension(EncodingJob state)
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
[assembly: AssemblyVersion("3.2.8.4")]
|
[assembly: AssemblyVersion("3.2.8.5")]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user