提问人:K. Russell Smith 提问时间:9/25/2022 更新时间:9/25/2022 访问量:241
无法成功关闭节点 .js 中的 ffmpeg 流
unable to successfully close ffmpeg stream in node.js
问:
我正在尝试编写一个使用 canvas api(通过 node-canvas,该项目目前唯一的 npm 依赖项)生成帧的 Node 视频应用程序,并通过流将其写入 ffmpeg 以生成视频:
const { createCanvas } = require('canvas');
const { spawn } = require('child_process');
const fs = require('fs');
const canvas = createCanvas(1280, 720);
const ffmpeg = spawn('ffmpeg', [
'-y',
'-f', 'rawVideo',
'-vcodec', 'rawVideo',
'-pix_fmt', 'rgb24',
'-s', `${ canvas.width }x${ canvas.height }`,
'-r', '40',
'-i', '-', '-f', 'mp4',
'-q:v', '5',
'-an', '-vcodec', 'mpeg4', 'output.mp4',
]);
const ctx = canvas.getContext('2d');
ctx.font = '30px Prime';
ctx.fillStyle = 'blue';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello Canvas', canvas.width / 2, canvas.height / 2);
for (let i = 0; i < 250; ++i)
{
console.log(i);
ffmpeg.stdin.write(Buffer.from(ctx.getImageData(0, 0, canvas.width, canvas.height).data));
}
ffmpeg.stdin.end();
不幸的是,当我运行它时,程序在写入帧后抛出这个:
node:events:368
throw er; // Unhandled 'error' event
^
Error: write EPIPE
at WriteWrap.onWriteComplete [as oncomplete] (node:internal/stream_base_commons:98:16)
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:164:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}
Node.js v17.1.0
我做错了什么?
答:
0赞
K. Russell Smith
9/25/2022
#1
因此,通过我得到的响应为我指明了正确的方向,我能够纠正我的 ffmpeg 生成中的语法错误,然后使画布数据重新编码为 24 位 RGB(因为 MP4 不支持 Alpha 通道);这些解决了我最初的问题。然后我正确地将写入过程设置为 drain:
const { createCanvas } = require('canvas');
const { spawn } = require('child_process');
const video = {
title: 'canvas',
width: 1280,
height: 720,
fps: 25,
duration: 10000,
}
const ffmpeg = spawn('ffmpeg', [
'-y',
'-f', 'rawvideo',
'-vcodec', 'rawvideo',
'-pix_fmt', 'rgb24',
'-s', `${ video.width }x${ video.height }`,
'-r', `${ video.fps }`,
'-i', '-', '-f', 'mp4',
'-q:v', '5',
'-an', '-vcodec', 'mpeg4', `${ video.title }.mp4`,
'-report',
]);
const canvas = createCanvas(video.width, video.height);
const ctx = canvas.getContext('2d');
const draw = delta =>
{
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = '60px Prime';
ctx.fillStyle = 'blue';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello Canvas', canvas.width / 2, canvas.height / 2);
}
// mp4 does not support transparency:
const canvas_to_raw_rgb24 = ctx =>
{
const data = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height).data;
const rgb24 = new Uint8Array(data.length * 0.75);
for (let i = 0, j = 0; i < data.length; ++i)
{
rgb24[j++] = data[i++];
rgb24[j++] = data[i++];
rgb24[j++] = data[i++];
}
return rgb24;
}
// Write the video
(() =>
{
const frames = Math.floor(video.duration / 1000 * video.fps);
let frame = frames;
write();
function write()
{
let ok = true;
do
{
const delta = video.duration - (video.duration / frames * frame);
draw(delta);
--frame;
const data = canvas_to_raw_rgb24(ctx);
if (frame === 0)
{
ffmpeg.stdin.write(data);
}
else
{
ok = ffmpeg.stdin.write(data);
}
} while (frame > 0 && ok)
if (frame > 0)
{
ffmpeg.stdin.once('drain', write);
}
else
{
ffmpeg.stdin.end();
}
}
})();
所以最后,我有一个强大的基础,可以生成一个可播放的视频
评论
stderr
rawVideo
rawvideo
ffmpeg.stdin.write()
false
drain