Skip to content

Instantly share code, notes, and snippets.

@biomathcode
Created July 3, 2024 01:27
Show Gist options
  • Save biomathcode/16acf30fcceceb1e2e67e352a26b59a4 to your computer and use it in GitHub Desktop.
Save biomathcode/16acf30fcceceb1e2e67e352a26b59a4 to your computer and use it in GitHub Desktop.
Video Encoding in React
import "./styles.css";
import { parseGIF, decompressFrames } from "gifuct-js";
import WebMWriter from "webm-writer";
export default function App() {
const handleChange = (e) => {
const file = e.target.files[0];
const filereader = new FileReader();
filereader.onload = (res) => {
// convertGifToMp4(res);
// https://media.giphy.com/media/3ov9jNLHzjpxp0EIJa/giphy.gif
convertGifToMp4(
res.target.result
// "https://media.giphy.com/media/TtlV3j8W1aKSQ/giphy.gif"
// "https://media.giphy.com/media/3ov9jNLHzjpxp0EIJa/giphy.gif";
// "https://raw.githubusercontent.com/AwesomeDevin/vue-waterfall2/master/src/assets/gifhome_240x514_17s.gif"
).then((res) => {
const url = URL.createObjectURL(res);
const a = document.createElement("a");
a.href = url;
a.download = "aaa.webm";
a.click();
});
};
filereader.readAsArrayBuffer(file);
};
async function convertGifToMp4(gifBlob) {
// Create a canvas to draw the gif frames onto
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
let gif;
if (typeof gifBlob === "string") {
gif = await fetch(gifBlob)
.then((resp) => resp.arrayBuffer())
.then((buff) => parseGIF(buff));
} else {
gif = parseGIF(gifBlob);
}
// Use the gifuct-js library to parse the gif file
// const gif = await fetch(gifBlob)
// .then((resp) => resp.arrayBuffer())
// .then((buff) => parseGIF(buff));
// fetch('https://media.giphy.com/media/tTutsU63rnHC8/giphy.gif')
// .then((response) => response.arrayBuffer())
// .then((buffer) => d.decode(buffer))
// .then(async (frames) => {})
const frames = decompressFrames(gif, true);
console.log(gif, frames);
canvas.width = frames[0].dims.width;
canvas.height = frames[0].dims.height;
const webmWriter = new WebMWriter({
quality: 1, // WebM image quality from 0.0 (worst) to 0.99999 (best), 1.00 (VP8L lossless) is not supported
fileWriter: null, // FileWriter in order to stream to a file instead of buffering to memory (optional)
fd: null, // Node.js file handle to write to instead of buffering to memory (optional)
frameDuration: frames[0].delay, // Duration of frames in milliseconds
frameRate: 1000 / frames[0].delay, // Number of frames per second
transparent: true, // True if an alpha channel should be included in the video
alphaQuality: 1 // Allows you to set the quality level of the alpha channel separately.
});
// Handle the encoded chunks
const chunks = [];
return new Promise((resolve, reject) => {
function handleChunk(chunk) {
chunks.push(chunk);
if (chunks.length === frames.length) {
console.log("close encoder");
encoder.flush();
// encoder.close();
resolve(webmWriter.complete());
}
}
// Create a VideoEncoder instance
const init = {
output: handleChunk,
error: (e) => console.error(e)
};
const config = {
codec: "vp8",
width: canvas.width,
height: canvas.height,
displayWidth: canvas.width,
displayHeight: canvas.height,
bitrate: 1_000_000,
framerate: 24
};
const encoder = new VideoEncoder(init);
encoder.configure(config);
// Encode each frame of the gif
for (const frame of frames) {
const imageData = new ImageData(
frame.patch,
frame.dims.width,
frame.dims.height
);
ctx.putImageData(imageData, frame.dims.left, frame.dims.top);
const videoFrame = new VideoFrame(canvas, {
timestamp: frame.delay * 1000
});
// console.log(canvas.toDataURL());
webmWriter.addFrame(canvas);
encoder.encode(videoFrame, { keyFrame: true });
videoFrame.close();
}
});
}
return (
<div className="App">
<input type="file" onChange={handleChange} />
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment