Applied strategy pattern to Encoder, refactored and reorganized code
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user