Applied strategy pattern to Encoder, refactored and reorganized code

This commit is contained in:
Samuele Lorefice
2025-12-15 21:32:49 +01:00
parent 24df58056c
commit fc915be3aa
5 changed files with 184 additions and 58 deletions

View File

@@ -1,16 +1,32 @@
using System.Numerics;
using FFMpegCore;
using FFMpegCore.Enums;
using Microsoft.Extensions.Options;
namespace Encoder;
public interface IEncoderService {
public Guid EnqueueJob(EncodingJob job);
public EncodingJob? GetJobStatus(Guid jobId);
}
public class EncoderService : BackgroundService, IEncoderService {
Queue<EncodingJob> JobQueue;
List<EncodingJob> Jobs = new();
ILogger<EncoderService> Logger;
FFmpegOptions options;
void Progress(EncodingProgress data) {
Logger.Log(LogLevel.Information,
//Using AV1 NVENC with QP={qp} for {W}x{H}@{framerate}.
$"""
Job {data.job.Id}: {data.progress / data.duration:P}
Processed {data.progress:g} | Total {data.duration:g}
In path: {data.inputPath}
Output path: {data.outputPath}
""");
data.job.Progress = (float)(data.progress / data.duration);
}
public EncoderService(ILogger<EncoderService> logger, IOptions<FFmpegOptions> ffmpegOptions) {
Logger = logger;
options = ffmpegOptions.Value;
@@ -64,44 +80,16 @@ public class EncoderService : BackgroundService, IEncoderService {
async Task ProcessJob(EncodingJob job, CancellationToken cancellationToken) {
job.Status = JobStatus.InProgress;
var file = job.FilePath;
var mediaInfo = await FFProbe.AnalyseAsync(file, cancellationToken: cancellationToken);
if (mediaInfo.PrimaryVideoStream == null) {
job.Status = JobStatus.Failed;
return;
}
int W = mediaInfo.PrimaryVideoStream.Width;
int H = mediaInfo.PrimaryVideoStream.Height;
string outputPath = Path.Combine(options.TemporaryFilesPath, Path.GetFileName(job.FilePath));
int qp = Utils.ToQPValue(W, H);
var result = FFMpegArguments
.FromFileInput(file, true, args => args.WithHardwareAcceleration())
.OutputToFile(outputPath, true, args => args
.CopyChannel(Channel.Audio)
.CopyChannel(Channel.Subtitle)
.WithVideoCodec("av1_nvenc")
.WithArgument(new NvencSpeedPreset(NvencSpeed.p2))
.WithArgument(new NvencTuneArgument(NvencTune.hq))
.WithArgument(new NvencHighBitDepthArgument(true))
.WithArgument(new NvencQPArgument((byte)qp))
.WithFastStart()
)
.NotifyOnProgress(progress =>
{
Logger.Log(LogLevel.Information,
$"""
Job {job.Id}: {progress / mediaInfo.Duration:P}
Processed {progress:g} | Total {mediaInfo.Duration:g}
Using AV1 NVENC with QP={qp} for {W}x{H}@{mediaInfo.PrimaryVideoStream.FrameRate}.
In path: {file}
Output path: {outputPath}
""");
job.Progress = (float)(progress / mediaInfo.Duration);
}).CancellableThrough(cancellationToken)
.ProcessAsynchronously();
result.Wait(cancellationToken);
IEncodingStrategy strategy = job.RequestedEncoding switch {
EncoderType.H264 => new H264EncodingStrategy(file, outputPath, job),
EncoderType.HEVC => new HEVCEncodingStrategy(file, outputPath, job),
EncoderType.AV1 => new AV1EncodingStrategy(file, outputPath, job),
_ => throw new ArgumentOutOfRangeException(nameof(job), job, null)
};
bool result = await strategy.ExecuteAsync(cancellationToken, Progress);
if(result.Result) {
if(result) {
job.Status = JobStatus.Completed;
job.EncodedFilePath = outputPath;
File.Delete(file); // Clean up original file