Привет, разработчики, в этой статье я покажу вам, как вы можете использовать ffmpeg в браузере, используя его WASM привязку с SolidJS.
Согласно WASM
WebAssembly (сокращенно Wasm) — это формат двоичных инструкций для виртуальной машины на основе стека. Wasm разработан как переносимая цель компиляции для языков программирования, позволяющая развертывать в Интернете клиентские и серверные приложения.
FFmpeg
FFmpeg — это кроссплатформенное решение для записи, преобразования и потоковой передачи аудио и видео.
FFmpeg.wasm
ffmpeg.wasm — это чистый Webassembly / Javascript порт FFmpeg. Он позволяет записывать, конвертировать и передавать видео и аудио прямо в браузере.
SolidJS
SolidJS — это реактивная, исполнительная, мощная, прагматичная и продуктивная библиотека Javascript.
В этом посте мы создадим хук useFFmpeg
, который будет конвертировать видеофайл в mp4
и воспроизводить его в браузере. Мы разработаем это веб-приложение, используя Hope ui
.
Создайте новый проект solidjs и установите зависимости.
> npx degit solidjs/templates/js ffmpeg-solidjs
> cd ffmpeg-solidjs
> pnpm i
> pnpm run dev
Добавьте Hope ui в проект solidjs
> pnpm add @hope-ui/solid @stitches/core solid-transition-group
Добавить зависимость FFmpeg.wasm
> pnpm add @ffmpeg/ffmpeg
Создадим файл useFFmpeg.jsx
внутри src/hooks
.
import { createStore } from "solid-js/store";
import { onCleanup } from "solid-js";
import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
export default function useFFmpeg() {
const [store, setStore] = createStore({
progress: null,
videoURL: null,
});
const ffmpeg = createFFmpeg({ progress: (e) => setStore("progress", e) });
const transcode = async (file) => {
const { name } = file;
// load ffmpeg.wasm code
await ffmpeg.load();
// write file to memory filesystem
ffmpeg.FS("writeFile", name, await fetchFile(file));
// convert video into mp4
await ffmpeg.run("-i", name, "output.mp4");
// read file from Memory filesystem
const data = ffmpeg.FS("readFile", "output.mp4");
const url = URL.createObjectURL(
new Blob([data.buffer], { type: "video/mp4" })
);
setStore("videoURL", url);
setStore("progress", null);
};
const handleFileChange = (e) => {
// start video conversion on file change
transcode(e.target.files[0]);
};
onCleanup(() => {
// revoke created blob url object
URL.revokeObjectURL(store.videoURL)
})
return {
store,
handleFileChange,
};
}
Функция createFFmpeg()
будет инициировать ffmpeg и обрабатывать логирование всех деталей во время работы с видео, отслеживать прогресс работы с видео и т.д..
Прогресс видео будет храниться в реактивном хранилище solidjs, чтобы мы могли использовать это хранилище для выполнения реактивных действий.
ffmpeg.load()
— это асинхронный метод, который загрузит ядро ffmpeg и инициализирует бинарный файл ffmpeg wasm.
ffmpeg.FS()
— вспомогательный метод, который поможет работать с файловой системой памяти в браузере.
URL.createObjectURL()
статический метод, создающий blob-объект url, который вы можете напрямую использовать внутри нативного элемента html, например <video>
.
После создания blob-объекта url видео мы сохраним в reactive store, чтобы использовать его для воспроизведения видео.
Когда компонент будет размонтирован, мы очистим память, занятую объектом blob, используя статический метод URL.revokeObjectURL()
.
Теперь подключим хук useFFmpeg
внутри App.jsx
.
import {
Box,
Button,
CircularProgress,
CircularProgressIndicator,
CircularProgressLabel,
Container,
Flex,
GridItem,
Heading,
HopeProvider,
HStack,
SimpleGrid,
Text,
VStack,
} from "@hope-ui/solid";
import useFFmpeg from "./hooks/useFFmpeg";
import { Show } from "solid-js";
function App() {
let fileRef;
const { store, handleFileChange } = useFFmpeg();
return (
<HopeProvider config={{ initialColorMode: "dark" }}>
<Container minH={"100vh"} display="grid" placeItems={"center"}>
<Box py={"$4"}>
<Heading fontSize={"$4xl"} textAlign="center">
Video processing in browser using
<Box>
<Text as="span" color={"$success10"}>
FFmpeg{" "}
</Text>
and{" "}
<Text as="span" color={"$primary10"}>
SolidJS
</Text>
</Box>
</Heading>
<HStack justifyContent={"center"} mt={"$6"}>
<input
type="file"
name="file"
id="file"
hidden
onChange={[handleFileChange]}
ref={fileRef}
/>
<Button onClick={() => fileRef.click()}>Select Video File</Button>
</HStack>
<Show when={store.progress}>
<SimpleGrid columns={{ "@initial": 1, "@sm": 2, "@md": 2,"@lg":3 }} justifyContent={"center"} alignItems="center" mt={"$6"} spacing={"$4"}>
<GridItem mx="auto">
<CircularProgress
value={Math.round(store.progress?.ratio * 100)}
size={"$52"}
>
<CircularProgressIndicator color="$success10" />
<CircularProgressLabel>
<VStack spacing={"$2"}>
<Heading fontSize={"$xl"}>Ratio</Heading>
<Heading fontSize={"$3xl"}>
{Math.round(store.progress?.ratio * 100)} %
</Heading>
</VStack>
</CircularProgressLabel>
</CircularProgress>
</GridItem>
<GridItem mx="auto">
<CircularProgress value={100} size={"$52"} thickness={"$1"}>
<CircularProgressIndicator color="$success10" />
<CircularProgressLabel>
<VStack spacing={"$2"}>
<Heading fontSize={"$xl"}>Duration</Heading>
<Heading fontSize={"$3xl"}>
{store.progress?.duration}
</Heading>
</VStack>
</CircularProgressLabel>
</CircularProgress>
</GridItem>
<GridItem mx="auto">
<CircularProgress size={"$52"} indeterminate>
<CircularProgressIndicator color="$primary10" />
<CircularProgressLabel>
<VStack spacing={"$2"}>
<Heading fontSize={"$xl"}>Time</Heading>
<Heading fontSize={"$3xl"}>{store.progress?.time}</Heading>
</VStack>
</CircularProgressLabel>
</CircularProgress>
</GridItem>
</SimpleGrid>
</Show>
<Show when={store.videoURL}>
<Flex justifyContent={"center"} mt={"$6"}>
<video
src={store.videoURL}
width={"400px"}
height={"400px"}
autoPlay
controls
/>
</Flex>
</Show>
</Box>
</Container>
</HopeProvider>
);
}
export default App;
Напоследок мы настроим vite на обработку кросс-оригинальной изоляции, добавив Cross-Origin-Embedder-Policy
и
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";
export default defineConfig({
plugins: [
solidPlugin(),
{
name: "configure-response-headers",
configureServer: (server) => {
server.middlewares.use((_req, res, next) => {
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
next();
});
},
},
],
build: {
target: "esnext",
polyfillDynamicImport: false,
},
});
Теперь запустите ваш dev-сервер и выберите любой видеофайл.
pnpm run dev
harshmangalam / ffmpeg-solidjs
Обработка видео в браузере с помощью FFmpeg и SolidJS
Использование
Эти зависимости шаблонов поддерживаются через pnpm посредством pnpm up -Lri
.
Именно по этой причине вы видите pnpm-lock.yaml
. Тем не менее, любой менеджер пакетов будет работать. Этот файл можно смело удалить после клонирования шаблона.
$ npm install # or pnpm install or yarn install
Узнайте больше на сайте Solid и пообщайтесь с нами в Discord.
Доступные скрипты
В директории проекта вы можете запустить:
Запускает приложение в режиме разработки.
Откройте http://localhost:3000 для просмотра в браузере.
Страница перезагрузится, если вы внесете изменения.
Собирает приложение для производства в папку dist
.
Он корректно связывает Solid в режиме производства и оптимизирует сборку для наилучшей производительности.
Сборка минифицирована, а имена файлов включают хэши.
Ваше приложение готово к развертыванию!
Развертывание
Вы можете…