Skip to content

Demonstrate how to render video with ScottPlot and FFmpeg #4149

@swharden

Description

@swharden

Here's some code to get things started

using FFMpegCore;
using FFMpegCore.Pipes;
using SkiaSharp;

// prepare data representing 10 seconds of a 1000 Hz recording
double sampleRate = 1000;
double samplePeriod = 1.0 / sampleRate;
double[] values = ScottPlot.Generate.Sin(10_000, oscillations: 10);
ScottPlot.Generate.AddNoiseInPlace(values, 0.1);

double frameRate = 30;
int frameCount = (int)(values.Length / sampleRate * frameRate) + 1;
IEnumerable<IVideoFrame> frames = FrameMaker(640, 480, frameCount, frameRate, values, samplePeriod);
RawVideoPipeSource videoFramesSource = new(frames) { FrameRate = 30 };
bool success = FFMpegArguments
    .FromPipeInput(videoFramesSource)
    .OutputToFile("output.webm", overwrite: true, options => options.WithVideoCodec("libvpx-vp9"))
    .ProcessSynchronously();

static IEnumerable<IVideoFrame> FrameMaker(int width, int height, 
    int frameCount, double frameRate, double[] values, double samplePeriod)
{
    ScottPlot.Plot plot = new();
    plot.Add.Signal(values, samplePeriod);

    double framePeriod = 1.0 / frameRate;
    double horizontalSpan = 1;
    for (int i = 0; i < frameCount; i++)
    {
        Console.WriteLine($"\rRendering frame {i + 1} of {frameCount}");
        double xMin = i / frameRate;
        plot.Title($"{xMin:0.000}");
        plot.Axes.SetLimitsX(xMin, xMin + horizontalSpan);

        using SKBitmap bmp = new(width, height);
        using SKCanvas canvas = new(bmp);
        using SKBitmapFrame frame = new(bmp);
        plot.Render(canvas, width, height);
        yield return frame;
    }
}

class SKBitmapFrame(SKBitmap bmp) : IVideoFrame, IDisposable
{
    public int Width => Source.Width;
    public int Height => Source.Height;
    public string Format => "bgra";
    private readonly SKBitmap Source = (bmp.ColorType == SKColorType.Bgra8888) ? bmp
        : throw new ArgumentException("Bitmap ColorType must be Bgra8888");
    public void Dispose() =>
        Source.Dispose();
    public void Serialize(Stream pipe) =>
        pipe.Write(Source.Bytes, 0, Source.Bytes.Length);
    public Task SerializeAsync(Stream pipe, CancellationToken token) =>
        pipe.WriteAsync(Source.Bytes, 0, Source.Bytes.Length, token);
}
output.webm

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions