{"id":21015596,"url":"https://github.com/lostbeard/spawndev.blazorjs.ffmpegwasm","last_synced_at":"2025-05-15T05:32:18.276Z","repository":{"id":191926355,"uuid":"685694976","full_name":"LostBeard/SpawnDev.BlazorJS.FFmpegWasm","owner":"LostBeard","description":"ffmpeg.wasm for Blazor WebAssembly","archived":false,"fork":false,"pushed_at":"2025-02-16T22:19:45.000Z","size":43417,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-12T01:36:48.044Z","etag":null,"topics":["blazor","blazor-webassembly","csharp","dotnet","ffmpeg","ffmpeg-wasm","webassembly"],"latest_commit_sha":null,"homepage":"https://lostbeard.github.io/SpawnDev.BlazorJS.FFmpegWasm/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LostBeard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["LostBeard"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"custom":null}},"created_at":"2023-08-31T19:51:09.000Z","updated_at":"2025-04-11T08:43:31.000Z","dependencies_parsed_at":"2024-07-17T17:14:02.619Z","dependency_job_id":null,"html_url":"https://github.com/LostBeard/SpawnDev.BlazorJS.FFmpegWasm","commit_stats":null,"previous_names":["lostbeard/spawndev.blazorjs.ffmpegwasm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.FFmpegWasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.FFmpegWasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.FFmpegWasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LostBeard%2FSpawnDev.BlazorJS.FFmpegWasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LostBeard","download_url":"https://codeload.github.com/LostBeard/SpawnDev.BlazorJS.FFmpegWasm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254282237,"owners_count":22045123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","blazor-webassembly","csharp","dotnet","ffmpeg","ffmpeg-wasm","webassembly"],"created_at":"2024-11-19T10:10:28.540Z","updated_at":"2025-05-15T05:32:14.446Z","avatar_url":"https://github.com/LostBeard.png","language":"C#","funding_links":["https://github.com/sponsors/LostBeard"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"#\"\u003e\n    \u003cimg alt=\"ffmpeg.wasm\" width=\"128px\" height=\"128px\" src=\"https://github.com/LostBeard/ffmpeg.wasm/blob/main/apps/website/static/img/logo192.png\"\u003e\u003c/img\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# SpawnDev.BlazorJS.FFmpegWasm\n| Package | Description | Includes |\n|---------|-------------|----------|\n|**SpawnDev.BlazorJS.FFmpegWasm** \u003cbr /\u003e [![NuGet version](https://badge.fury.io/nu/SpawnDev.BlazorJS.FFmpegWasm.svg)](https://www.nuget.org/packages/SpawnDev.BlazorJS.FFmpegWasm)| ffmpeg.wasm for Blazor WASM | ffmpeg/*\u003cbr /\u003effmpeg.js\u003cbr /\u003e814.ffmpeg.js\n|**SpawnDev.BlazorJS.FFmpegWasm.Core** \u003cbr /\u003e [![NuGet version](https://badge.fury.io/nu/SpawnDev.BlazorJS.FFmpegWasm.Core.svg)](https://www.nuget.org/packages/SpawnDev.BlazorJS.FFmpegWasm.Core)| Includes SpawnDev.BlazorJS.FFmpegWasm and ffmpeg.wasm core resources | core/*\u003cbr /\u003effmpeg-core.js\u003cbr /\u003effmpeg-core.wasm\n|**SpawnDev.BlazorJS.FFmpegWasm.CoreMT** \u003cbr /\u003e [![NuGet version](https://badge.fury.io/nu/SpawnDev.BlazorJS.FFmpegWasm.CoreMT.svg)](https://www.nuget.org/packages/SpawnDev.BlazorJS.FFmpegWasm.CoreMT)| Includes SpawnDev.BlazorJS.FFmpegWasm and ffmpeg.wasm core-mt resources | core-mt/*\u003cbr /\u003effmpeg-core.js\u003cbr /\u003effmpeg-core.wasm\u003cbr /\u003effmpeg-core.worker.js\n \n[ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) is a pure WebAssembly / Javascript port of FFmpeg. It enables video \u0026 audio record, convert and stream right inside browsers.\n\nSpawnDev.BlazorJS.FFmpegWasm uses [SpawnDev.BlazorJS](https://github.com/LostBeard/SpawnDev.BlazorJS) [JSObjects](https://github.com/LostBeard/SpawnDev.BlazorJS#jsobject-base-class) to bring [ffmpeg.wasm](https://github.com/ffmpegwasm/ffmpeg.wasm) into Blazor WASM apps. A slightly customized version of ffmpeg.wasm ([repo here](https://github.com/LostBeard/ffmpeg.wasm)) is used to add additional functionality to the base version.\n\n[Live Demo](https://lostbeard.github.io/SpawnDev.BlazorJS.FFmpegWasm/)\n- [Transcode](https://lostbeard.github.io/SpawnDev.BlazorJS.FFmpegWasm/)\n- [Video to Gif](https://lostbeard.github.io/SpawnDev.BlazorJS.FFmpegWasm/VideoToGif)\n- [Add subtitles](https://lostbeard.github.io/SpawnDev.BlazorJS.FFmpegWasm/AddSubtitles)\n\n## FFmpegFactory\nFFmpegFactory is an optional service that can handle importing FFmpegWasm and includes helper methods like ToBlobURL, FetchFile, CreateLoadCoreConfig, and CreateLoadCoreMTConfig.\n\n#### With FFmpegFactory\nSource [BasicFactoryExample.razor](https://github.com/LostBeard/SpawnDev.BlazorJS.FFmpegWasm/blob/main/SpawnDev.BlazorJS.FFmpegWasmDemo/Pages/BasicFactoryExample.razor) \n\n\u003cdetails\u003e\n\u003csummary\u003eExample code\u003c/summary\u003e\n\nProgram.cs\n```cs\n// ...\n// Add SpawnDev.BlazorJS.BlazorJSRuntime service\nbuilder.Services.AddBlazorJSRuntime();\n// Add FFmpegFactory service\nbuilder.Services.AddSingleton\u003cFFmpegFactory\u003e();\n// ...\n```\n\nBasicFactoryExample.razor\n```cs\n@page \"/BasicFactoryExample\"\n@using System.Text\n@using SpawnDev.BlazorJS\n@using SpawnDev.BlazorJS.FFmpegWasm\n\n\u003ch3\u003eBasic FFmpegFactory Example of ffmpeg.wasm in Blazor\u003c/h3\u003e\n\u003cp\u003eA bare bones example of ffmpeg.wasm in Blazor using SpawnDev.BlazorJS and SpawnDev.BlazorJS.FFmpegWASM\u003c/p\u003e\n\u003cvideo @ref=videoResult autoplay muted id=\"video-result\" controls\u003e\u003c/video\u003e\n\u003cbr /\u003e\n\u003cbutton @onclick=Load disabled=\"@(loaded || busy)\" id=\"load-button\"\u003eLoad ffmpeg-core (~31 MB)\u003c/button\u003e\n\u003cbr /\u003e\n\u003cbutton @onclick=Transcode disabled=\"@(!loaded || busy)\" id=\"transcode-button\"\u003eTranscode webm to mp4\u003c/button\u003e\n\u003cbr /\u003e\n\u003cp\u003e@logMessage\u003c/p\u003e\n\u003cp\u003e@progressMessage\u003c/p\u003e\n\u003cp\u003eOpen Developer Tools (Ctrl+Shift+I) to View Logs\u003c/p\u003e\n\n@code {\n    [Inject]\n    BlazorJSRuntime JS { get; set; }\n\n    [Inject]\n    FFmpegFactory FFmpegFactory { get; set; }\n\n    ElementReference videoResult;\n    HTMLVideoElement? videoEl;\n    bool loaded = false;\n    bool busy = false;\n    string logMessage = \"\";\n    string progressMessage = \"\";\n\n    FFmpeg? ffmpeg = null;\n\n    async Task Load()\n    {\n        busy = true;\n        StateHasChanged();\n        videoEl = new HTMLVideoElement(videoResult);\n        await FFmpegFactory.Init();\n        ffmpeg = new FFmpeg();\n        ffmpeg.OnLog += FFmpeg_OnLog;\n        ffmpeg.OnProgress += FFmpeg_OnProgress;\n        // Use FFmpegFactory extension methods supplied by the Nuget packages\n        // SpawnDev.BlazorJS.FFmpegWasm.Core\n        // SpawnDev.BlazorJS.FFmpegWasm.CoreMT\n        //\n        // Single thread and multi thread versions acn be used independently of each other to lower resource packaging.\n        //\n        // From SpawnDev.BlazorJS.FFmpegWasm.Core \n        // - Contains the ffmpeg.wasm core for single thread files\n        // - Adds CreateLoadCoreConfig to FFmpegFactory\n        // From SpawnDev.BlazorJS.FFmpegWasm.CoreMT \n        // - Contains the ffmpeg.wasm core for multi thread files\n        // - Adds CreateLoadCoreMTConfig to FFmpegFactory\n        var loadConfig = FFmpegFactory.MultiThreadSupported ? FFmpegFactory.CreateLoadCoreMTConfig() : FFmpegFactory.CreateLoadCoreConfig();\n        await ffmpeg.Load(loadConfig);\n        busy = false;\n        loaded = true;\n        StateHasChanged();\n    }\n\n    async Task Transcode()\n    {\n        busy = true;\n        StateHasChanged();\n        logMessage = \"Downloading source video\";\n        StateHasChanged();\n        await ffmpeg.WriteFile(\"input.webm\", await FFmpegFactory.FetchFile(\"https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm\"));\n        logMessage = \"Transcoding source video\";\n        StateHasChanged();\n        var ret = await ffmpeg.Exec(new string[] { \"-i\", \"input.webm\", \"output.mp4\" });\n        logMessage = \"Source video transcoded\";\n        StateHasChanged();\n        using var data = await ffmpeg.ReadFileUint8Array(\"output.mp4\");\n        using var blob = new Blob(new Uint8Array[] { data }, new BlobOptions { Type = \"video/mp4\" });\n        var objSrc = URL.CreateObjectURL(blob);\n        videoEl.Src = objSrc;\n        busy = false;\n        StateHasChanged();\n    }\n\n    void FFmpeg_OnLog(FFmpegLogEvent ev)\n    {\n        logMessage = ev.Message;\n        JS.Log(\"FFmpeg_OnLog\", ev.Message);\n        StateHasChanged();\n    }\n\n    void FFmpeg_OnProgress(FFmpegProgressEvent ev)\n    {\n        var progress = ev.Progress;\n        var time = ev.Time;\n        progressMessage = $\"{progress * 100} % (transcoded time: {time / 1000000} s)\";\n        JS.Log(\"FFmpeg_OnProgress\", ev.Time, ev.Progress);\n        StateHasChanged();\n    }\n}\n```\n\u003c/details\u003e\n\n#### Without FFmpegFactory\nSource [BasicExample.razor](https://github.com/LostBeard/SpawnDev.BlazorJS.FFmpegWasm/blob/main/SpawnDev.BlazorJS.FFmpegWasmDemo/Pages/BasicExample.razor)\n\n\u003cdetails\u003e\n\u003csummary\u003eExample code\u003c/summary\u003e\n\nProgram.cs\n```cs\n// ...\n// Add SpawnDev.BlazorJS.BlazorJSRuntime service\nbuilder.Services.AddBlazorJSRuntime();\n// ...\n```\n\nBasicExample.razor\n```cs\n@page \"/\"\n@using System.Text\n@using SpawnDev.BlazorJS\n@using SpawnDev.BlazorJS.FFmpegWasm\n\n\u003ch3\u003eBasic Example of ffmpeg.wasm in Blazor\u003c/h3\u003e\n\u003cp\u003eA bare bones example of ffmpeg.wasm in Blazor using SpawnDev.BlazorJS and SpawnDev.BlazorJS.FFmpegWASM\u003c/p\u003e\n\u003cvideo @ref=videoResult autoplay muted id=\"video-result\" controls\u003e\u003c/video\u003e\n\u003cbr /\u003e\n\u003cbutton @onclick=Load disabled=\"@(loaded || busy)\" id=\"load-button\"\u003eLoad ffmpeg-core (~31 MB)\u003c/button\u003e\n\u003cbr /\u003e\n\u003cbutton @onclick=Transcode disabled=\"@(!loaded || busy)\" id=\"transcode-button\"\u003eTranscode webm to mp4\u003c/button\u003e\n\u003cbr /\u003e\n\u003cp\u003e@logMessage\u003c/p\u003e\n\u003cp\u003e@progressMessage\u003c/p\u003e\n\u003cp\u003eOpen Developer Tools (Ctrl+Shift+I) to View Logs\u003c/p\u003e\n\n@code {\n    [Inject]\n    BlazorJSRuntime JS { get; set; }\n\n    ElementReference videoResult;\n    HTMLVideoElement? videoEl;\n    bool loaded = false;\n    bool busy = false;\n    string logMessage = \"\";\n    string progressMessage = \"\";\n    string baseURLFFmpeg = \"https://unpkg.com/@ffmpeg/ffmpeg@0.12.6/dist/umd\";\n    string baseURLCore = \"https://unpkg.com/@ffmpeg/core@0.12.3/dist/umd\";\n\n    FFmpeg? ffmpeg = null;\n\n    async Task Load()\n    {\n        busy = true;\n        StateHasChanged();\n        videoEl = new HTMLVideoElement(videoResult);\n        if (JS.IsUndefined(\"FFmpegWASM\"))\n        {\n            // a quick patch to allow us the ability to specify the full path to the primary ffmpeg worker (814.ffmpeg.js in umd version) via our FFMessageLoadConfig\n            // the ffmpeg.js script tries to build the path location itself (with the code being replaced) but will fail (in our scenario) so we patch it to allow us to specify the path\n            // essentially the same as Pull request #562 (https://github.com/ffmpegwasm/ffmpeg.wasm/pull/562) except this works on the minified UMD version\n            var FFmpegObjUrl = await ToBlobURL($\"{baseURLFFmpeg}/ffmpeg.js\", \"application/javascript\", (js) =\u003e js.Replace(\"new Worker(new URL(e.p+e.u(814),e.b),{type:void 0})\", \"new Worker(r.worker814URL,{type:void 0})\"));\n            await JS.Import(FFmpegObjUrl);\n            URL.RevokeObjectURL(FFmpegObjUrl);\n        }\n        ffmpeg = new FFmpeg();\n        ffmpeg.OnLog += FFmpeg_OnLog;\n        ffmpeg.OnProgress += FFmpeg_OnProgress;\n        await ffmpeg.Load(new FFMessageLoadConfig\n            {\n                Worker814URL = await ToBlobURL($\"{baseURLFFmpeg}/814.ffmpeg.js\", \"application/javascript\"),\n                CoreURL = await ToBlobURL($\"{baseURLCore}/ffmpeg-core.js\", \"application/javascript\"),\n                WasmURL = await ToBlobURL($\"{baseURLCore}/ffmpeg-core.wasm\", \"application/wasm\"),\n            });\n        busy = false;\n        loaded = true;\n        StateHasChanged();\n    }\n\n    async Task Transcode()\n    {\n        busy = true;\n        StateHasChanged();\n        logMessage = \"Downloading source video\";\n        StateHasChanged();\n        await ffmpeg.WriteFile(\"input.webm\", await FetchFile(\"https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm\"));\n        logMessage = \"Transcoding source video\";\n        StateHasChanged();\n        var ret = await ffmpeg.Exec(new string[] { \"-i\", \"input.webm\", \"output.mp4\" });\n        logMessage = \"Source video transcoded\";\n        StateHasChanged();\n        using var data = await ffmpeg.ReadFileUint8Array(\"output.mp4\");\n        using var blob = new Blob(new Uint8Array[] { data }, new BlobOptions { Type = \"video/mp4\" });\n        var objSrc = URL.CreateObjectURL(blob);\n        videoEl.Src = objSrc;\n        busy = false;\n        StateHasChanged();\n    }\n\n    void FFmpeg_OnLog(FFmpegLogEvent ev)\n    {\n        logMessage = ev.Message;\n        JS.Log(\"FFmpeg_OnLog\", ev.Message);\n        StateHasChanged();\n    }\n\n    void FFmpeg_OnProgress(FFmpegProgressEvent ev)\n    {\n        var progress = ev.Progress;\n        var time = ev.Time;\n        progressMessage = $\"{progress * 100} % (transcoded time: {time / 1000000} s)\";\n        JS.Log(\"FFmpeg_OnProgress\", ev.Time, ev.Progress);\n        StateHasChanged();\n    }\n\n    async Task\u003cUint8Array\u003e FetchFile(string resource)\n    {\n        using var resp = await JS.Fetch(resource);\n        using var body = await resp.ArrayBuffer();\n        return new Uint8Array(body);\n    }\n    async Task\u003cstring\u003e FetchText(string resource)\n    {\n        using var resp = await JS.Fetch(resource);\n        return await resp.Text();\n    }\n    async Task\u003cstring\u003e ToBlobURL(string src, string mimeType)\n    {\n        using var data = await FetchFile(src);\n        using var blob = new Blob(new Uint8Array[] { data }, new BlobOptions { Type = mimeType });\n        return URL.CreateObjectURL(blob);\n    }\n    async Task\u003cstring\u003e ToBlobURL(string src, string mimeType, Func\u003cstring, string\u003e patcher)\n    {\n        var text = await FetchText(src);\n        if (patcher != null) text = patcher(text);\n        using var blob = new Blob(new string[] { text }, new BlobOptions { Type = mimeType });\n        return URL.CreateObjectURL(blob);\n    }\n}\n```\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.ffmpegwasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.ffmpegwasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flostbeard%2Fspawndev.blazorjs.ffmpegwasm/lists"}