使用 Node JS 将 h264 文件转换或包装为 mp4
Convert or wrap h264 file to mp4 with Node JS
我正在 Raspberry Pi 上拍摄视频,并希望将原始 h264 文件转换为 mp4 文件,或者可能将其包装在 mp4 中,就像使用命令 line/Python 完成的那样。然而,我希望在 NodeJS 中做到这一点。似乎有许多节点 JS 库在 npm 上使用 Raspberry Pi 的 mp4-box 库。但是,其中 none 个具有适当的文档或似乎适合我的项目的需要。我不知道我是否遗漏了什么或者这是不可能的。
了解 mp4(如 mkv)是一个容器非常重要。您可以向这些容器添加视频、音频、字幕 "layer"。而且H.264已经是一种压缩格式,不是原始视频格式。
没有一种直接的方法可以将 H.264 编码文件嵌入到 MP4 容器中,而无需从头开始实际构建整个文件结构。 是 可行的,但为了做到这一点,您需要了解 mp4 容器格式(它在很大程度上基于 Quicktime MOV 容器)并使用 TypedArrays
构建它,结果你可以保存为 MP4 文件(我创建了一个 paste here 描述容器文件结构)。
另一种方法是从 Node.js 生成 FFmpeg(或直接使用该软件)并提供 H.264 作为输入并将其保存为 MP4 文件。这很简单。该命令类似于:
ffmpeg -i yourH264encodedFileHere -c:v copy mp4FileContainer.mp4
到 运行 从 Node 可以使用 spawn
(参见示例)。
这种基本方法的替代方法是安装和使用 fluent-ffmpeg NPM module 来完成所有繁重的工作。
例子
var ffmpeg = require("fluent-ffmpeg");
var inFilename = "video.h264";
var outFilename = "video.mp4";
ffmpeg(inFilename)
.outputOptions("-c:v", "copy") // this will copy the data instead or reencode it
.save(outFilename);
一些注意事项:
- fluent 可以对文件名(空格等)挑剔。
- FFmpeg 需要预先安装并在全局路径中可用。如果你不想这样,你可以使用
ffmpeg.setFfmpegPath(pathToFFmpegBin)
代替。
- 要在 RPI 上安装 FFmpeg,this resource 可能会有用。
尝试在我的 pi 零上安装 ffmpeg 花费了两个多小时并以错误结束,所以这是包装 mp4 的替代方法:
- 安装 MP4Box
sudo apt install -y gpac
- 在您的 Node 脚本中,使用
exec
调用 MP4Box
命令,就像 Raspivid docs 所说的那样
import raspivid from 'raspivid';
import { createWriteStream, unlinkSync } from 'fs'
import { exec } from 'child_process'
import internal from 'stream'
// Helper: promisify streaming to a file
const streamToFile = (inputStream: internal.Readable, filePath: string) => {
return new Promise((resolve, reject) => {
const fileWriteStream = createWriteStream(filePath)
inputStream
.pipe(fileWriteStream)
.on('finish', resolve)
.on('error', reject)
})
}
// Helper: promisify executing shell command
const execPromise = (command: string) => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
doLogInfo(stdout)
if (error) {
console.error(error.message)
reject(error)
}
if (stderr) {
console.error(stderr)
resolve(stderr)
}
resolve()
})
})
}
// Driver
const getMp4FromRaspivid = async () => {
// Record vid w raspivid
const h264Path = './myVid.h264'
const readVideoStream = await raspivid(VIDEO_OPTIONS) as internal.Readable;
await streamToFile(readVideoStream, h264Path)
// Wrap in mp4
const mp4Path = './myVid.mp4'
const execCommand = ['MP4Box', '-add', h264Path, mp4Path].join(' ')
await execPromise(execCommand)
// Delete h264
unlinkSync(h264Path)
}
我正在 Raspberry Pi 上拍摄视频,并希望将原始 h264 文件转换为 mp4 文件,或者可能将其包装在 mp4 中,就像使用命令 line/Python 完成的那样。然而,我希望在 NodeJS 中做到这一点。似乎有许多节点 JS 库在 npm 上使用 Raspberry Pi 的 mp4-box 库。但是,其中 none 个具有适当的文档或似乎适合我的项目的需要。我不知道我是否遗漏了什么或者这是不可能的。
了解 mp4(如 mkv)是一个容器非常重要。您可以向这些容器添加视频、音频、字幕 "layer"。而且H.264已经是一种压缩格式,不是原始视频格式。
没有一种直接的方法可以将 H.264 编码文件嵌入到 MP4 容器中,而无需从头开始实际构建整个文件结构。 是 可行的,但为了做到这一点,您需要了解 mp4 容器格式(它在很大程度上基于 Quicktime MOV 容器)并使用 TypedArrays
构建它,结果你可以保存为 MP4 文件(我创建了一个 paste here 描述容器文件结构)。
另一种方法是从 Node.js 生成 FFmpeg(或直接使用该软件)并提供 H.264 作为输入并将其保存为 MP4 文件。这很简单。该命令类似于:
ffmpeg -i yourH264encodedFileHere -c:v copy mp4FileContainer.mp4
到 运行 从 Node 可以使用 spawn
(参见示例)。
这种基本方法的替代方法是安装和使用 fluent-ffmpeg NPM module 来完成所有繁重的工作。
例子
var ffmpeg = require("fluent-ffmpeg");
var inFilename = "video.h264";
var outFilename = "video.mp4";
ffmpeg(inFilename)
.outputOptions("-c:v", "copy") // this will copy the data instead or reencode it
.save(outFilename);
一些注意事项:
- fluent 可以对文件名(空格等)挑剔。
- FFmpeg 需要预先安装并在全局路径中可用。如果你不想这样,你可以使用
ffmpeg.setFfmpegPath(pathToFFmpegBin)
代替。 - 要在 RPI 上安装 FFmpeg,this resource 可能会有用。
尝试在我的 pi 零上安装 ffmpeg 花费了两个多小时并以错误结束,所以这是包装 mp4 的替代方法:
- 安装 MP4Box
sudo apt install -y gpac
- 在您的 Node 脚本中,使用
exec
调用MP4Box
命令,就像 Raspivid docs 所说的那样
import raspivid from 'raspivid';
import { createWriteStream, unlinkSync } from 'fs'
import { exec } from 'child_process'
import internal from 'stream'
// Helper: promisify streaming to a file
const streamToFile = (inputStream: internal.Readable, filePath: string) => {
return new Promise((resolve, reject) => {
const fileWriteStream = createWriteStream(filePath)
inputStream
.pipe(fileWriteStream)
.on('finish', resolve)
.on('error', reject)
})
}
// Helper: promisify executing shell command
const execPromise = (command: string) => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
doLogInfo(stdout)
if (error) {
console.error(error.message)
reject(error)
}
if (stderr) {
console.error(stderr)
resolve(stderr)
}
resolve()
})
})
}
// Driver
const getMp4FromRaspivid = async () => {
// Record vid w raspivid
const h264Path = './myVid.h264'
const readVideoStream = await raspivid(VIDEO_OPTIONS) as internal.Readable;
await streamToFile(readVideoStream, h264Path)
// Wrap in mp4
const mp4Path = './myVid.mp4'
const execCommand = ['MP4Box', '-add', h264Path, mp4Path].join(' ')
await execPromise(execCommand)
// Delete h264
unlinkSync(h264Path)
}