Added SW encoding strategies
This commit is contained in:
@@ -89,7 +89,7 @@ public class EncoderService : BackgroundService, IEncoderService {
|
|||||||
FileName = Path.Combine(options.FfmpegPath, "ffmpeg.exe"),
|
FileName = Path.Combine(options.FfmpegPath, "ffmpeg.exe"),
|
||||||
Arguments = @"-hide_banner -init_hw_device ""list""",
|
Arguments = @"-hide_banner -init_hw_device ""list""",
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
UseShellExecute = true,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
};
|
};
|
||||||
using Process ffmpeg = Process.Start(psi)!;
|
using Process ffmpeg = Process.Start(psi)!;
|
||||||
@@ -97,12 +97,21 @@ public class EncoderService : BackgroundService, IEncoderService {
|
|||||||
await ffmpeg.WaitForExitAsync(cancellationToken);
|
await ffmpeg.WaitForExitAsync(cancellationToken);
|
||||||
bool nvenc = output.Contains("cuda");
|
bool nvenc = output.Contains("cuda");
|
||||||
|
|
||||||
IEncodingStrategy strategy = job.RequestedEncoding switch {
|
IEncodingStrategy strategy;
|
||||||
EncoderType.H264 => new H264EncodingStrategy(file, outputPath, job),
|
|
||||||
EncoderType.HEVC => new HEVCEncodingStrategy(file, outputPath, job),
|
if(nvenc) strategy= job.RequestedEncoding switch {
|
||||||
EncoderType.AV1 => new AV1EncodingStrategy(file, outputPath, job),
|
EncoderType.H264 => new H264NvencEncodingStrategy(file, outputPath, job),
|
||||||
|
EncoderType.HEVC => new HevcNvencEncodingStrategy(file, outputPath, job),
|
||||||
|
EncoderType.AV1 => new AV1NvencEncodingStrategy(file, outputPath, job),
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(job), job, null)
|
_ => throw new ArgumentOutOfRangeException(nameof(job), job, null)
|
||||||
};
|
};
|
||||||
|
else strategy = job.RequestedEncoding switch {
|
||||||
|
EncoderType.H264 => new H264SwEncodingStrategy(file, outputPath, job),
|
||||||
|
EncoderType.HEVC => new HevcSwEncodingStrategy(file, outputPath, job),
|
||||||
|
EncoderType.AV1 => new AV1SwEncodingStrategy(file, outputPath, job),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(job), job, null)
|
||||||
|
};
|
||||||
|
|
||||||
bool result = await strategy.ExecuteAsync(cancellationToken, Progress);
|
bool result = await strategy.ExecuteAsync(cancellationToken, Progress);
|
||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public interface IEncodingStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[EncodingStrategy(EncoderType.AV1)]
|
[EncodingStrategy(EncoderType.AV1)]
|
||||||
public class AV1EncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
public class AV1NvencEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
public string InputFilePath { get; set; } = inputFilePath;
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
public string OutputFilePath { get; set; } = outputFilePath;
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ public class AV1EncodingStrategy(string inputFilePath, string outputFilePath, En
|
|||||||
}
|
}
|
||||||
|
|
||||||
[EncodingStrategy(EncoderType.HEVC)]
|
[EncodingStrategy(EncoderType.HEVC)]
|
||||||
public class HEVCEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
public class HevcNvencEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
public string InputFilePath { get; set; } = inputFilePath;
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
public string OutputFilePath { get; set; } = outputFilePath;
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ public class HEVCEncodingStrategy(string inputFilePath, string outputFilePath, E
|
|||||||
}
|
}
|
||||||
|
|
||||||
[EncodingStrategy(EncoderType.H264)]
|
[EncodingStrategy(EncoderType.H264)]
|
||||||
public class H264EncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
public class H264NvencEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
public string InputFilePath { get; set; } = inputFilePath;
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
public string OutputFilePath { get; set; } = outputFilePath;
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
@@ -155,3 +155,134 @@ public class H264EncodingStrategy(string inputFilePath, string outputFilePath, E
|
|||||||
.ProcessAsynchronously();
|
.ProcessAsynchronously();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[EncodingStrategy(EncoderType.AV1)]
|
||||||
|
public class AV1SwEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
|
private static readonly Tuple<int, int>[] QPTable = new Tuple<int, int>[] {
|
||||||
|
new(1280*720, 64),
|
||||||
|
new(1920*1080, 96),
|
||||||
|
new(3840*2160, 128),
|
||||||
|
new(5760*2880, 96), //VR6K
|
||||||
|
new(8128*4096, 120) //VR8K
|
||||||
|
}.OrderBy(t => t.Item1).ToArray();
|
||||||
|
|
||||||
|
public async Task<bool> ExecuteAsync(CancellationToken cancellationToken, Action<EncodingProgress> onProgress) {
|
||||||
|
var mediaInfo = await FFProbe.AnalyseAsync(InputFilePath, cancellationToken: cancellationToken);
|
||||||
|
if (mediaInfo.PrimaryVideoStream == null) return false;
|
||||||
|
int W = mediaInfo.PrimaryVideoStream.Width;
|
||||||
|
int H = mediaInfo.PrimaryVideoStream.Height;
|
||||||
|
int qp = Utils.ToQPValue(W, H, QPTable);
|
||||||
|
return await FFMpegArguments
|
||||||
|
.FromFileInput(InputFilePath, true, args => args.WithHardwareAcceleration())
|
||||||
|
.OutputToFile(OutputFilePath, true, args => args
|
||||||
|
.CopyChannel(Channel.Audio)
|
||||||
|
.CopyChannel(Channel.Subtitle)
|
||||||
|
.WithVideoCodec(VideoCodec.LibaomAv1)
|
||||||
|
// TODO: adjust settings for software AV1 encoding
|
||||||
|
.WithFastStart()
|
||||||
|
)
|
||||||
|
.NotifyOnProgress(progress => onProgress(new() {
|
||||||
|
progress = progress,
|
||||||
|
duration = mediaInfo.Duration,
|
||||||
|
framerate = (float)mediaInfo.PrimaryVideoStream.FrameRate,
|
||||||
|
job = job,
|
||||||
|
inputPath = InputFilePath,
|
||||||
|
outputPath = OutputFilePath
|
||||||
|
}))
|
||||||
|
.CancellableThrough(cancellationToken)
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[EncodingStrategy(EncoderType.HEVC)]
|
||||||
|
public class HevcSwEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
|
//TODO: needs to be adjusted for software HEVC encoding
|
||||||
|
private static readonly Tuple<int, int>[] QPTable = new Tuple<int, int>[] {
|
||||||
|
new(1280*720, 0),
|
||||||
|
new(1920*1080, 0),
|
||||||
|
new(3840*2160, 0),
|
||||||
|
new(5760*2880, 0), //VR6K
|
||||||
|
new(8128*4096, 0) //VR8K
|
||||||
|
}.OrderBy(t => t.Item1).ToArray();
|
||||||
|
|
||||||
|
public async Task<bool> ExecuteAsync(CancellationToken cancellationToken, Action<EncodingProgress> onProgress) {
|
||||||
|
var mediaInfo = await FFProbe.AnalyseAsync(InputFilePath, cancellationToken: cancellationToken);
|
||||||
|
if (mediaInfo.PrimaryVideoStream == null) return false;
|
||||||
|
int W = mediaInfo.PrimaryVideoStream.Width;
|
||||||
|
int H = mediaInfo.PrimaryVideoStream.Height;
|
||||||
|
int qp = Utils.ToQPValue(W, H, QPTable);
|
||||||
|
return await FFMpegArguments
|
||||||
|
.FromFileInput(InputFilePath, true, args => args.WithHardwareAcceleration())
|
||||||
|
.OutputToFile(OutputFilePath, true, args => args
|
||||||
|
.CopyChannel(Channel.Audio)
|
||||||
|
.CopyChannel(Channel.Subtitle)
|
||||||
|
.WithVideoCodec(VideoCodec.LibX265)
|
||||||
|
.WithArgument(new NvencSpeedPreset(NvencSpeed.p2))
|
||||||
|
.WithArgument(new NvencTuneArgument(NvencTune.hq))
|
||||||
|
.WithArgument(new NvencHighBitDepthArgument(true))
|
||||||
|
.WithArgument(new NvencQPArgument((byte)qp))
|
||||||
|
.WithFastStart()
|
||||||
|
)
|
||||||
|
.NotifyOnProgress(progress => onProgress(new() {
|
||||||
|
progress = progress,
|
||||||
|
duration = mediaInfo.Duration,
|
||||||
|
framerate = (float)mediaInfo.PrimaryVideoStream.FrameRate,
|
||||||
|
job = job,
|
||||||
|
inputPath = InputFilePath,
|
||||||
|
outputPath = OutputFilePath
|
||||||
|
}))
|
||||||
|
.CancellableThrough(cancellationToken)
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[EncodingStrategy(EncoderType.H264)]
|
||||||
|
public class H264SwEncodingStrategy(string inputFilePath, string outputFilePath, EncodingJob job) : IEncodingStrategy {
|
||||||
|
public string InputFilePath { get; set; } = inputFilePath;
|
||||||
|
public string OutputFilePath { get; set; } = outputFilePath;
|
||||||
|
|
||||||
|
//TODO: needs to be adjusted
|
||||||
|
private static readonly Tuple<int, int>[] QPTable = new Tuple<int, int>[] {
|
||||||
|
new(1280*720, 0),
|
||||||
|
new(1920*1080, 0),
|
||||||
|
new(3840*2160, 0),
|
||||||
|
new(5760*2880, 0), //VR6K
|
||||||
|
new(8128*4096, 0) //VR8K
|
||||||
|
}.OrderBy(t => t.Item1).ToArray();
|
||||||
|
|
||||||
|
public async Task<bool> ExecuteAsync(CancellationToken cancellationToken, Action<EncodingProgress> onProgress) {
|
||||||
|
var mediaInfo = await FFProbe.AnalyseAsync(InputFilePath, cancellationToken: cancellationToken);
|
||||||
|
if (mediaInfo.PrimaryVideoStream == null) return false;
|
||||||
|
int W = mediaInfo.PrimaryVideoStream.Width;
|
||||||
|
int H = mediaInfo.PrimaryVideoStream.Height;
|
||||||
|
int qp = Utils.ToQPValue(W, H, QPTable);
|
||||||
|
return await FFMpegArguments
|
||||||
|
.FromFileInput(InputFilePath, true, args => args.WithHardwareAcceleration())
|
||||||
|
.OutputToFile(OutputFilePath, true, args => args
|
||||||
|
.CopyChannel(Channel.Audio)
|
||||||
|
.CopyChannel(Channel.Subtitle)
|
||||||
|
.WithVideoCodec(VideoCodec.LibX264)
|
||||||
|
.WithArgument(new NvencSpeedPreset(NvencSpeed.p2))
|
||||||
|
.WithArgument(new NvencTuneArgument(NvencTune.hq))
|
||||||
|
.WithArgument(new NvencHighBitDepthArgument(true))
|
||||||
|
.WithArgument(new NvencQPArgument((byte)qp))
|
||||||
|
.WithFastStart()
|
||||||
|
)
|
||||||
|
.NotifyOnProgress(progress => onProgress(new() {
|
||||||
|
progress = progress,
|
||||||
|
duration = mediaInfo.Duration,
|
||||||
|
framerate = (float)mediaInfo.PrimaryVideoStream.FrameRate,
|
||||||
|
job = job,
|
||||||
|
inputPath = InputFilePath,
|
||||||
|
outputPath = OutputFilePath
|
||||||
|
}))
|
||||||
|
.CancellableThrough(cancellationToken)
|
||||||
|
.ProcessAsynchronously();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user