Git Product home page Git Product logo

ffmpegblazor's Introduction

FFmpegBlazor

NuGet Badge License: MIT

FFmpegBlazor provides ability to utilize ffmpeg.wasm from Blazor Wasm C#.
ffmpeg.wasm is a pure Webassembly / Javascript port of FFmpeg. It enables video & audio record, convert and stream right inside browsers.
Supports Lazy loading of ffmpeg binary. It is self hosted version one time download of core ffmpeg wasm lib will be 25Mb.

Video Tutorial : Link Credit Dev Express

Roadmap for .NET 9

  • Exploring to reduce extra configuration steps aka no longer need to add Wasm headers to run app locally.
  • Robust integration with .NET 9 wasm threads and possibilty to use newer JSInterop API.

Installation

Download package via Nuget or DotNet CLI and you are good to go , no extra configuration required.

dotnet add package FFmpegBlazor 

API Documentation

Running WASM App

Currently we need to use a workaround to run FFmpegApps on web assembly, this will be removed in .NET 9 (Early September 2024) once Multi threading support is available on WASM.

We need to add 2 headers in Blazor WASM-local-server and in actual deployment static server also

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

To do so we can create a web.config file in root of our project with content

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<system.webServer>
		<httpProtocol>
			<customHeaders>
				<add name="Cross-Origin-Embedder-Policy" value="require-corp"/>
				<add name="Cross-Origin-Opener-Policy" value="same-origin"/>
			</customHeaders>
		</httpProtocol>
	</system.webServer>

</configuration>

Use IIS Express to run apps locally. Also In actual deployment we need to add these 2 headers in server config to avoid SharedArrayBuffer not defined error. You can check Netlify deployment sample here.
Thanks to @aokocax for helping with it.

Sample

Here is a sample page to convert mp4 to mp3 and play it in browser.

@page "/"
@using FFmpegBlazor
@inject IJSRuntime Runtime
@using Microsoft.AspNetCore.Components.Forms


<InputFile OnChange="fileLoad" /><br /> <br />
<video width="300" height="200" autoplay controls src="@url" /><br /><br />
<button class="btn btn-primary" @onclick="Process">Convert Mp3</button><br /><br />
<audio controls src="@url2" />

@code
{
    string url; string url2;
    FFMPEG ff;
    byte[] buffer;

    protected override async Task OnInitializedAsync()
    {
        if (FFmpegFactory.Runtime == null)
        {
            FFmpegFactory.Logger += WriteLogs;
            FFmpegFactory.Progress += ProgressChange;
        }

        //initialize Library
        await FFmpegFactory.Init(Runtime);
    }

    async void fileLoad(InputFileChangeEventArgs v)
    {
        //get fist file from input selection
        var file = v.GetMultipleFiles()[0];

        //read all bytes
        using var stream = file.OpenReadStream(100000000); //Max size for file that can be read
        buffer = new byte[file.Size];

        //read all bytes
        await stream.ReadAsync(buffer);

        //create a video link from buffer so that video can be played
        url = FFmpegFactory.CreateURLFromBuffer(buffer, "myFile.mp4", file.ContentType);

        //reRender DOM
        StateHasChanged();
    }

    async void Process()
    {
        //create an instance
        ff = FFmpegFactory.CreateFFmpeg(new FFmpegConfig() { Log = true });

        //download all dependencies from cdn
        await ff.Load(); 

        if (!ff.IsLoaded) return;

        //write buffer to in-memory files (special emscripten files, Ffmpeg only interact with this file)
        ff.WriteFile("myFile.mp4", buffer);

        //Pass CLI argument here equivalent to ffmpeg -i myFile.mp4 output.mp3
        await ff.Run("-i", "myFile.mp4", "output.mp3");

        //delete in-memory file
        //ff.UnlinkFile("myFile.mp4");
    }

    async void ProgressChange(Progress m)
    {
         // display progress % (0-1)
        Console.WriteLine($"Progress {m.Ratio}");

        //if ffmpeg processing is complete (generate a media URL so that it can be played or alternatively download that file)
        if (m.Ratio == 1)
        {
            //get bytepointer from c wasm to c#
            var res = await ff.ReadFile("output.mp3");


            //generate a url from file bufferPointer
            url2 = FFmpegFactory.CreateURLFromBuffer(res, "output.mp3", "audio/mp3");

            //Download the file instantly

            //FFmpegFactory.DownloadBufferAsFile(res, "output.mp3", "audio/mp3");

            StateHasChanged();
        }
    }

    void WriteLogs(Logs m)
    {
        Console.WriteLine(m.Type + " " + m.Message);
    }
}

ffmpegblazor's People

Contributors

dheerprajapat avatar sps014 avatar wickedscissors avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ffmpegblazor's Issues

Getting "Not allowed to load local resource"

Hi there. Awesome project. Stumbled upon it after finding your issue over here: dotnet/aspnetcore#27983

Your CreateURLFromBuffer does exactly what I want to do. I am trying to create a Tesla Sentry viewer Blazor Webassembly app, but when I try to implement your package, and use your example, I get the following error:

Not allowed to load local resource: blob:System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.String,TeslaViewer.Pages.Index+<GetVideoStream>d__9]

What I am trying to do is pretty simple: Use an InputFile element set to multiple webkitdirectory directory to allow the user to add a directory with .mp4 files in it, then simply embed those files on the page. I've tried a bunch of things, but I cannot get past the Not allowed to load local resource error, which comes when I try to read the stream into the buffer via await stream.ReadAsync(buffer).

Here's what I do:

@page "/"
@using FFmpegBlazor;
@using Microsoft.AspNetCore.Components.Forms;
@using System.Linq;
@using System.Globalization;
@inject IJSRuntime Runtime

<h3>Sentry Clips Viewer</h3>

<InputFile OnChange="@LoadFiles" multiple webkitdirectory directory @ref="inputFile" />

@if (events != null)
{
    <InputSelect @bind-Value="selectedEvent" @oninput="OnEventChanged">
        @foreach (var e in events)
        {
            <option value="@e.Key">@e.Key.ToString("yyyy-MM-dd_HH-mm-ss")</option>
        }
    </InputSelect>

    if (selectedFiles != null)
    {
        <div>
            @foreach (var file in selectedFiles)
            {
                <h4>@file.Name</h4>
                <video controls src="blob:@GetVideoStream(file)" style="max-width: 300px;"></video>
                <br />
            }
        </div>
    }
}

@code {
    private IEnumerable<IGrouping<DateTime, IBrowserFile>>? events;
    private List<IBrowserFile>? selectedFiles;
    private DateTime selectedEvent;
    private InputFile? inputFile;

    protected override async Task OnInitializedAsync()
    {
        if (FFmpegFactory.Runtime == null)
        {
            FFmpegFactory.Logger += WriteLogs;
        }

        //initialize Library
        await FFmpegFactory.Init(Runtime);
    }

    void WriteLogs(Logs m)
    {
        Console.WriteLine(m.Type + " " + m.Message);
    }

    private void LoadFiles(InputFileChangeEventArgs args)
    {
        var files = args.GetMultipleFiles(int.MaxValue);

        events = files
            .Where(x => x.ContentType == "video/mp4")
            .GroupBy(x => DateTime.ParseExact(x.Name.Substring(0, 19), "yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None))
            .OrderByDescending(x => x.Key)
            .ToList();

        selectedEvent = events.First().Key;

        StateHasChanged();
    }

    private void OnEventChanged()
    {
        selectedFiles = events
            .Single(x => x.Key == selectedEvent)
            .ToList();
    }

    private async Task<string> GetVideoStream(IBrowserFile file)
    {
        using var stream = file.OpenReadStream(int.MaxValue); //Max size for file that can be read
        var buffer = new byte[file.Size];

        //read all bytes
        await stream.ReadAsync(buffer);

        //create a video link from buffer so that video can be played
        var url = FFmpegFactory.CreateURLFromBuffer(buffer, file.Name, file.ContentType);

        return url;
    }
}

I've tried hosting it with IIS and I've tried dotnet publish -c Release and then adding it to a Nginx site. I test locally using IIS and with the web.config headers added (which I also did in on the Nginx site), as I thought it might have something to do with the headers, but it does not, obviously.

Any clue?

Progress.Ratio should be nullable?

Was getting errors when Ratio was null, had to wrap it in a try / catch.

blazor.webassembly.js:1 Uncaught (in promise) Error: System.Text.Json.JsonException: The JSON value could not be converted to System.Double. Path: $.ratio | LineNumber: 0 | BytePositionInLine: 13.
---> System.InvalidOperationException: Cannot get the value of a token type 'Null' as a number.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(JsonTokenType tokenType)
at System.Text.Json.Utf8JsonReader.TryGetDouble(Double& value)
at System.Text.Json.Utf8JsonReader.GetDouble()
at System.Text.Json.Serialization.Converters.DoubleConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo1[[System.Double, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter1[[FFmpegBlazor.Progress, FFmpegBlazor, Version=1.0.0.7, Culture=neutral, PublicKeyToken=null]].OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, Progress& value)
at System.Text.Json.Serialization.JsonConverter1[[FFmpegBlazor.Progress, FFmpegBlazor, Version=1.0.0.7, Culture=neutral, PublicKeyToken=null]].TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, Progress& value) at System.Text.Json.Serialization.JsonConverter1[[FFmpegBlazor.Progress, FFmpegBlazor, Version=1.0.0.7, Culture=neutral, PublicKeyToken=null]].ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)
at System.Text.Json.Serialization.JsonConverter1[[FFmpegBlazor.Progress, FFmpegBlazor, Version=1.0.0.7, Culture=neutral, PublicKeyToken=null]].ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.Serialization.JsonConverter1[[FFmpegBlazor.Progress, FFmpegBlazor, Version=1.0.0.7, Culture=neutral, PublicKeyToken=null]].ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore[Object](Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo, ReadStack& state)
at System.Text.Json.JsonSerializer.Read[Object](Utf8JsonReader& reader, JsonTypeInfo jsonTypeInfo)
at System.Text.Json.JsonSerializer.Deserialize(Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.ParseArguments(JSRuntime jsRuntime, String methodIdentifier, String arguments, Type[] parameterTypes)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
at Object.endInvokeDotNetFromJS (
https://localhost:7279/_framework/blazor.webassembly.js:1:3549)
at Object.Xt [as endInvokeDotNetFromJS] (
https://localhost:7279/_framework/blazor.webassembly.js:1:63231)
at Object.Gt [as invokeJSFromDotNet] (
https://localhost:7279/_framework/blazor.webassembly.js:1:62728)
at Object.Ii (
https://localhost:7279/_framework/dotnet.7.0.16.x2z935gxnv.js:5:71974)
at _mono_wasm_invoke_js_blazor (
https://localhost:7279/_framework/dotnet.7.0.16.x2z935gxnv.js:14:103886)
at wasm://wasm/00993926:wasm-function[313]:0x1d6b8
at wasm://wasm/00993926:wasm-function[283]:0x1cae6
at wasm://wasm/00993926:wasm-function[221]:0xe1d6
at wasm://wasm/00993926:wasm-function[220]:0xd046
at wasm://wasm/00993926:wasm-function[8115]:0x1a237d

Factory loads relative path resulting in 404s

I am calling the FFMpegBlazor from within my application: I am at the url: myapp.com/video/asset/3016.
When the factor proceeds to load ffmpeg-core.js for example: this is the url that gets used:

https://localhost:44348/video/3016/_content/FFmpegBlazor/0.10.0/dist/ffmpeg-core.js resulting in a 404.

In the factory class it is using a relative path: //cdnURL ??= "https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js";
cdnURL = "./_content/FFmpegBlazor/0.10.0/ffmpeg.min.js";
await Runtime.InvokeVoidAsync("import", cdnURL);
await Runtime.InvokeVoidAsync("import", "./_content/FFmpegBlazor/blazorFfmpeg.js");

It should be using an absolute path: cdnURL = "/_content/FFmpegBlazor/0.10.0/ffmpeg.min.js";

the _content folder is at the root of the application and that is where the build places the needed js files.

Is there a way to catch errors/exceptions coming from the ff.Run()?

I'm doing a two-pass conversion on an MP4 to AVI. If it works, things seems great. But if my parameters will not allow a file to be created, I get an error that I can sort of see with logging messages, but I get an immediate RuntimeError: memory access out of bounds right after that. I'm running locally in a "Start without Debugging" mode. I'm not sure if it's something I'm doing, or if it's something happening in the code with this approach. Any ideas?

FFMpegBlazor_error_low_bitrate_with_exception

[Bug] IJSUnmarshalledRuntime is obsolote in .NET 7

I have a .NET 7 project running blazor server side.
When initializing the JSRuntime, an exception is thrown:

await FFmpegFactory.Init(Runtime); // throws System.InvalidCastException: 'Unable to cast object of type 'Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSRuntime' to type 'Microsoft.JSInterop.IJSUnmarshalledRuntime'.'

I found this -> https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/blazor/javascript-interoperability/import-export-interop.md#obsolete-javascript-interop-api

File Write/Read returning array of null bytes

Been struggling to get this to work in my own local project, and I finally realized there is a demo of this project on netlify.
However the netlify instance of the project doesn't seem to be working either.

It seems either the File Write or File Read operation is broken.
In my own testing, I found that writing and a file and reading it always returns an array of null bytes (00 00 00 00...). I assume it's working to some extent, as the file size remains exactly correct, but the actual content of the file is useless.

Is this something wrong with ffmpeg.wasm? Or is this a FFmpegBlazor issue?

JSException: SharedArrayBuffer is not defined

i'm not sure if it's somehow related with #6 but there i get the following error if i want to use FFmpegBlazor in my WebAssembly project. I noticed that there is a JSException on the hosted version on netlify as well.
i'm testing https://ffmpegblazor.netlify.app/tomp3

Microsoft.JSInterop.JSException: SharedArrayBuffer is not defined
ReferenceError: SharedArrayBuffer is not defined
    at blob:https://localhost:5011/8b279815-9592-46f1-806c-a92e17d96ca7:22:175
    at https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:7334
    at f (https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:12320)
    at Generator._invoke (https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:12108)
    at Generator.next (https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:12745)
    at i (https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:5052)
    at c (https://unpkg.com/@ffmpeg/[email protected]/dist/ffmpeg.min.js:1:5255)
   at Microsoft.JSInterop.JSRuntime.<InvokeAsync>d__16`1[[Microsoft.JSInterop.Infrastructure.IJSVoidResult, Microsoft.JSInterop, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
   at Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(IJSObjectReference jsObjectReference, String identifier, Object[] args)
   at FFmpegBlazor.FFMPEG.Load(Boolean triggerEvents)

i'm happy to provide more info if needed.
Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.