Created
July 3, 2024 01:27
-
-
Save biomathcode/16acf30fcceceb1e2e67e352a26b59a4 to your computer and use it in GitHub Desktop.
Video Encoding in React
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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