我开始使用 WebCodecs API。我尝试从一组 jpg 图像创建 mp4 视频。 从简单的事情开始,我尝试从一张 jpg 图像创建 mp4。当调用输出回调时,它将视频块保存到 mp4 文件中。
const downloadVideo = (data) => {
const link = document.createElement("a");
const file = new Blob([data], { type: 'video/mp4' });
link.href = URL.createObjectURL(file);
link.download = "video.mp4";
link.click();
URL.revokeObjectURL(link.href);
};
$('#create_movie').click( function(ev) {
const videoEncoder = new VideoEncoder({
output(chunk, metadata) {
console.log("timestamp:" + chunk.timestamp);
console.log("length:" + chunk.byteLength);
console.log("video chunk type:" + chunk.type);
console.log("duration:" + chunk.duration)
console.log(JSON.stringify(metadata));
var videoBlob = new ArrayBuffer(chunk.byteLength);
chunk.copyTo(videoBlob);
downloadVideo(videoBlob);
},
error(error) {
console.log(error);
},
});
videoEncoder.configure( {
codec: "avc1.42001E",
height: 480,
width: 640,
framerate: 1,
bitrate: 2_000_000, // 2 Mbps
});
console.log("video encoder created! " + videoEncoder);
const myImage = new Image();
myImage.src = "samples/pic0.jpg";
var imageBitmap = null;
var imageBitmapPromise = null;
myImage.onload = () => {
imgContext.drawImage(myImage,0,0);
imageBitmapPromise = createImageBitmap(myImage);
imageBitmap = imageBitmapPromise.then( result => {
const ms = 1_000_000; // 1µs
const fps = 10;
return new VideoFrame(result, {
timestamp: (ms * 1) / fps,
duration: ms / fps
},false);
}).then(vidFrame => {
console.log("imageBitmap:" + vidFrame);
console.log("video frame timestamp:" + vidFrame.timestamp);
console.log("format:" + vidFrame.format);
videoEncoder.encode(vidFrame, { keyFrame: true });
videoEncoder.flush();
vidFrame.close();
});
console.log("imageBitmap:" + imageBitmap);
}
});
});
不幸的是,视频打开时显示错误消息:您尝试播放的文件是空文件 我使用 ffmpeg 工具从同一个 jpg 文件生成了 mp4:
ffmpeg -i pic%d.jpg -vcodec mpeg4 test.mp4
两个文件的结构完全不同:
左边是ffmpeg生成的视频。它包含我的代码生成的 vido 文件中缺少的一些元数据。所以我的问题是:如何使用 WebCodecs API 正确生成视频文件?
“我尝试从一组 jpg 图像创建 mp4 视频”
WebCodecs 不会对 MP4 或 AVI 等容器进行任何混合。它只提供 原始编码帧(视频或音频),然后代码编写者自己将决定输出容器格式(如果需要)。
您是否假设您需要 MP4 才能显示它?如果是:H.264 可以按原样播放,因为它是一种视频格式。
或者您确定需要 MP4 输出(例如: HTML5)?如果是:祝你好运。不难,但是很乏味。
如果您只是想检查您的代码是否正常工作,请使用像 VLC 这样的播放器来播放您保存的 H.264 文件。
VLC 在桌面上运行(因此只需将文件拖到其窗口中即可检查视觉效果)。
对于在线播放,您可以找到一个支持 JS 的复用器,例如:JMuxer,或者喜欢学习 MP4 结构,然后编写代码。我试图在另一个问题上解释一个起点,但他们从未回应。
您首先需要一个正确编码的 H.264 文件,否则没有播放器会接受它(请参阅下面的代码修复)。
(1) 在函数
downloadVideo()
中,您需要作为二进制流下载..
替换:
const file = new Blob([data], { type: 'video/mp4' });
与:
let file = new Blob(
[ Uint8Array.from( data ) ] ,
{type: "application/octet-stream"}
);
使用
"application/octet-stream"
将保证您获得实际的 binary 字节值(没有任何不需要的格式转换,例如 ASCII 或 UTF 等)。另外更安全(问题更少)的方法是将输出字节包装到新的 Uint8Array.from
缓冲区中。
(2) 保存时您需要包含元数据。 Webcodecs 将其提供为
metadata.decoderConfig
。
const videoEncoder = new VideoEncoder(
{
output(chunk, metadata)
{
console.log("timestamp:" + chunk.timestamp);
console.log("length:" + chunk.byteLength);
console.log("video chunk type:" + chunk.type);
console.log("duration:" + chunk.duration)
console.log(JSON.stringify(metadata));
var myMetadata; var videoBlob
if (metadata.decoderConfig)
{
//# save the decoder's description/config ...
//# is SPS and PPS metadata needed by players to display H.264)
myMetadata = new Uint8Array( metadata.decoderConfig.description );
}
else
{ alert("WARNING : No Metadata Found --> Saved bytes will not be playable" );
videoBlob = new ArrayBuffer(chunk.byteLength);
chunk.copyTo(videoBlob);
//# combine the two arrays (in the shown order of appearance)
var output_Bytes = [...myMetadata, ...videoBlob];
downloadVideo( output_Bytes );
},
error(error) { console.log(error); },
}
);