📄
src/Infrastructure/Ffmpeg/FrameExtractor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System; using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; using FFMpegCore; using FFMpegCore.Pipes; using Microsoft.Extensions.Logging; using Slopper.Domain; namespace Slopper.Infrastructure.Ffmpeg; internal sealed class FrameExtractor(ILogger<FrameExtractor> logger) : IFrameExtractor { public async Task<IReadOnlyList<byte[]>> ExtractFrames( MediaItem media, IReadOnlyList<TimeSpan> timestamps, CancellationToken cancellationToken ) { var frames = new List<byte[]>(timestamps.Count); foreach (var timestamp in timestamps) { using var stream = new MemoryStream(); var args = FFMpegArguments .FromFileInput(media.Path, addArguments: options => options.Seek(timestamp)) .OutputToPipe( new StreamPipeSink(stream), addArguments: options => options .WithArgument("-frames:v 1") .WithVideoFilters(filter => filter.Scale(-1, 720)) .ForceFormat("image2pipe") .WithArgument("-c:v png") ) .CancellableThrough(cancellationToken); using var activity = Tracing.StartExtractFrame(media.Path, timestamp); logger.LogInformation("Running ffmpeg {FfmpegArguments}", args.Arguments); await args.ProcessAsynchronously(); frames.Add(stream.ToArray()); } return frames; } }