如何从 VideoFrame 创建视频 srcObject?
How to create video srcObject from VideoFrame?
我现在正在学习webcodecs,看到的东西如下:
所以我想知道它是否可以在包含多张图片的视频元素上播放视频。我尝试了很多次,但仍然无法正常工作。
我从图片创建 videoFrame,然后使用 MediaStreamTrackGenerator 创建媒体轨道。但是调用play()时视频显示为黑色。
这是我的代码:
const test = async () => {
const imgSrcList = [
'https://gw.alicdn.com/imgextra/i4/O1CN01CeTlwJ1Pji9Pu6KW6_!!6000000001877-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i3/O1CN01h7tWZr1ZiTEk1K02I_!!6000000003228-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i4/O1CN01CSwWiA1xflg5TnI9b_!!6000000006471-2-tps-62-66.png',
];
const imgEleList: HTMLImageElement[] = [];
await Promise.all(
imgSrcList.map((src, index) => {
return new Promise((resolve) => {
let img = new Image();
img.src = src;
img.crossOrigin = 'anonymous';
img.onload = () => {
imgEleList[index] = img;
resolve(true);
};
});
}),
);
const trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });
const writer = trackGenerator.writable.getWriter();
await writer.ready;
for (let i = 0; i < imgEleList.length; i++) {
const frame = new VideoFrame(imgEleList[i], {
duration: 500,
timestamp: i * 500,
alpha: 'keep',
});
await writer.write(frame);
frame.close();
}
// Call ready again to ensure that all chunks are written before closing the writer.
await writer.ready.then(() => {
writer.close();
});
const stream = new MediaStream();
stream.addTrack(trackGenerator);
const videoEle = document.getElementById('video') as HTMLVideoElement;
videoEle.onloadedmetadata = () => {
videoEle.play();
};
videoEle.srcObject = stream;
};
谢谢!
免责声明:
我不是该领域的专家,这是我第一次以这种方式使用 API。规范和当前的实现似乎不匹配,并且很可能在不久的将来事情会发生变化。因此,请尽可能多地接受这个答案,它只有经过试验才能支持。
您的实施中有几处似乎是错误的:
duration
和 timestamp
设置为 micro 秒,即 1/1,000,000 秒。你的 500
持续时间只有半毫秒,大约是 2000FPS,你的三张图片将在 1.5 毫秒内全部显示。你会想要改变它。
- 在当前 Chrome 的实现中,您需要指定 VideoFrameInit 字典的
displayWidth
和 displayHeight
成员(尽管如果我正确阅读规范应该默认为源图像的宽度和高度)。
然后有些事情我不太确定,但你似乎不能 batch-write 很多帧。 timestamp
字段在这种情况下似乎有点无用(即使它需要存在,即使具有无意义的值)。再一次,规格已经改变,所以很难知道它是否是一个实现错误,或者它是否应该像那样工作,但无论如何它是这样的(除非我也错过了什么)。
因此,要解决该限制,您需要定期写入流并在您希望它们出现时附加帧。
这是一个这样的例子,当我们希望它被呈现时,通过向 WritableStream 写入一个新的框架,试图让它接近您自己的实现。
const test = async() => {
const imgSrcList = [
'https://gw.alicdn.com/imgextra/i4/O1CN01CeTlwJ1Pji9Pu6KW6_!!6000000001877-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i3/O1CN01h7tWZr1ZiTEk1K02I_!!6000000003228-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i4/O1CN01CSwWiA1xflg5TnI9b_!!6000000006471-2-tps-62-66.png',
];
// rewrote this part to use ImageBitmaps,
// using HTMLImageElement works too
// but it's less efficient
const imgEleList = await Promise.all(
imgSrcList.map((src) => fetch(src)
.then(resp => resp.ok && resp.blob())
.then(createImageBitmap)
)
);
const trackGenerator = new MediaStreamTrackGenerator({
kind: 'video'
});
const duration = 1000 * 1000; // in µs (1/1,000,000s)
let i = 0;
const presentFrame = async() => {
i++;
const writer = trackGenerator.writable.getWriter();
const img = imgEleList[i % imgEleList.length];
await writer.ready;
const frame = new VideoFrame(img, {
duration, // value doesn't mean much, but required
timestamp: i * duration, // ditto
alpha: 'keep',
displayWidth: img.width * 2, // required
displayHeight: img.height * 2, // required
});
await writer.write(frame);
frame.close();
await writer.ready;
// unlock our Writable so we can write again at next frame
writer.releaseLock();
setTimeout(presentFrame, duration / 1000);
}
presentFrame();
const stream = new MediaStream();
stream.addTrack(trackGenerator);
const videoEle = document.getElementById('video');
videoEle.srcObject = stream;
};
test().catch(console.error)
<video id=video controls autoplay muted></video>
我现在正在学习webcodecs,看到的东西如下:
所以我想知道它是否可以在包含多张图片的视频元素上播放视频。我尝试了很多次,但仍然无法正常工作。 我从图片创建 videoFrame,然后使用 MediaStreamTrackGenerator 创建媒体轨道。但是调用play()时视频显示为黑色。
这是我的代码:
const test = async () => {
const imgSrcList = [
'https://gw.alicdn.com/imgextra/i4/O1CN01CeTlwJ1Pji9Pu6KW6_!!6000000001877-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i3/O1CN01h7tWZr1ZiTEk1K02I_!!6000000003228-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i4/O1CN01CSwWiA1xflg5TnI9b_!!6000000006471-2-tps-62-66.png',
];
const imgEleList: HTMLImageElement[] = [];
await Promise.all(
imgSrcList.map((src, index) => {
return new Promise((resolve) => {
let img = new Image();
img.src = src;
img.crossOrigin = 'anonymous';
img.onload = () => {
imgEleList[index] = img;
resolve(true);
};
});
}),
);
const trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });
const writer = trackGenerator.writable.getWriter();
await writer.ready;
for (let i = 0; i < imgEleList.length; i++) {
const frame = new VideoFrame(imgEleList[i], {
duration: 500,
timestamp: i * 500,
alpha: 'keep',
});
await writer.write(frame);
frame.close();
}
// Call ready again to ensure that all chunks are written before closing the writer.
await writer.ready.then(() => {
writer.close();
});
const stream = new MediaStream();
stream.addTrack(trackGenerator);
const videoEle = document.getElementById('video') as HTMLVideoElement;
videoEle.onloadedmetadata = () => {
videoEle.play();
};
videoEle.srcObject = stream;
};
谢谢!
免责声明:
我不是该领域的专家,这是我第一次以这种方式使用 API。规范和当前的实现似乎不匹配,并且很可能在不久的将来事情会发生变化。因此,请尽可能多地接受这个答案,它只有经过试验才能支持。
您的实施中有几处似乎是错误的:
duration
和timestamp
设置为 micro 秒,即 1/1,000,000 秒。你的500
持续时间只有半毫秒,大约是 2000FPS,你的三张图片将在 1.5 毫秒内全部显示。你会想要改变它。- 在当前 Chrome 的实现中,您需要指定 VideoFrameInit 字典的
displayWidth
和displayHeight
成员(尽管如果我正确阅读规范应该默认为源图像的宽度和高度)。
然后有些事情我不太确定,但你似乎不能 batch-write 很多帧。 timestamp
字段在这种情况下似乎有点无用(即使它需要存在,即使具有无意义的值)。再一次,规格已经改变,所以很难知道它是否是一个实现错误,或者它是否应该像那样工作,但无论如何它是这样的(除非我也错过了什么)。
因此,要解决该限制,您需要定期写入流并在您希望它们出现时附加帧。
这是一个这样的例子,当我们希望它被呈现时,通过向 WritableStream 写入一个新的框架,试图让它接近您自己的实现。
const test = async() => {
const imgSrcList = [
'https://gw.alicdn.com/imgextra/i4/O1CN01CeTlwJ1Pji9Pu6KW6_!!6000000001877-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i3/O1CN01h7tWZr1ZiTEk1K02I_!!6000000003228-2-tps-62-66.png',
'https://gw.alicdn.com/imgextra/i4/O1CN01CSwWiA1xflg5TnI9b_!!6000000006471-2-tps-62-66.png',
];
// rewrote this part to use ImageBitmaps,
// using HTMLImageElement works too
// but it's less efficient
const imgEleList = await Promise.all(
imgSrcList.map((src) => fetch(src)
.then(resp => resp.ok && resp.blob())
.then(createImageBitmap)
)
);
const trackGenerator = new MediaStreamTrackGenerator({
kind: 'video'
});
const duration = 1000 * 1000; // in µs (1/1,000,000s)
let i = 0;
const presentFrame = async() => {
i++;
const writer = trackGenerator.writable.getWriter();
const img = imgEleList[i % imgEleList.length];
await writer.ready;
const frame = new VideoFrame(img, {
duration, // value doesn't mean much, but required
timestamp: i * duration, // ditto
alpha: 'keep',
displayWidth: img.width * 2, // required
displayHeight: img.height * 2, // required
});
await writer.write(frame);
frame.close();
await writer.ready;
// unlock our Writable so we can write again at next frame
writer.releaseLock();
setTimeout(presentFrame, duration / 1000);
}
presentFrame();
const stream = new MediaStream();
stream.addTrack(trackGenerator);
const videoEle = document.getElementById('video');
videoEle.srcObject = stream;
};
test().catch(console.error)
<video id=video controls autoplay muted></video>