diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index da1f00c3e9..7626cc3785 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -9,29 +9,6 @@ namespace MediaBrowser.Api.Playback
///
public class StreamRequest : BaseEncodingJobOptions
{
- ///
- /// Gets or sets the id.
- ///
- /// The id.
- [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public Guid Id { get; set; }
-
- [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string MediaSourceId { get; set; }
-
- [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceId { get; set; }
-
- [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Container { get; set; }
-
- ///
- /// Gets or sets the audio codec.
- ///
- /// The audio codec.
- [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string AudioCodec { get; set; }
-
[ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceProfileId { get; set; }
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 400ad47cc6..96dc4ab4c7 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -92,76 +92,60 @@ namespace MediaBrowser.Api.Playback
}
}
- public int HlsListSize => 0;
-
public string UserAgent { get; set; }
public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
- : base(logger, mediaSourceManager, transcodingType)
+ : base(transcodingType)
{
_mediaSourceManager = mediaSourceManager;
_logger = logger;
}
- public string MimeType { get; set; }
-
public bool EstimateContentLength { get; set; }
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
- public long? EncodingDurationTicks { get; set; }
-
- public string GetMimeType(string outputPath, bool enableStreamDefault = true)
- {
- if (!string.IsNullOrEmpty(MimeType))
- {
- return MimeType;
- }
-
- return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
- }
-
public bool EnableDlnaHeaders { get; set; }
- public void Dispose()
+ public override void Dispose()
{
DisposeTranscodingThrottler();
- DisposeLiveStream();
DisposeLogStream();
+ DisposeLiveStream();
TranscodingJob = null;
}
- private void DisposeLogStream()
+ private void DisposeTranscodingThrottler()
{
- if (LogFileStream != null)
+ if (TranscodingThrottler != null)
{
try
{
- LogFileStream.Dispose();
+ TranscodingThrottler.Dispose();
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error disposing log stream");
+ _logger.LogError(ex, "Error disposing TranscodingThrottler");
}
- LogFileStream = null;
+ TranscodingThrottler = null;
}
}
- private void DisposeTranscodingThrottler()
+ private void DisposeLogStream()
{
- if (TranscodingThrottler != null)
+ if (LogFileStream != null)
{
try
{
- TranscodingThrottler.Dispose();
+ LogFileStream.Dispose();
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error disposing TranscodingThrottler");
+ _logger.LogError(ex, "Error disposing log stream");
}
- TranscodingThrottler = null;
+ LogFileStream = null;
}
}
@@ -180,58 +164,12 @@ namespace MediaBrowser.Api.Playback
}
}
- public string OutputFilePath { get; set; }
-
- public string ActualOutputVideoCodec
- {
- get
- {
- var codec = OutputVideoCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = VideoStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
- public string ActualOutputAudioCodec
- {
- get
- {
- var codec = OutputAudioCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- var stream = AudioStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
- }
-
- return codec;
- }
- }
-
public DeviceProfile DeviceProfile { get; set; }
public TranscodingJob TranscodingJob;
- public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
+ public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
{
- ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, 0, percentComplete, 0, bitRate);
+ ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
}
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index edc43ef46f..e086f9d330 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Threading;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
@@ -21,6 +22,8 @@ namespace MediaBrowser.Controller.MediaEncoding
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly ISubtitleEncoder _subtitleEncoder;
+ // private readonly IApplicationPaths _appPaths;
+ // private readonly IAssemblyInfo _assemblyInfo;
public EncodingHelper(IMediaEncoder mediaEncoder, IFileSystem fileSystem, ISubtitleEncoder subtitleEncoder)
{
@@ -40,56 +43,55 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var hwType = encodingOptions.HardwareAccelerationType;
- if (!encodingOptions.EnableHardwareEncoding)
+ var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- hwType = null;
- }
-
- if (string.Equals(hwType, "qsv", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(hwType, "h264_qsv", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_qsv", defaultEncoder);
- }
+ {"qsv", "h264_qsv"},
+ {"h264_qsv", "h264_qsv"},
+ {"nvenc", "h264_nvenc"},
+ {"amf", "h264_amf"},
+ {"omx", "h264_omx"},
+ {"h264_v4l2m2m", "h264_v4l2m2m"},
+ {"mediacodec", "h264_mediacodec"},
+ {"vaapi", "h264_vaapi"}
+ };
- if (string.Equals(hwType, "nvenc", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_nvenc", defaultEncoder);
- }
- if (string.Equals(hwType, "amf", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_amf", defaultEncoder);
- }
- if (string.Equals(hwType, "omx", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_omx", defaultEncoder);
- }
- if (string.Equals(hwType, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_v4l2m2m", defaultEncoder);
- }
- if (string.Equals(hwType, "mediacodec", StringComparison.OrdinalIgnoreCase))
- {
- return GetAvailableEncoder("h264_mediacodec", defaultEncoder);
- }
- if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(encodingOptions.VaapiDevice))
+ if (!string.IsNullOrEmpty(hwType)
+ && encodingOptions.EnableHardwareEncoding && codecMap.ContainsKey(hwType))
{
- if (IsVaapiSupported(state))
+ if (CheckVaapi(state, hwType, encodingOptions))
{
- return GetAvailableEncoder("h264_vaapi", defaultEncoder);
+ var preferredEncoder = codecMap[hwType];
+
+ if (_mediaEncoder.SupportsEncoder(preferredEncoder))
+ {
+ return preferredEncoder;
+ }
}
}
+
}
+ // Avoid performing a second attempt when the first one
+ // hasn't tried hardware encoding anyway.
+ encodingOptions.EnableHardwareEncoding = false;
return defaultEncoder;
}
- private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder)
+ private bool CheckVaapi(EncodingJobInfo state, string hwType, EncodingOptions encodingOptions)
{
- if (_mediaEncoder.SupportsEncoder(preferredEncoder))
+ if (!string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase))
{
- return preferredEncoder;
+ // No vaapi requested, return OK.
+ return true;
}
- return defaultEncoder;
+
+ if (string.IsNullOrEmpty(encodingOptions.VaapiDevice))
+ {
+ // No device specified, return OK.
+ return true;
+ }
+
+ return IsVaapiSupported(state);
}
private bool IsVaapiSupported(EncodingJobInfo state)
@@ -340,7 +342,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public int GetVideoProfileScore(string profile)
{
- string[] list =
+ var list = new[]
{
"ConstrainedBaseline",
"Baseline",
@@ -538,14 +540,54 @@ namespace MediaBrowser.Controller.MediaEncoding
? string.Empty
: string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture));
- string fallbackFontParam = string.Empty;
+ // TODO
+ // var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf");
+ // string fallbackFontParam = string.Empty;
+
+ // if (!_fileSystem.FileExists(fallbackFontPath))
+ // {
+ // _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath));
+ // using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf"))
+ // {
+ // using (var fileStream = _fileSystem.GetFileStream(fallbackFontPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ // {
+ // stream.CopyTo(fileStream);
+ // }
+ // }
+ // }
+
+ // fallbackFontParam = string.Format(":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
+
+ if (state.SubtitleStream.IsExternal)
+ {
+ var subtitlePath = state.SubtitleStream.Path;
+
+ var charsetParam = string.Empty;
+
+ if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
+ {
+ var charenc = _subtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language, state.MediaSource.Protocol, CancellationToken.None).Result;
+
+ if (!string.IsNullOrEmpty(charenc))
+ {
+ charsetParam = ":charenc=" + charenc;
+ }
+ }
+
+ // TODO: Perhaps also use original_size=1920x800 ??
+ return string.Format("subtitles=filename='{0}'{1}{2}{3}",
+ _mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
+ charsetParam,
+ // fallbackFontParam,
+ setPtsParam);
+ }
var mediaPath = state.MediaPath ?? string.Empty;
- return string.Format("subtitles='{0}:si={1}'{2}{3}",
+ return string.Format("subtitles='{0}:si={1}'{2}",
_mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(_usCulture),
- fallbackFontParam,
+ // fallbackFontParam,
setPtsParam);
}
@@ -718,14 +760,18 @@ namespace MediaBrowser.Controller.MediaEncoding
var request = state.BaseRequest;
var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault();
- if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+
+ // vaapi does not support Baseline profile, force Constrained Baseline in this case,
+ // which is compatible (and ugly)
+ if (string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
+ profile != null && profile.ToLower().Contains("baseline"))
{
- param += " -profile:v 578";
+ profile = "constrained_baseline";
}
- else if (!string.IsNullOrEmpty(profile))
+
+ if (!string.IsNullOrEmpty(profile))
{
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
{
// not supported by h264_omx
@@ -1039,51 +1085,67 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var bitrate = request.VideoBitRate;
- if (videoStream != null)
+ // If specific values were requested, then force the caller to supply a bitrate as well
+ if (request.Height.HasValue && request.Width.HasValue)
{
- var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
- request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
- request.Width.Value > videoStream.Width.Value;
+ return bitrate;
+ }
- // Don't allow bitrate increases unless upscaling
- if (!isUpscaling)
+ if (videoStream != null)
+ {
+ if (bitrate.HasValue)
{
- if (bitrate.HasValue && videoStream.BitRate.HasValue)
+ var inputVideoCodec = videoStream.Codec;
+ bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+
+ // If a max bitrate was requested, don't let the scaled bitrate exceed it
+ if (request.VideoBitRate.HasValue)
{
- bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value);
+ bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
}
}
}
- if (bitrate.HasValue)
- {
- var inputVideoCodec = videoStream == null ? null : videoStream.Codec;
- bitrate = ResolutionNormalizer.ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+ return bitrate;
+ }
- // If a max bitrate was requested, don't let the scaled bitrate exceed it
- if (request.VideoBitRate.HasValue)
- {
- bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
- }
+ private static double GetVideoBitrateScaleFactor(string codec)
+ {
+ if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
+ StringHelper.EqualsIgnoreCase(codec, "hevc") ||
+ StringHelper.EqualsIgnoreCase(codec, "vp9"))
+ {
+ return .5;
}
-
- return bitrate;
+ return 1;
}
- private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
+ private static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
{
- if (sourceBitrate <= 2000000)
+ var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
+ var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
+ var scaleFactor = outputScaleFactor / inputScaleFactor;
+
+ if (bitrate <= 500000)
+ {
+ scaleFactor = Math.Max(scaleFactor, 4);
+ }
+ else if (bitrate <= 1000000)
+ {
+ scaleFactor = Math.Max(scaleFactor, 3);
+ }
+ else if (bitrate <= 2000000)
{
- sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
+ scaleFactor = Math.Max(scaleFactor, 2.5);
}
- else if (sourceBitrate <= 3000000)
+ else if (bitrate <= 3000000)
{
- sourceBitrate = Convert.ToInt32(sourceBitrate * 2);
+ scaleFactor = Math.Max(scaleFactor, 2);
}
- var bitrate = Math.Min(sourceBitrate, requestedBitrate);
+ var newBitrate = scaleFactor * bitrate;
- return bitrate;
+ return Convert.ToInt32(newBitrate);
}
public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream)
@@ -1405,7 +1467,7 @@ namespace MediaBrowser.Controller.MediaEncoding
videoSizeParam);
}
- private Tuple GetFixedOutputSize(int? videoWidth,
+ private ValueTuple GetFixedOutputSize(int? videoWidth,
int? videoHeight,
int? requestedWidth,
int? requestedHeight,
@@ -1414,11 +1476,11 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (!videoWidth.HasValue && !requestedWidth.HasValue)
{
- return new Tuple(null, null);
+ return new ValueTuple(null, null);
}
if (!videoHeight.HasValue && !requestedHeight.HasValue)
{
- return new Tuple(null, null);
+ return new ValueTuple(null, null);
}
decimal inputWidth = Convert.ToDecimal(videoWidth ?? requestedWidth);
@@ -1438,7 +1500,7 @@ namespace MediaBrowser.Controller.MediaEncoding
outputWidth = 2 * Math.Truncate(outputWidth / 2);
outputHeight = 2 * Math.Truncate(outputHeight / 2);
- return new Tuple(Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
+ return new ValueTuple(Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
}
public List GetScalingFilters(int? videoWidth,
@@ -1669,7 +1731,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var inputHeight = videoStream == null ? null : videoStream.Height;
var threeDFormat = state.MediaSource.Video3DFormat;
- var videoDecoder = GetVideoDecoder(state, options);
+ var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, options);
filters.AddRange(GetScalingFilters(inputWidth, inputHeight, threeDFormat, videoDecoder, outputVideoCodec, request.Width, request.Height, request.MaxWidth, request.MaxHeight));
@@ -1851,7 +1913,7 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier += " -fflags " + string.Join("", flags.ToArray());
}
- var videoDecoder = GetVideoDecoder(state, encodingOptions);
+ var videoDecoder = this.GetHardwareAcceleratedVideoDecoder(state, encodingOptions);
if (!string.IsNullOrEmpty(videoDecoder))
{
inputModifier += " " + videoDecoder;
@@ -1893,7 +1955,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (state.MediaSource.RequiresLooping)
{
- inputModifier += " -stream_loop -1";
+ inputModifier += " -stream_loop -1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2";
}
return inputModifier;
@@ -1989,7 +2051,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
if (string.IsNullOrEmpty(requestedUrl))
{
- requestedUrl = "test." + videoRequest.OutputContainer;
+ requestedUrl = "test." + videoRequest.Container;
}
videoRequest.VideoCodec = InferVideoCodec(requestedUrl);
@@ -2015,6 +2077,49 @@ namespace MediaBrowser.Controller.MediaEncoding
}
state.MediaSource = mediaSource;
+
+ var request = state.BaseRequest;
+ if (!string.IsNullOrWhiteSpace(request.AudioCodec))
+ {
+ var supportedAudioCodecsList = request.AudioCodec.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+
+ ShiftAudioCodecsIfNeeded(supportedAudioCodecsList, state.AudioStream);
+
+ state.SupportedAudioCodecs = supportedAudioCodecsList.ToArray();
+
+ request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => _mediaEncoder.CanEncodeToAudioCodec(i))
+ ?? state.SupportedAudioCodecs.FirstOrDefault();
+ }
+ }
+
+ private void ShiftAudioCodecsIfNeeded(List audioCodecs, MediaStream audioStream)
+ {
+ // Nothing to do here
+ if (audioCodecs.Count < 2)
+ {
+ return;
+ }
+
+ var inputChannels = audioStream == null ? 6 : audioStream.Channels ?? 6;
+ if (inputChannels >= 6)
+ {
+ return;
+ }
+
+ // Transcoding to 2ch ac3 almost always causes a playback failure
+ // Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used
+ var shiftAudioCodecs = new[] { "ac3", "eac3" };
+ if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparer.OrdinalIgnoreCase)))
+ {
+ return;
+ }
+
+ while (shiftAudioCodecs.Contains(audioCodecs[0], StringComparer.OrdinalIgnoreCase))
+ {
+ var removed = shiftAudioCodecs[0];
+ audioCodecs.RemoveAt(0);
+ audioCodecs.Add(removed);
+ }
}
private void NormalizeSubtitleEmbed(EncodingJobInfo state)
@@ -2035,17 +2140,17 @@ namespace MediaBrowser.Controller.MediaEncoding
///
/// Gets the name of the output video codec
///
- protected string GetVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
+ protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions)
{
if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return null;
}
- return GetVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
+ return this.GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions);
}
- public string GetVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
+ public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions)
{
// Only use alternative encoders for video files.
// When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully
@@ -2070,6 +2175,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// qsv decoder does not support 10-bit input
if ((videoStream.BitDepth ?? 8) > 8)
{
+ encodingOptions.HardwareDecodingCodecs = Array.Empty();
return null;
}
return "-c:v h264_qsv ";
@@ -2204,6 +2310,11 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase))
{
+ if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ {
+ return "-hwaccel dxva2";
+ }
+
switch (videoStream.Codec.ToLower())
{
case "avc":
@@ -2223,6 +2334,9 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ // Avoid a second attempt if no hardware acceleration is being used
+ encodingOptions.HardwareDecodingCodecs = Array.Empty();
+
// leave blank so ffmpeg will decide
return null;
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index ea8a793068..6651a6d70a 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
@@ -12,13 +11,17 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
+using System.IO;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Controller.Library;
+using System.Threading.Tasks;
namespace MediaBrowser.Controller.MediaEncoding
{
// For now, a common base class until the API and MediaEncoding classes are unified
- public abstract class EncodingJobInfo
+ public class EncodingJobInfo
{
- private readonly ILogger _logger;
+ protected readonly IMediaSourceManager MediaSourceManager;
public MediaStream VideoStream { get; set; }
public VideoType VideoType { get; set; }
@@ -43,6 +46,21 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool ReadInputAtNativeFramerate { get; set; }
+ public string OutputFilePath { get; set; }
+
+ public string MimeType { get; set; }
+ public long? EncodingDurationTicks { get; set; }
+
+ public string GetMimeType(string outputPath, bool enableStreamDefault = true)
+ {
+ if (!string.IsNullOrEmpty(MimeType))
+ {
+ return MimeType;
+ }
+
+ return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
+ }
+
private TranscodeReason[] _transcodeReasons = null;
public TranscodeReason[] TranscodeReasons
{
@@ -266,9 +284,8 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool IsVideoRequest { get; set; }
public TranscodingJobType TranscodingType { get; set; }
- public EncodingJobInfo(ILogger logger, IMediaSourceManager unused, TranscodingJobType jobType)
+ public EncodingJobInfo(TranscodingJobType jobType)
{
- _logger = logger;
TranscodingType = jobType;
RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase);
PlayableStreamFileNames = Array.Empty();
@@ -602,6 +619,28 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ public string ActualOutputAudioCodec
+ {
+ get
+ {
+ var codec = OutputAudioCodec;
+
+ if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ {
+ var stream = AudioStream;
+
+ if (stream != null)
+ {
+ return stream.Codec;
+ }
+
+ return null;
+ }
+
+ return codec;
+ }
+ }
+
public bool? IsTargetInterlaced
{
get
@@ -657,6 +696,14 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ public int HlsListSize
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
private int? GetMediaStreamCount(MediaStreamType type, int limit)
{
var count = MediaSource.GetStreamCount(type);
@@ -668,22 +715,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return count;
}
- protected void DisposeIsoMount()
- {
- if (IsoMount != null)
- {
- try
- {
- IsoMount.Dispose();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error disposing iso mount");
- }
-
- IsoMount = null;
- }
- }
public IProgress Progress { get; set; }
public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate)
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index ff54bb3937..be97c75a25 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -11,9 +11,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
public string OutputDirectory { get; set; }
public string ItemId { get; set; }
- public string MediaSourceId { get; set; }
- public string AudioCodec { get; set; }
+ public string TempDirectory { get; set; }
public bool ReadInputAtNativeFramerate { get; set; }
///
@@ -22,15 +21,16 @@ namespace MediaBrowser.Controller.MediaEncoding
/// true if this instance has fixed resolution; otherwise, false.
public bool HasFixedResolution => Width.HasValue || Height.HasValue;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+ public DeviceProfile DeviceProfile { get; set; }
+
public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
{
- OutputContainer = info.Container;
+ Container = info.Container;
StartTimeTicks = info.StartPositionTicks;
MaxWidth = info.MaxWidth;
MaxHeight = info.MaxHeight;
MaxFramerate = info.MaxFramerate;
- ItemId = info.ItemId.ToString("");
+ Id = info.ItemId;
MediaSourceId = info.MediaSourceId;
AudioCodec = info.TargetAudioCodec.FirstOrDefault();
MaxAudioChannels = info.GlobalMaxAudioChannels;
@@ -55,6 +55,29 @@ namespace MediaBrowser.Controller.MediaEncoding
// For now until api and media encoding layers are unified
public class BaseEncodingJobOptions
{
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public Guid Id { get; set; }
+
+ [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
+ [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string DeviceId { get; set; }
+
+ [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string Container { get; set; }
+
+ ///
+ /// Gets or sets the audio codec.
+ ///
+ /// The audio codec.
+ [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string AudioCodec { get; set; }
+
[ApiMember(Name = "EnableAutoStreamCopy", Description = "Whether or not to allow automatic stream copy if requested values match the original source. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool EnableAutoStreamCopy { get; set; }
@@ -180,7 +203,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool RequireNonAnamorphic { get; set; }
public int? TranscodingMaxAudioChannels { get; set; }
public int? CpuCoreLimit { get; set; }
- public string OutputContainer { get; set; }
+
public string LiveStreamId { get; set; }
public bool EnableMpegtsM2TsMode { get; set; }
@@ -244,11 +267,5 @@ namespace MediaBrowser.Controller.MediaEncoding
Context = EncodingContext.Streaming;
StreamOptions = new Dictionary(StringComparer.OrdinalIgnoreCase);
}
-
- public string TempDirectory { get; set; }
- public string DeviceId { get; set; }
- public Guid Id { get; set; }
- public string Container { get; set; }
- public DeviceProfile DeviceProfile { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
index df78d117af..d4040cd317 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
@@ -15,7 +15,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
public bool IsCancelled { get; internal set; }
public Stream LogFileStream { get; set; }
- public IProgress Progress { get; set; }
public TaskCompletionSource TaskCompletionSource;
public EncodingJobOptions Options
@@ -24,34 +23,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
set => BaseRequest = value;
}
- public string Id { get; set; }
+ public Guid Id { get; set; }
- public string MimeType { get; set; }
public bool EstimateContentLength { get; set; }
public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
- public long? EncodingDurationTicks { get; set; }
public string ItemType { get; set; }
- public string GetMimeType(string outputPath)
- {
- if (!string.IsNullOrEmpty(MimeType))
- {
- return MimeType;
- }
-
- return MimeTypes.GetMimeType(outputPath);
- }
-
private readonly ILogger _logger;
private readonly IMediaSourceManager _mediaSourceManager;
public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) :
- base(logger, mediaSourceManager, TranscodingJobType.Progressive)
+ base(TranscodingJobType.Progressive)
{
_logger = logger;
_mediaSourceManager = mediaSourceManager;
- Id = Guid.NewGuid().ToString("N");
+ Id = Guid.NewGuid();
_logger = logger;
TaskCompletionSource = new TaskCompletionSource();
@@ -61,6 +48,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
DisposeLiveStream();
DisposeLogStream();
+ DisposeIsoMount();
}
private void DisposeLogStream()
@@ -95,49 +83,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- public string OutputFilePath { get; set; }
- public string ActualOutputVideoCodec
+ private void DisposeIsoMount()
{
- get
+ if (IsoMount != null)
{
- var codec = OutputVideoCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ try
{
- var stream = VideoStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
+ IsoMount.Dispose();
}
-
- return codec;
- }
- }
-
- public string ActualOutputAudioCodec
- {
- get
- {
- var codec = OutputAudioCodec;
-
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ catch (Exception ex)
{
- var stream = AudioStream;
-
- if (stream != null)
- {
- return stream.Codec;
- }
-
- return null;
+ _logger.LogError("Error disposing iso mount", ex);
}
- return codec;
+ IsoMount = null;
}
}