JS代码:
// 顶层变量存储文件对象,供后续合成使用
let videoFile = null;
let imageFile = null;
// 视频选择与预览
fileVideo.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
videoFile = file;
const url = URL.createObjectURL(file);
videoPreview.innerHTML = `<video src="${url}" controls></video>`;
}
});
// 图片选择与预览
fileImage.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
imageFile = file;
const url = URL.createObjectURL(file);
imagePreview.innerHTML = `<img src="${url}">`;
}
});
const {
Input,
Output,
Conversion,
ALL_FORMATS,
BlobSource,
Mp4OutputFormat,
BufferTarget
} = Mediabunny;
form.addEventListener('submit', async (event) => {
event.preventDefault();
const submit = form.querySelector('button');
submit.textContent = '合成中...';
submit.disabled = true;
// 水印图片
const watermark = imagePreview.querySelector('img');
const input = new Input({
formats: ALL_FORMATS,
source: new BlobSource(videoFile)
});
const output = new Output({
format: new Mp4OutputFormat(), // The format of the file
target: new BufferTarget()
});
let ctx = null;
const conversion = await Conversion.init({
input,
output,
video: {
process: (sample) => {
if (!ctx) {
// Create a canvas for image compositing
const canvas = new OffscreenCanvas(
sample.displayWidth,
sample.displayHeight
);
ctx = canvas.getContext('2d');
}
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
sample.draw(ctx, 0, 0);
ctx.drawImage(watermark, 80, 80 * watermark.naturalHeight / watermark.naturalWidth);
return ctx.canvas;
}
}
});
conversion.onProgress = function (progress) {
submit.textContent = `合成中...${Math.round(progress * 100)}%`;
};
await conversion.execute();
const buffer = output.target.buffer;
const url = URL.createObjectURL(new Blob([buffer]));
result.innerHTML = `<video src="${url}" controls></video>`;
submit.textContent = '开始合成';
submit.disabled = false;
});