aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/MediaEncoding
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/MediaEncoding')
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs94
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs32
2 files changed, 100 insertions, 26 deletions
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 14417a5e4..6621ae284 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -100,6 +100,13 @@ namespace MediaBrowser.Controller.MediaEncoding
{ "truehd", 6 },
};
+ private static readonly string _defaultMjpegEncoder = "mjpeg";
+ private static readonly Dictionary<string, string> _mjpegCodecMap = new(StringComparer.OrdinalIgnoreCase)
+ {
+ { "vaapi", _defaultMjpegEncoder + "_vaapi" },
+ { "qsv", _defaultMjpegEncoder + "_qsv" }
+ };
+
public static readonly string[] LosslessAudioCodecs = new string[]
{
"alac",
@@ -167,6 +174,24 @@ namespace MediaBrowser.Controller.MediaEncoding
return defaultEncoder;
}
+ private string GetMjpegEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
+ {
+ if (state.VideoType == VideoType.VideoFile)
+ {
+ var hwType = encodingOptions.HardwareAccelerationType;
+
+ if (!string.IsNullOrEmpty(hwType)
+ && encodingOptions.EnableHardwareEncoding
+ && _mjpegCodecMap.TryGetValue(hwType, out var preferredEncoder)
+ && _mediaEncoder.SupportsEncoder(preferredEncoder))
+ {
+ return preferredEncoder;
+ }
+ }
+
+ return _defaultMjpegEncoder;
+ }
+
private bool IsVaapiSupported(EncodingJobInfo state)
{
// vaapi will throw an error with this input
@@ -300,6 +325,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return GetH264Encoder(state, encodingOptions);
}
+ if (string.Equals(codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
+ {
+ return GetMjpegEncoder(state, encodingOptions);
+ }
+
if (string.Equals(codec, "vp8", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{
@@ -548,25 +578,25 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.Nullable{VideoCodecs}.</returns>
public string InferVideoCodec(string url)
{
- var ext = Path.GetExtension(url);
+ var ext = Path.GetExtension(url.AsSpan());
- if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".asf", StringComparison.OrdinalIgnoreCase))
{
return "wmv";
}
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".webm", StringComparison.OrdinalIgnoreCase))
{
// TODO: this may not always mean VP8, as the codec ages
return "vp8";
}
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".ogg", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ogv", StringComparison.OrdinalIgnoreCase))
{
return "theora";
}
- if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".m3u8", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ts", StringComparison.OrdinalIgnoreCase))
{
return "h264";
}
@@ -745,12 +775,17 @@ namespace MediaBrowser.Controller.MediaEncoding
private string GetVaapiDeviceArgs(string renderNodePath, string driver, string kernelDriver, string srcDeviceAlias, string alias)
{
alias ??= VaapiAlias;
- renderNodePath = renderNodePath ?? "/dev/dri/renderD128";
- var driverOpts = string.IsNullOrEmpty(driver)
- ? ":" + renderNodePath
- : ":,driver=" + driver + (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver);
+
+ // 'renderNodePath' has higher priority than 'kernelDriver'
+ var driverOpts = string.IsNullOrEmpty(renderNodePath)
+ ? (string.IsNullOrEmpty(kernelDriver) ? string.Empty : ",kernel_driver=" + kernelDriver)
+ : renderNodePath;
+
+ // 'driver' behaves similarly to env LIBVA_DRIVER_NAME
+ driverOpts += string.IsNullOrEmpty(driver) ? string.Empty : ",driver=" + driver;
+
var options = string.IsNullOrEmpty(srcDeviceAlias)
- ? driverOpts
+ ? (string.IsNullOrEmpty(driverOpts) ? string.Empty : ":" + driverOpts)
: "@" + srcDeviceAlias;
return string.Format(
@@ -872,14 +907,14 @@ namespace MediaBrowser.Controller.MediaEncoding
if (_mediaEncoder.IsVaapiDeviceInteliHD)
{
- args.Append(GetVaapiDeviceArgs(null, "iHD", null, null, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "iHD", null, null, VaapiAlias));
}
else if (_mediaEncoder.IsVaapiDeviceInteli965)
{
// Only override i965 since it has lower priority than iHD in libva lookup.
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME", "i965");
Environment.SetEnvironmentVariable("LIBVA_DRIVER_NAME_JELLYFIN", "i965");
- args.Append(GetVaapiDeviceArgs(null, "i965", null, null, VaapiAlias));
+ args.Append(GetVaapiDeviceArgs(options.VaapiDevice, "i965", null, null, VaapiAlias));
}
var filterDevArgs = string.Empty;
@@ -1080,10 +1115,10 @@ namespace MediaBrowser.Controller.MediaEncoding
&& state.SubtitleStream.IsExternal)
{
var subtitlePath = state.SubtitleStream.Path;
- var subtitleExtension = Path.GetExtension(subtitlePath);
+ var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan());
- if (string.Equals(subtitleExtension, ".sub", StringComparison.OrdinalIgnoreCase)
- || string.Equals(subtitleExtension, ".sup", StringComparison.OrdinalIgnoreCase))
+ if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase)
+ || subtitleExtension.Equals(".sup", StringComparison.OrdinalIgnoreCase))
{
var idxFile = Path.ChangeExtension(subtitlePath, ".idx");
if (File.Exists(idxFile))
@@ -1745,11 +1780,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// Values 0-3, 0 being highest quality but slower
var profileScore = 0;
- string crf;
var qmin = "0";
var qmax = "50";
-
- crf = "10";
+ var crf = "10";
if (isVc1)
{
@@ -2947,7 +2980,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(
CultureInfo.InvariantCulture,
- "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
+ @"scale=trunc(min(max(iw\,ih*a)\,min({0}\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\,ih)\,min({0}/a\,{1}))/2)*2",
maxWidthParam,
maxHeightParam,
scaleVal);
@@ -2989,7 +3022,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(
CultureInfo.InvariantCulture,
- "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
+ @"scale=trunc(min(max(iw\,ih*a)\,{0})/{1})*{1}:trunc(ow/a/2)*2",
maxWidthParam,
scaleVal);
}
@@ -3001,7 +3034,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(
CultureInfo.InvariantCulture,
- "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
+ @"scale=trunc(oh*a/{1})*{1}:min(max(iw/a\,ih)\,{0})",
maxHeightParam,
scaleVal);
}
@@ -3021,19 +3054,19 @@ namespace MediaBrowser.Controller.MediaEncoding
switch (threedFormat.Value)
{
case Video3DFormat.HalfSideBySide:
- filter = "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
+ filter = @"crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
// hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to requestedWidth. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not.
break;
case Video3DFormat.FullSideBySide:
- filter = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
+ filter = @"crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
// fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to requestedWidth.
break;
case Video3DFormat.HalfTopAndBottom:
- filter = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
+ filter = @"crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
// htab crop height in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to requestedWidth
break;
case Video3DFormat.FullTopAndBottom:
- filter = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
+ filter = @"crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\,ih*dar):min(ih\,iw/dar):(iw-min(iw\,iw*sar))/2:(ih - min (ih\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2";
// ftab crop height in half, set the display aspect,crop out any black bars we may have made the scale width to requestedWidth
break;
default:
@@ -4919,6 +4952,15 @@ namespace MediaBrowser.Controller.MediaEncoding
subFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter));
overlayFilters?.RemoveAll(filter => string.IsNullOrEmpty(filter));
+ var framerate = GetFramerateParam(state);
+ if (framerate.HasValue)
+ {
+ mainFilters.Insert(0, string.Format(
+ CultureInfo.InvariantCulture,
+ "fps={0}",
+ framerate.Value));
+ }
+
var mainStr = string.Empty;
if (mainFilters?.Count > 0)
{
@@ -6040,7 +6082,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var format = string.Empty;
var keyFrame = string.Empty;
- if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase)
+ if (Path.GetExtension(outputPath.AsSpan()).Equals(".mp4", StringComparison.OrdinalIgnoreCase)
&& state.BaseRequest.Context == EncodingContext.Streaming)
{
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 4114dea4f..c2cef4978 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -4,8 +4,10 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
@@ -138,6 +140,36 @@ namespace MediaBrowser.Controller.MediaEncoding
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, ImageFormat? targetFormat, CancellationToken cancellationToken);
/// <summary>
+ /// Extracts the video images on interval.
+ /// </summary>
+ /// <param name="inputFile">Input file.</param>
+ /// <param name="container">Video container type.</param>
+ /// <param name="mediaSource">Media source information.</param>
+ /// <param name="imageStream">Media stream information.</param>
+ /// <param name="maxWidth">The maximum width.</param>
+ /// <param name="interval">The interval.</param>
+ /// <param name="allowHwAccel">Allow for hardware acceleration.</param>
+ /// <param name="threads">The input/output thread count for ffmpeg.</param>
+ /// <param name="qualityScale">The qscale value for ffmpeg.</param>
+ /// <param name="priority">The process priority for the ffmpeg process.</param>
+ /// <param name="encodingHelper">EncodingHelper instance.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Directory where images where extracted. A given image made before another will always be named with a lower number.</returns>
+ Task<string> ExtractVideoImagesOnIntervalAccelerated(
+ string inputFile,
+ string container,
+ MediaSourceInfo mediaSource,
+ MediaStream imageStream,
+ int maxWidth,
+ TimeSpan interval,
+ bool allowHwAccel,
+ int? threads,
+ int? qualityScale,
+ ProcessPriorityClass? priority,
+ EncodingHelper encodingHelper,
+ CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets the media info.
/// </summary>
/// <param name="request">The request.</param>