我想使用 Webcodecs API 播放原始 .h264 流并提取特定帧。它保存为 .h264 文件,本质上只是混合到 .mp4 容器格式之前的视频数据。
应该可以使用新的 WebCodecs API 进行播放。我无法提供
avc1.64001e
VideoDecoder 以及格式正确的 EncodedVideoChunk。我怎样才能这样做呢?我知道分辨率、帧速率和帧计数,但如何获取这些信息并从中创建 EncodedVideoChunk?
我有一个可能有千兆字节的大本地文件,我通过以下方式收到:
const readableStream = this.data.file.stream();
const reader = readableStream.getReader();
VideoDecoder: decode()
?
我知道你可以让 mp4box.js
处理解复用和提供块。但我已经将数据分解了。那么,除了混合并通过 mp4box.js
提供它,然后再次将其分解之外,我该如何克服这一步呢?
为了轻松解码,可以方便地拆分流分组 SPS + PPS + IDR,这可以通过以下方式完成:
for (let i = 0; i < buffer.length; i++) {
if (buffer[i] === 0 && buffer[i+1] === 0 && buffer[i+2] === 0 && buffer[i+3] === 1 && (buffer[i+4]&0x1f !== 6) && (buffer[i+4]&0x1f) !== 7) {
if (i !== start) {
// process a frame
}
start = i;
}
}
一个简单的解码,具有最少的错误情况管理,设置类型和编解码器设置为虚拟值,使用annexb将让VideoDecoder用帧内容覆盖值,可以是:
async function process(data) {
if (decoder.state !== "configured") {
await decoder.configure({ codec: "avc1.64001e" });
}
const chunk = new EncodedVideoChunk({
timestamp: (performance.now() + performance.timeOrigin) * 1000,
type: "key",
data,
});
return decoder.decode(chunk);
}
综合起来:
const decoder = new VideoDecoder({
output: (frame) => {
canvas.getContext("2d").drawImage(frame, 0, 0, frame.displayWidth, frame.displayHeight);
frame.close();
},
error: (e) => console.warn(e.message),
})
async function process(data) {
if (decoder.state !== "configured") {
const config = { codec: "avc1.4d002a" };
await decoder.configure(config);
}
const chunk = new EncodedVideoChunk({
timestamp: (performance.now() + performance.timeOrigin) * 1000,
type: "key",
data,
});
return decoder.decode(chunk);
}
function load() {
const file = document.getElementById("file").files[0];
const stream = file.stream();
const reader = stream.getReader();
let buffer = new Uint8Array();
return reader.read().then(async function processChunk({done, value}) {
if (done) {
return process(buffer);
} else {
buffer = new Uint8Array([...buffer, ...value]);
let start = 0;
for (let i = 0; i < buffer.length; i++) {
if (buffer[i] === 0 && buffer[i+1] === 0 && buffer[i+2] === 0 && buffer[i+3] === 1 && (buffer[i+4]&0x1f !== 6) && (buffer[i+4]&0x1f) !== 7) {
if (i !== start) {
const frame = buffer.slice(start, i);
await process(frame);
}
start = i;
}
}
buffer = buffer.slice(start);
return reader.read().then(processChunk);
}
});
}
<input type="file" id="file" onchange="load()" />
<canvas id="canvas"></canvas>