nginx
location /videos/player { add_header Cross-Origin-Opener-Policy "same-origin"; add_header Cross-Origin-Embedder-Policy "require-corp"; }
vue.config.js
devServer: { proxy: {}, // 可能会影响图片的加载 headers: { "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp", }, }
package.json
"dependencies": { "@ffmpeg/core": "~0.10.0", "@ffmpeg/ffmpeg": "~0.10.1", }
从 node_modules/@ffmpeg 的 dist 目录中找到以下文件放入 public/js/
ffmpeg-core.js ffmpeg-core.wasm ffmpeg-core.worker.js
AviPlayer.vue (节选)
import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg"; export default { data() { return { src: null, loading: false, // false 或 xx% }; }, computed: { ext() { let self = this; let ext = null; if (self.src) { ext = self.src .replace(/.*(?=\.w+)/, "") // http://a/b/c/d.e?f=g -> .e?f=g .replace(/\?.*/, ""); // .e?f=g -> .e ext = ext && ext.toLowerCase(); } return ext; }, }, methods: { async convertAvi2mp4(aviSrc) { let self = this; let src = aviSrc; try { const ffmpeg = createFFmpeg({ log: true, corePath: "/js/ffmpeg-core.js", // 不写这行时会从 CDN 中加载 }); ffmpeg.setProgress(({ ratio }) => { let progress = Math.floor(ratio * 100); progress = progress < 100 ? `${progress}%` : "准备中"; self.loading = progress; }); await ffmpeg.load(); // ffmpeg.FS("writeFile", `videoFile${self.ext}`, await fetchFile(src)); // 无 fetch 进度 ffmpeg.FS("writeFile", `videoFile${self.ext}`, await self.getFile(src)); // 有 fetch 进度 await ffmpeg.run("-i", `videoFile${self.ext}`, "videoFile.mp4"); const data = ffmpeg.FS("readFile", "videoFile.mp4"); src = URL.createObjectURL(new Blob([data.buffer], { type: "video/mp4" })); } catch (e) { console.log("avi2mp4.fail", e); } }, // 带进度版的 fetchFile async getFile(src) { let data = src; if (src instanceof File || src instanceof Blob) { data = await readFromBlobOrFile(src); } else { const res = await fetch(src) .then((response) => { const contentLength = response.headers.get('Content-Length'); const contentType = response.headers.get('Content-Type'); const total = contentLength ? parseInt(contentLength, 10) : NaN; const reader = response.body.getReader(); let received = 0; let blobParts = []; return new Promise((resolve, reject) => { const process = () => { reader.read() .then(({ value, done }) => { if (done) { const blob = new Blob(blobParts, { type: contentType }); resolve(blob); } else { blobParts.push(value); received += value.length; let progress = Math.floor((received / total) * 100); progress = progress < 100 ? `已下载${progress}%` : '准备读取'; console.log('下载进度', `${progress}, ${received}/${total}`); process(); } }, reject); }; process(); }); }) .catch((e) => new Promise((resolve, reject) => reject(e))); data = await res.arrayBuffer(); } const blob = new Uint8Array(data); return blob; }, }, };