我想将 HTML5 画布中的动画转换为可以上传到 YouTube 的视频文件。是否有任何类型的屏幕捕获 API 或可以让我以编程方式执行此操作的东西?
6 回答
回到2020
通过使用MediaRecorder API 解决了它。除其他外,它正是为此而构建的。
这是一个录制 X 毫秒画布视频的解决方案,您可以使用 Buttons UI 对其进行扩展,以启动、暂停、恢复、停止、生成 URL。
function record(canvas, time) {
var recordedChunks = [];
return new Promise(function (res, rej) {
var stream = canvas.captureStream(25 /*fps*/);
mediaRecorder = new MediaRecorder(stream, {
mimeType: "video/webm; codecs=vp9"
});
//ondataavailable will fire in interval of `time || 4000 ms`
mediaRecorder.start(time || 4000);
mediaRecorder.ondataavailable = function (event) {
recordedChunks.push(event.data);
// after stop `dataavilable` event run one more time
if (mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
}
mediaRecorder.onstop = function (event) {
var blob = new Blob(recordedChunks, {type: "video/webm" });
var url = URL.createObjectURL(blob);
res(url);
}
})
}
Firefox 有一个名为 HTMLCanvasElement.captureStream() 的实验性功能(默认禁用)
本质上,它将画布元素捕获为视频流,然后可以使用 RTCPeerConnection() 将其发送到另一台计算机,或者您可以使用 YouTube Live Streaming API 直接进行流式传输。
请参阅:https ://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream
另外:https ://developers.google.com/youtube/v3/live/getting-started
存在声称使用 JavaScript 从静止图像中生成 webm 视频的 whammy 库:http:
//antimatter15.com/wp/2012/08/whammy-a-real-time-javascript-webm-encoder/
请注意,有一些限制(正如预期的那样)。这个编码器基于 webp 图像格式,目前只在 Chrome 中支持(也许新的 Opera 也是,但我没有检查)。这意味着您无法在其他浏览器中进行编码,除非您首先找到一种方法来对您想要用作 webp 图像的图像进行编码(有关可能的解决方案,请参见此链接)。
除此之外,无法使用 JavaScript 和使用原生浏览器 API 从图像创建视频文件。
命令行上的 FileSaver.js + ffmpeg
使用FilSaver.js,我们可以将每个画布帧下载为 PNG:从 Blob 保存到本地文件
然后我们只需从命令行使用 ffmpeg 将 PNG 转换为任何视频格式:How to create a video from images with FFmpeg?
Chromium 75 询问您是否允许它保存多个图像。然后,一旦您说是,它会自动在您的下载文件夹下一张一张地下载图像,命名为0.png
,1.png
等。
它也适用于 Firefox 68,但效果较差,因为浏览器会打开一堆“你想保存这个文件”窗口。他们确实有一个“为类似的下载做同样的事情”弹出窗口,但你必须快速选择它并按 Enter,否则会出现一个新的弹出窗口!
要停止它,您必须关闭选项卡,或添加停止按钮和一些 JavaScript 逻辑。
var canvas = document.getElementById("my-canvas");
var ctx = canvas.getContext("2d");
var pixel_size = 1;
var t = 0;
/* We need this to fix t because toBlob calls are asynchronous. */
function createBlobFunc(t) {
return function(blob) {
saveAs(blob, t.toString() + '.png');
};
}
function draw() {
console.log("draw");
for (x = 0; x < canvas.width; x += pixel_size) {
for (y = 0; y < canvas.height; y += pixel_size) {
var b = ((1.0 + Math.sin(t * Math.PI / 16)) / 2.0);
ctx.fillStyle =
"rgba(" +
(x / canvas.width) * 255 + "," +
(y / canvas.height) * 255 + "," +
b * 255 +
",255)"
;
ctx.fillRect(x, y, pixel_size, pixel_size);
}
}
canvas.toBlob(createBlobFunc(t));
t++;
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
<canvas id="my-canvas" width="512" height="512" style="border:1px solid black;"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>
这是使用此图像输出到 GIF 的图像:https ://askubuntu.com/questions/648244/how-do-i-create-an-animated-gif-from-still-images-preferably-with-the-command- l
如果 FPS 太高,则会跳过帧
这可以通过在上面的演示中减小画布的大小来加快速度来观察。在 32x32 时,我的 Chromium 77 以大约 10 个文件的块下载,并在其间跳过大约 50 个文件......
不幸的是,没有办法等待下载完成...在 FileSaver.js 中保存文件后关闭窗口
所以如果你有高帧率,我能看到的唯一解决方案是帧率限制......用 requestAnimationFrame 控制 fps?这是一个现场演示:https ://cirosantilli.com/#html-canvas
也许有一天会有人回答:
然后我们就可以直接下载视频了!
如果您认为浏览器不适合您,这里是一个 OpenGL 版本 :-)如何使用 GLUT/OpenGL 渲染到文件?
在 Ubuntu 19.04 中测试。
这应该会有所帮助,它允许您删除一些转换为 HTML5 CANVAS 然后转换为 webm 视频的图像:http: //techslides.com/demos/image-video/create.html
纯 javascript,没有其他第三包。
如果您有视频并想拍摄一些帧,您可以尝试如下
class Video2Canvas {
/**
* @description Create a canvas and save the frame of the video that you are giving.
* @param {HTMLVideoElement} video
* @param {Number} fps
* @see https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
* */
constructor(video, fps) {
this.video = video
this.fps = fps
this.canvas = document.createElement("canvas");
[this.canvas.width, this.canvas.height] = [video.width, video.height]
document.querySelector("body").append(this.canvas)
this.ctx = this.canvas.getContext('2d')
this.initEventListener()
}
initEventListener() {
this.video.addEventListener("play", ()=>{
const timeout = Math.round(1000/this.fps)
const width = this.video.width
const height = this.video.height
const recordFunc = ()=> {
if (this.video.paused || this.video.ended) {
return
}
this.ctx.drawImage(this.video, 0, 0, width, height)
const frame = this.ctx.getImageData(0, 0, width, height)
// ... // you can make some modifications to change the frame. For example, create the grayscale frame: https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
// Below is the options. That saves each frame as a link. If you wish, then you can click the link to download the picture.
const range = document.createRange()
const frag = range.createContextualFragment('<div><a></a></div>')
const tmpCanvas = document.createElement('canvas')
tmpCanvas.width = this.canvas.width
tmpCanvas.height = this.canvas.height
tmpCanvas.getContext('2d').putImageData(frame, 0, 0)
const a = frag.querySelector('a')
a.innerText = "my.png"
a.download = "my.png"
const quality = 1.0
a.href = tmpCanvas.toDataURL("image/png", quality)
a.append(tmpCanvas)
document.querySelector('body').append(frag)
setTimeout(recordFunc, timeout)
}
setTimeout(recordFunc, timeout)
})
}
}
const v2c = new Video2Canvas(document.querySelector("video"), 1)
<video id="my-video" controls="true" width="480" height="270" crossorigin="anonymous">
<source src="http://jplayer.org/video/webm/Big_Buck_Bunny_Trailer.webm" type="video/webm">
</video>
如果您想编辑视频(例如,需要 5~8秒+12~15秒然后创建一个新视频),您可以尝试
class CanvasRecord {
/**
* @param {HTMLCanvasElement} canvas
* @param {Number} fps
* @param {string} mediaType: video/webm, video/mp4(not support yet) ...
* */
constructor(canvas, fps, mediaType) {
this.canvas = canvas
const stream = canvas.captureStream(25) // fps // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream
this.mediaRecorder = new MediaRecorder(stream, { // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/MediaRecorder
mimeType: mediaType
})
this.initControlBtn()
this.chunks = []
this.mediaRecorder.ondataavailable = (event) => {
this.chunks.push(event.data)
}
this.mediaRecorder.onstop = (event) => {
const blob = new Blob(this.chunks, {
type: mediaType
})
const url = URL.createObjectURL(blob)
// Below is a test code for you to know you are successful. Also, you can download it if you wish.
const video = document.createElement('video')
video.src = url
video.onend = (e) => {
URL.revokeObjectURL(this.src);
}
document.querySelector("body").append(video)
video.controls = true
}
}
initControlBtn() {
const range = document.createRange()
const frag = range.createContextualFragment(`<div>
<button id="btn-start">Start</button>
<button id="btn-pause">Pause</button>
<button id="btn-resume">Resume</button>
<button id="btn-end">End</button>
</div>
`)
const btnStart = frag.querySelector(`button[id="btn-start"]`)
const btnPause = frag.querySelector(`button[id="btn-pause"]`)
const btnResume = frag.querySelector(`button[id="btn-resume"]`)
const btnEnd = frag.querySelector(`button[id="btn-end"]`)
document.querySelector('body').append(frag)
btnStart.onclick = (event) => {
this.chunks = [] // clear
this.mediaRecorder.start() // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start
console.log(this.mediaRecorder.state) // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/state
}
btnPause.onclick = (event) => { // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/pause
this.mediaRecorder.pause()
console.log(this.mediaRecorder.state)
}
btnResume.onclick = (event) => {
this.mediaRecorder.resume()
console.log(this.mediaRecorder.state)
}
btnEnd.onclick = (event) => {
this.mediaRecorder.requestData() // trigger ``ondataavailable`` // https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/requestData
this.mediaRecorder.stop()
console.log(this.mediaRecorder.state)
}
}
}
class Video2Canvas {
/**
* @description Create a canvas and save the frame of the video that you are giving.
* @param {HTMLVideoElement} video
* @param {Number} fps
* @see https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
* */
constructor(video, fps) {
this.video = video
this.fps = fps
this.canvas = document.createElement("canvas");
[this.canvas.width, this.canvas.height] = [video.width, video.height]
document.querySelector("body").append(this.canvas)
this.ctx = this.canvas.getContext('2d')
this.initEventListener()
}
initEventListener() {
this.video.addEventListener("play", ()=>{
const timeout = Math.round(1000/this.fps)
const width = this.video.width
const height = this.video.height
const recordFunc = ()=> {
if (this.video.paused || this.video.ended) {
return
}
this.ctx.drawImage(this.video, 0, 0, width, height)
/*
const frame = this.ctx.getImageData(0, 0, width, height)
// ... // you can make some modifications to change the frame. For example, create the grayscale frame: https://developer.mozilla.org/en-US/docs/Web/Guide/Audio_and_video_manipulation#video_manipulation
// Below is the options. That saves each frame as a link. If you wish, then you can click the link to download the picture.
const range = document.createRange()
const frag = range.createContextualFragment('<div><a></a></div>')
const tmpCanvas = document.createElement('canvas')
tmpCanvas.width = this.canvas.width
tmpCanvas.height = this.canvas.height
tmpCanvas.getContext('2d').putImageData(frame, 0, 0)
const a = frag.querySelector('a')
a.innerText = "my.png"
a.download = "my.png"
const quality = 1.0
a.href = tmpCanvas.toDataURL("image/png", quality)
a.append(tmpCanvas)
document.querySelector('body').append(frag)
*/
setTimeout(recordFunc, timeout)
}
setTimeout(recordFunc, timeout)
})
}
}
(()=>{
const v2c = new Video2Canvas(document.querySelector("video"), 60)
const canvasRecord = new CanvasRecord(v2c.canvas, 25, 'video/webm')
v2c.video.addEventListener("play", (event)=>{
if (canvasRecord.mediaRecorder.state === "inactive") {
return
}
document.getElementById("btn-resume").click()
})
v2c.video.addEventListener("pause", (event)=>{
if (canvasRecord.mediaRecorder.state === "inactive") {
return
}
document.getElementById("btn-pause").click()
})
})()
<video id="my-video" controls="true" width="480" height="270" crossorigin="anonymous">
<source src="http://jplayer.org/video/webm/Big_Buck_Bunny_Trailer.webm" type="video/webm">
</video>