diff options
Diffstat (limited to 'MediaBrowser.MediaEncoding')
6 files changed, 159 insertions, 51 deletions
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 8ebb59c59..5bae4fbd5 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -86,7 +86,7 @@ public class BdInfoExaminer : IBlurayExaminer if (playlist.StreamClips is not null && playlist.StreamClips.Count > 0) { // Get the files in the playlist - outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray(); + outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.FileInfo.FullName).ToArray(); } return outputStream; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index ec39f159e..a865b0e4c 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "msmpeg4", "dca", "ac3", + "ac4", "aac", "mp3", "flac", @@ -94,6 +95,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_v4l2m2m", "h264_videotoolbox", "hevc_videotoolbox", + "mjpeg_videotoolbox", "h264_rkmpp", "hevc_rkmpp" }; @@ -103,6 +105,7 @@ namespace MediaBrowser.MediaEncoding.Encoder // sw "alphasrc", "zscale", + "tonemapx", // qsv "scale_qsv", "vpp_qsv", @@ -498,6 +501,11 @@ namespace MediaBrowser.MediaEncoding.Encoder return output.Contains(keyDesc, StringComparison.Ordinal); } + public bool CheckSupportedHwaccelFlag(string flag) + { + return !string.IsNullOrEmpty(flag) && GetProcessExitCode(_encoderPath, $"-loglevel quiet -hwaccel_flags +{flag} -hide_banner -f lavfi -i nullsrc=s=1x1:d=100 -f null -"); + } + private IEnumerable<string> GetCodecs(Codec codec) { string codecstr = codec == Codec.Encoder ? "encoders" : "decoders"; @@ -603,6 +611,31 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + private bool GetProcessExitCode(string path, string arguments) + { + using var process = new Process(); + process.StartInfo = new ProcessStartInfo(path, arguments) + { + CreateNoWindow = true, + UseShellExecute = false, + WindowStyle = ProcessWindowStyle.Hidden, + ErrorDialog = false + }; + _logger.LogDebug("Running {Path} {Arguments}", path, arguments); + + try + { + process.Start(); + process.WaitForExit(); + return process.ExitCode == 0; + } + catch (Exception ex) + { + _logger.LogError("Running {Path} {Arguments} failed with exception {Exception}", path, arguments, ex.Message); + return false; + } + } + [GeneratedRegex("^\\s\\S{6}\\s(?<codec>[\\w|-]+)\\s+.+$", RegexOptions.Multiline)] private static partial Regex CodecRegex(); diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d0d41c2d3..5cfead502 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -74,6 +74,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private IDictionary<int, bool> _filtersWithOption = new Dictionary<int, bool>(); private bool _isPkeyPauseSupported = false; + private bool _isLowPriorityHwDecodeSupported = false; private bool _isVaapiDeviceAmd = false; private bool _isVaapiDeviceInteliHD = false; @@ -194,6 +195,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _threads = EncodingHelper.GetNumberOfThreads(null, options, null); _isPkeyPauseSupported = validator.CheckSupportedRuntimeKey("p pause transcoding"); + _isLowPriorityHwDecodeSupported = validator.CheckSupportedHwaccelFlag("low_priority"); // Check the Vaapi device vendor if (OperatingSystem.IsLinux() @@ -708,16 +710,22 @@ namespace MediaBrowser.MediaEncoding.Encoder filters.Add("thumbnail=n=" + (useLargerBatchSize ? "50" : "24")); } - // Use SW tonemap on HDR10/HLG video stream only when the zscale filter is available. + // Use SW tonemap on HDR10/HLG video stream only when the zscale or tonemapx filter is available. var enableHdrExtraction = false; - if ((string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) + if (string.Equals(videoStream?.ColorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase) || string.Equals(videoStream?.ColorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase)) - && SupportsFilter("zscale")) { - enableHdrExtraction = true; - - filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"); + if (SupportsFilter("tonemapx")) + { + enableHdrExtraction = true; + filters.Add("tonemapx=tonemap=bt2390:desat=0:peak=100:t=bt709:m=bt709:p=bt709:format=yuv420p"); + } + else if (SupportsFilter("zscale")) + { + enableHdrExtraction = true; + filters.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"); + } } var vf = string.Join(',', filters); @@ -807,12 +815,28 @@ namespace MediaBrowser.MediaEncoding.Encoder int? threads, int? qualityScale, ProcessPriorityClass? priority, + bool enableKeyFrameOnlyExtraction, EncodingHelper encodingHelper, CancellationToken cancellationToken) { var options = allowHwAccel ? _configurationManager.GetEncodingOptions() : new EncodingOptions(); threads ??= _threads; + if (allowHwAccel && enableKeyFrameOnlyExtraction) + { + var supportsKeyFrameOnly = (string.Equals(options.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase) && options.EnableEnhancedNvdecDecoder) + || (string.Equals(options.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase) && OperatingSystem.IsWindows()) + || (string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && options.PreferSystemNativeHwDecoder) + || string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + || string.Equals(options.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase); + if (!supportsKeyFrameOnly) + { + // Disable hardware acceleration when the hardware decoder does not support keyframe only mode. + allowHwAccel = false; + options = new EncodingOptions(); + } + } + // A new EncodingOptions instance must be used as to not disable HW acceleration for all of Jellyfin. // Additionally, we must set a few fields without defaults to prevent null pointer exceptions. if (!allowHwAccel) @@ -862,6 +886,17 @@ namespace MediaBrowser.MediaEncoding.Encoder inputArg = "-threads " + threads + " " + inputArg; // HW accel args set a different input thread count, only set if disabled } + if (options.HardwareAccelerationType.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase) && _isLowPriorityHwDecodeSupported) + { + // VideoToolbox supports low priority decoding, which is useful for trickplay + inputArg = "-hwaccel_flags +low_priority " + inputArg; + } + + if (enableKeyFrameOnlyExtraction) + { + inputArg = "-skip_frame nokey " + inputArg; + } + var filterParam = encodingHelper.GetVideoProcessingFilterParam(jobState, options, vidEncoder).Trim(); if (string.IsNullOrWhiteSpace(filterParam)) { @@ -894,6 +929,14 @@ namespace MediaBrowser.MediaEncoding.Encoder encoderQuality = (100 - ((qualityScale - 1) * (100 / 30))) / 118; } + if (vidEncoder.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + // videotoolbox's mjpeg encoder uses jpeg quality scaled to QP2LAMBDA (118) instead of ffmpeg defined qscale + // ffmpeg qscale is a value from 1-31, with 1 being best quality and 31 being worst + // jpeg quality is a value from 0-100, with 0 being worst quality and 100 being best + encoderQuality = 118 - ((qualityScale - 1) * (118 / 30)); + } + // Output arguments var targetDirectory = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N")); Directory.CreateDirectory(targetDirectory); @@ -902,12 +945,13 @@ namespace MediaBrowser.MediaEncoding.Encoder // Final command arguments var args = string.Format( CultureInfo.InvariantCulture, - "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}-f {5} \"{6}\"", + "-loglevel error {0} -an -sn {1} -threads {2} -c:v {3} {4}{5}-f {6} \"{7}\"", inputArg, filterParam, outputThreads.GetValueOrDefault(_threads), vidEncoder, qualityScale.HasValue ? "-qscale:v " + encoderQuality.Value.ToString(CultureInfo.InvariantCulture) + " " : string.Empty, + vidEncoder.Contains("videotoolbox", StringComparison.InvariantCultureIgnoreCase) ? "-allow_sw 1 " : string.Empty, // allow_sw fallback for some intel macs "image2", outputPath); @@ -1149,18 +1193,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// <inheritdoc /> public IReadOnlyList<string> GetPrimaryPlaylistM2tsFiles(string path) - { - // Get all playable .m2ts files - var validPlaybackFiles = _blurayExaminer.GetDiscInfo(path).Files; - - // Get all files from the BDMV/STREAMING directory - // Only return playable local .m2ts files - var files = _fileSystem.GetFiles(Path.Join(path, "BDMV", "STREAM")).ToList(); - return validPlaybackFiles - .Select(validFile => files.FirstOrDefault(f => Path.GetFileName(f.FullName.AsSpan()).Equals(validFile, StringComparison.OrdinalIgnoreCase))?.FullName) - .Where(f => f is not null) - .ToList(); - } + => _blurayExaminer.GetDiscInfo(path).Files; /// <inheritdoc /> public string GetInputPathArgument(EncodingJobInfo state) @@ -1171,8 +1204,8 @@ namespace MediaBrowser.MediaEncoding.Encoder { return mediaSource.VideoType switch { - VideoType.Dvd => GetInputArgument(GetPrimaryPlaylistVobFiles(path, null).ToList(), mediaSource), - VideoType.BluRay => GetInputArgument(GetPrimaryPlaylistM2tsFiles(path).ToList(), mediaSource), + VideoType.Dvd => GetInputArgument(GetPrimaryPlaylistVobFiles(path, null), mediaSource), + VideoType.BluRay => GetInputArgument(GetPrimaryPlaylistM2tsFiles(path), mediaSource), _ => GetInputArgument(path, mediaSource) }; } @@ -1197,6 +1230,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } // Generate concat configuration entries for each file and write to file + Directory.CreateDirectory(Path.GetDirectoryName(concatFilePath)); using StreamWriter sw = new StreamWriter(concatFilePath); foreach (var path in files) { @@ -1216,7 +1250,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var duration = TimeSpan.FromTicks(mediaInfoResult.RunTimeTicks.Value).TotalSeconds; // Add file path stanza to concat configuration - sw.WriteLine("file '{0}'", path); + sw.WriteLine("file '{0}'", path.Replace("'", @"'\''", StringComparison.Ordinal)); // Add duration stanza to concat configuration sw.WriteLine("duration {0}", duration); diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 8b2685fe1..c8bd1eb7f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -721,6 +721,8 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo.CodecType == CodecType.Audio) { stream.Type = MediaStreamType.Audio; + stream.LocalizedDefault = _localization.GetLocalizedString("Default"); + stream.LocalizedExternal = _localization.GetLocalizedString("External"); stream.Channels = streamInfo.Channels; @@ -1319,23 +1321,38 @@ namespace MediaBrowser.MediaEncoding.Probing // These support multiple values, but for now we only store the first. var mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Artist Id")) ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMARTISTID")); - audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); + if (!string.IsNullOrEmpty(mb)) + { + audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); + } mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Artist Id")) ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ARTISTID")); - audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); + if (!string.IsNullOrEmpty(mb)) + { + audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); + } mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Album Id")) ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_ALBUMID")); - audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); + if (!string.IsNullOrEmpty(mb)) + { + audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); + } mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Group Id")) ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASEGROUPID")); - audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); + if (!string.IsNullOrEmpty(mb)) + { + audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); + } mb = GetMultipleMusicBrainzId(tags.GetValueOrDefault("MusicBrainz Release Track Id")) ?? GetMultipleMusicBrainzId(tags.GetValueOrDefault("MUSICBRAINZ_RELEASETRACKID")); - audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); + if (!string.IsNullOrEmpty(mb)) + { + audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); + } } private string GetMultipleMusicBrainzId(string value) diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 16837d98b..9ecbfa9cf 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -198,10 +198,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles { if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)) { - await ExtractAllTextSubtitles(mediaSource, cancellationToken).ConfigureAwait(false); + await ExtractAllExtractableSubtitles(mediaSource, cancellationToken).ConfigureAwait(false); - var outputFormat = GetTextSubtitleFormat(subtitleStream); - var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat); + var outputFileExtension = GetExtractableSubtitleFileExtension(subtitleStream); + var outputFormat = GetExtractableSubtitleFormat(subtitleStream); + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFileExtension); return new SubtitleInfo() { @@ -215,6 +216,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); + // Handle PGS subtitles as raw streams for the client to render + if (MediaStream.IsPgsFormat(currentFormat)) + { + return new SubtitleInfo() + { + Path = subtitleStream.Path, + Protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path), + Format = "pgssub", + IsExternal = true + }; + } + // Fallback to ffmpeg conversion if (!_subtitleParser.SupportsFileExtension(currentFormat)) { @@ -428,10 +441,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath); } - private string GetTextSubtitleFormat(MediaStream subtitleStream) + private string GetExtractableSubtitleFormat(MediaStream subtitleStream) { if (string.Equals(subtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) - || string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)) + || string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase) + || string.Equals(subtitleStream.Codec, "pgssub", StringComparison.OrdinalIgnoreCase)) { return subtitleStream.Codec; } @@ -441,21 +455,35 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } + private string GetExtractableSubtitleFileExtension(MediaStream subtitleStream) + { + // Using .pgssub as file extension is not allowed by ffmpeg. The file extension for pgs subtitles is .sup. + if (string.Equals(subtitleStream.Codec, "pgssub", StringComparison.OrdinalIgnoreCase)) + { + return "sup"; + } + else + { + return GetExtractableSubtitleFormat(subtitleStream); + } + } + private bool IsCodecCopyable(string codec) { return string.Equals(codec, "ass", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "ssa", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "srt", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "subrip", StringComparison.OrdinalIgnoreCase); + || string.Equals(codec, "subrip", StringComparison.OrdinalIgnoreCase) + || string.Equals(codec, "pgssub", StringComparison.OrdinalIgnoreCase); } /// <summary> - /// Extracts all text subtitles. + /// Extracts all extractable subtitles (text and pgs). /// </summary> /// <param name="mediaSource">The mediaSource.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task ExtractAllTextSubtitles(MediaSourceInfo mediaSource, CancellationToken cancellationToken) + private async Task ExtractAllExtractableSubtitles(MediaSourceInfo mediaSource, CancellationToken cancellationToken) { var locks = new List<IDisposable>(); var extractableStreams = new List<MediaStream>(); @@ -463,11 +491,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { var subtitleStreams = mediaSource.MediaStreams - .Where(stream => stream is { IsTextSubtitleStream: true, SupportsExternalStream: true, IsExternal: false }); + .Where(stream => stream is { IsExtractableSubtitleStream: true, SupportsExternalStream: true, IsExternal: false }); foreach (var subtitleStream in subtitleStreams) { - var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetTextSubtitleFormat(subtitleStream)); + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream)); var releaser = await _semaphoreLocks.LockAsync(outputPath, cancellationToken).ConfigureAwait(false); @@ -483,7 +511,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (extractableStreams.Count > 0) { - await ExtractAllTextSubtitlesInternal(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false); + await ExtractAllExtractableSubtitlesInternal(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) @@ -496,7 +524,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles } } - private async Task ExtractAllTextSubtitlesInternal( + private async Task ExtractAllExtractableSubtitlesInternal( MediaSourceInfo mediaSource, List<MediaStream> subtitleStreams, CancellationToken cancellationToken) @@ -510,7 +538,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles foreach (var subtitleStream in subtitleStreams) { - var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetTextSubtitleFormat(subtitleStream)); + var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream)); var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt"; var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream); diff --git a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs index ea5dbf7f7..67a2dddb8 100644 --- a/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs +++ b/MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs @@ -235,15 +235,6 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable if (delete(job.Path!)) { await DeletePartialStreamFiles(job.Path!, job.Type, 0, 1500).ConfigureAwait(false); - if (job.MediaSource?.VideoType == VideoType.Dvd || job.MediaSource?.VideoType == VideoType.BluRay) - { - var concatFilePath = Path.Join(_serverConfigurationManager.GetTranscodePath(), job.MediaSource.Id + ".concat"); - if (File.Exists(concatFilePath)) - { - _logger.LogInformation("Deleting ffmpeg concat configuration at {Path}", concatFilePath); - File.Delete(concatFilePath); - } - } } if (closeLiveStream && !string.IsNullOrWhiteSpace(job.LiveStreamId)) @@ -419,7 +410,7 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable var attachmentPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id); if (state.MediaSource.VideoType == VideoType.Dvd || state.MediaSource.VideoType == VideoType.BluRay) { - var concatPath = Path.Join(_serverConfigurationManager.GetTranscodePath(), state.MediaSource.Id + ".concat"); + var concatPath = Path.Join(_appPaths.CachePath, "concat", state.MediaSource.Id + ".concat"); await _attachmentExtractor.ExtractAllAttachments(concatPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false); } else @@ -479,6 +470,11 @@ public sealed class TranscodeManager : ITranscodeManager, IDisposable : "FFmpeg.DirectStream-"; } + if (state.VideoRequest is null && EncodingHelper.IsCopyCodec(state.OutputAudioCodec)) + { + logFilePrefix = "FFmpeg.Remux-"; + } + var logFilePath = Path.Combine( _serverConfigurationManager.ApplicationPaths.LogDirectoryPath, $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log"); |
