mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-15 14:52:20 +01:00
Merge branch 'master' into mbaff-interlace-detection
This commit is contained in:
@@ -223,11 +223,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
|
||||
|
||||
if (failed)
|
||||
{
|
||||
var msg = $"ffmpeg attachment extraction failed for {inputPath} to {outputPath}";
|
||||
_logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath);
|
||||
|
||||
_logger.LogError(msg);
|
||||
|
||||
throw new InvalidOperationException(msg);
|
||||
throw new InvalidOperationException(
|
||||
string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.MediaEncoding.Probing;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
@@ -478,17 +479,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
Protocol = MediaProtocol.File
|
||||
};
|
||||
|
||||
return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, ".jpg", cancellationToken);
|
||||
return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, ImageFormat.Jpg, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, ".jpg", cancellationToken);
|
||||
return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, ImageFormat.Jpg, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, string outputExtension, CancellationToken cancellationToken)
|
||||
public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, ImageFormat? targetFormat, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, outputExtension, cancellationToken);
|
||||
return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, targetFormat, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<string> ExtractImage(
|
||||
@@ -500,7 +501,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
bool isAudio,
|
||||
Video3DFormat? threedFormat,
|
||||
TimeSpan? offset,
|
||||
string outputExtension,
|
||||
ImageFormat? targetFormat,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var inputArgument = GetInputArgument(inputFile, mediaSource);
|
||||
@@ -510,7 +511,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
// The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
|
||||
try
|
||||
{
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, true, outputExtension, cancellationToken).ConfigureAwait(false);
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, true, targetFormat, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -523,7 +524,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
|
||||
try
|
||||
{
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, true, outputExtension, cancellationToken).ConfigureAwait(false);
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, true, targetFormat, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -536,7 +537,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
|
||||
try
|
||||
{
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, false, outputExtension, cancellationToken).ConfigureAwait(false);
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, false, targetFormat, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
@@ -548,24 +549,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
}
|
||||
}
|
||||
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, false, outputExtension, cancellationToken).ConfigureAwait(false);
|
||||
return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, false, targetFormat, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, bool allowTonemap, string outputExtension, CancellationToken cancellationToken)
|
||||
private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, bool allowTonemap, ImageFormat? targetFormat, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputPath))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(inputPath));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(outputExtension))
|
||||
var outputExtension = targetFormat switch
|
||||
{
|
||||
outputExtension = ".jpg";
|
||||
}
|
||||
else if (outputExtension[0] != '.')
|
||||
{
|
||||
outputExtension = "." + outputExtension;
|
||||
}
|
||||
ImageFormat.Bmp => ".bmp",
|
||||
ImageFormat.Gif => ".gif",
|
||||
ImageFormat.Jpg => ".jpg",
|
||||
ImageFormat.Png => ".png",
|
||||
ImageFormat.Webp => ".webp",
|
||||
_ => ".jpg"
|
||||
};
|
||||
|
||||
var tempExtractPath = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + outputExtension);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
|
||||
@@ -682,11 +684,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
|
||||
if (exitCode == -1 || !file.Exists || file.Length == 0)
|
||||
{
|
||||
var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath);
|
||||
_logger.LogError("ffmpeg image extraction failed for {Path}", inputPath);
|
||||
|
||||
_logger.LogError(msg);
|
||||
|
||||
throw new FfmpegException(msg);
|
||||
throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath));
|
||||
}
|
||||
|
||||
return tempExtractPath;
|
||||
@@ -705,117 +705,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
return time.ToString(@"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public async Task ExtractVideoImagesOnInterval(
|
||||
string inputFile,
|
||||
string container,
|
||||
MediaStream videoStream,
|
||||
MediaSourceInfo mediaSource,
|
||||
Video3DFormat? threedFormat,
|
||||
TimeSpan interval,
|
||||
string targetDirectory,
|
||||
string filenamePrefix,
|
||||
int? maxWidth,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var inputArgument = GetInputArgument(inputFile, mediaSource);
|
||||
|
||||
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (maxWidth.HasValue)
|
||||
{
|
||||
var maxWidthParam = maxWidth.Value.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(targetDirectory);
|
||||
var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
|
||||
|
||||
var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, _threads);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(container))
|
||||
{
|
||||
var inputFormat = EncodingHelper.GetInputFormat(container);
|
||||
if (!string.IsNullOrWhiteSpace(inputFormat))
|
||||
{
|
||||
args = "-f " + inputFormat + " " + args;
|
||||
}
|
||||
}
|
||||
|
||||
var processStartInfo = new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
FileName = _ffmpegPath,
|
||||
Arguments = args,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
ErrorDialog = false
|
||||
};
|
||||
|
||||
_logger.LogInformation(processStartInfo.FileName + " " + processStartInfo.Arguments);
|
||||
|
||||
await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
bool ranToCompletion = false;
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = processStartInfo,
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
using (var processWrapper = new ProcessWrapper(process, this))
|
||||
{
|
||||
try
|
||||
{
|
||||
StartProcess(processWrapper);
|
||||
|
||||
// Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
|
||||
// but we still need to detect if the process hangs.
|
||||
// Making the assumption that as long as new jpegs are showing up, everything is good.
|
||||
|
||||
bool isResponsive = true;
|
||||
int lastCount = 0;
|
||||
|
||||
while (isResponsive)
|
||||
{
|
||||
if (await process.WaitForExitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false))
|
||||
{
|
||||
ranToCompletion = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var jpegCount = _fileSystem.GetFilePaths(targetDirectory)
|
||||
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
isResponsive = jpegCount > lastCount;
|
||||
lastCount = jpegCount;
|
||||
}
|
||||
|
||||
if (!ranToCompletion)
|
||||
{
|
||||
StopProcess(processWrapper, 1000);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_thumbnailResourcePool.Release();
|
||||
}
|
||||
|
||||
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
||||
|
||||
if (exitCode == -1)
|
||||
{
|
||||
var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument);
|
||||
|
||||
_logger.LogError(msg);
|
||||
|
||||
throw new FfmpegException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StartProcess(ProcessWrapper process)
|
||||
{
|
||||
process.Process.Start();
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BDInfo" Version="0.7.6.1" />
|
||||
<PackageReference Include="libse" Version="3.6.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0-rc.2*" />
|
||||
<PackageReference Include="UTF.Unknown" Version="2.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||
<PackageReference Include="UTF.Unknown" Version="2.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
|
||||
@@ -716,10 +716,6 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
}
|
||||
else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
stream.Type = isAudio || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase)
|
||||
? MediaStreamType.EmbeddedImage
|
||||
: MediaStreamType.Video;
|
||||
|
||||
stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
|
||||
stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
|
||||
|
||||
@@ -740,8 +736,10 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
stream.IsInterlaced = true;
|
||||
}
|
||||
|
||||
if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
|
||||
if (isAudio
|
||||
|| string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
stream.Type = MediaStreamType.EmbeddedImage;
|
||||
}
|
||||
|
||||
@@ -636,17 +636,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
if (failed)
|
||||
{
|
||||
var msg = $"ffmpeg subtitle extraction failed for {inputPath} to {outputPath}";
|
||||
_logger.LogError("ffmpeg subtitle extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath);
|
||||
|
||||
_logger.LogError(msg);
|
||||
|
||||
throw new FfmpegException(msg);
|
||||
throw new FfmpegException(
|
||||
string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
var msg = $"ffmpeg subtitle extraction completed for {inputPath} to {outputPath}";
|
||||
|
||||
_logger.LogInformation(msg);
|
||||
_logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
|
||||
}
|
||||
|
||||
if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase))
|
||||
|
||||
@@ -19,12 +19,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
writer.WriteLine("WEBVTT");
|
||||
writer.WriteLine();
|
||||
writer.WriteLine("REGION");
|
||||
writer.WriteLine("id:subtitle");
|
||||
writer.WriteLine("width:80%");
|
||||
writer.WriteLine("lines:3");
|
||||
writer.WriteLine("regionanchor:50%,100%");
|
||||
writer.WriteLine("viewportanchor:50%,90%");
|
||||
writer.WriteLine("Region: id:subtitle width:80% lines:3 regionanchor:50%,100% viewportanchor:50%,90%");
|
||||
writer.WriteLine();
|
||||
foreach (var trackEvent in info.TrackEvents)
|
||||
{
|
||||
@@ -39,7 +34,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
endTime = startTime.Add(TimeSpan.FromMilliseconds(1));
|
||||
}
|
||||
|
||||
writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff} region:subtitle", startTime, endTime);
|
||||
writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff} region:subtitle line:90%", startTime, endTime);
|
||||
|
||||
var text = trackEvent.Text;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user