我正在开发一个涉及帧操作的项目,我决定为此选择节点画布 API。我曾经使用 OpenCV Python,有一个
cv2.VideoCapture
类将视频作为输入并准备读取视频的帧,我们可以一次循环一个帧,而不必一次加载所有帧在记忆中。
现在我尝试了很多方法来使用 ffmpeg 复制相同的内容,即尝试以有序但“按需”的方式从视频加载帧。
我尝试使用 ffmpeg 作为子进程来处理帧并突出帧。
const spawnProcess = require('child_process').spawn,
ffmpeg = spawnProcess('ffmpeg', [
'-i', 'test.mp4',
'-vcodec', 'png',
'-f', 'rawvideo',
'-s', '1920*1080', // size of one frame
'pipe:1'
]);
ffmpeg.stdout.on('data', (data) => {
try {
// console.log(tf.node.decodeImage(data).shape)
console.log(`${++i} frames read`)
//context.drawImage(data, 0, 0, width, height)
} catch(e) {
console.log(e)
}
})
控制台中的值显示大约 4000 + 控制台日志,但视频只有 150 帧,经过大量调查和控制台记录数据后,我发现这是缓冲区数据,并且它没有对每一帧进行处理。 on-data 函数以非结构化方式返回缓冲区数据 我想从视频中读取帧并在内存中一次处理每个帧,我不想同时将所有帧保存在内存或文件系统中。
我还想以可以使用drawImage在画布上渲染的格式传输帧
我发现这是缓冲区数据,并且它没有为每一帧处理它。
正确。
on-data 函数以非结构化方式返回缓冲区数据我想从视频中读取帧并在内存中一次处理每个帧
您不能依赖 STDIO 进行数据分帧。您所在的系统控制着应用程序之间的缓冲工作方式。因此,您获得的数据量及其时间应被视为未定义。您必须在应用程序中缓冲数据。
现在,您不必进行太多缓冲,并且您的方案不必非常复杂。只需进行缓冲,直到有足够的字节等于或大于原始帧所需的字节。然后,对该帧执行您需要执行的操作,将其从缓冲区中删除,并继续缓冲下一帧。
由于您使用的是 JavaScript,您可能会考虑使用流 API 来执行此操作,这使得处理背压等问题变得更加容易。 (毕竟,您的应用程序实际上可能比 FFmpeg 处理得慢,并且您可能希望在应用程序运行时减慢该流的速度,这样您就不会耗尽内存。)