前端ffmpeg压缩视频

小卷不卷 2024-06-23 12:33:07 阅读 80

下载ffmpeg

npm install @ffmpeg/core @ffmpeg/ffmpeg

这里需要注意两个插件的版本  "@ffmpeg/core": "^0.10.0",  "@ffmpeg/ffmpeg": "^0.10.1"

配置ffmpeg

安装好插件以后,需要配置一下代码,否则会报错:

1、以VUE为例 在vue.config.js文件中配置请求头

devServer: {headers: {'Cross-Origin-Opener-Policy': 'same-origin','Cross-Origin-Embedder-Policy': 'require-corp'}}

2、在页面中实例化ffmpeg的时候可能会报找不到模块儿的错误

const ffmpeg = createFFmpeg({ // ffmpeg路径 corePath: 'ffmpeg-core.js', // 日志 log: true, // 进度 progress: ({ ratio }) => { _this.msg = `完成率: ${(ratio * 100.0).toFixed(1)}%` }})

因此,最好将下载好的插件文件放到public文件夹里面就可以了

 使用ffmpeg压缩视频

引入ffmpeg

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'  文件上传 获取上传的文件

<input id="upload" type="file" accept="video/mp4" capture="camcorder" @change="upload">

注:capture="camcorder" 代表获取手机的摄像头录像   自行查看

实例化ffmpeg

// 实例化ffmpegconst ffmpeg = createFFmpeg({ // ffmpeg路径 corePath: 'ffmpeg-core.js', // 日志 log: true, // 进度 progress: ({ ratio }) => { _this.msg = `完成率: ${(ratio * 100.0).toFixed(1)}%` }}) 压缩视频

await ffmpeg.load()this.msg = '开始压缩'// 把文件加到ffmpeg 写文件ffmpeg.FS('writeFile', name, await fetchFile(file))// await ffmpeg.run('-i', name, '-b', '2000000', '-fs', '4194304', '-preset medium', 'superfast', 'put.mp4')// 开始压缩视频await ffmpeg.run('-i', name, '-b', '2000000', '-crf', '23', '-fs', '4194304', '-s', resolution, 'put.mp4')this.msg = '压缩完成'// 压缩所完成, 读文件 压缩后的文件名称为 put.mp4const data = ffmpeg.FS('readFile', 'put.mp4')

 名词解释:-b: 比特率(也就是速度大小) -crf: 压缩的视频质量   -fs:  把视频压缩到指定大小(有可能会压缩到指定大小,但是可能会剪切指定大小以后的视频片段并删除超出的部分)  -preset medium:  压缩速度    -s: 分辨率  (可以用于指定视频的分辨率  分辨率越大 压缩时间越慢   越小 时间越快)     put.mp4:压缩完成后的文件名称

压缩完的视频格式是blob格式按照需要可以将视频格式转换成file格式,代码如下:

// 类型转换 blob 转换 filetransToFile (data) { console.log(data) const _this = this var file = [] // 转换bolb类型 const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) // 这么写是因为文件转换是异步任务 const transToFile = async (blob, fileName, fileType) => { return new window.File([blob], fileName, { type: fileType }) } const textContain = transToFile(blob, 'put.mp4', 'video/mp4') // 转换完成后可以将file对象传给接口 textContain.then((res) => { file.push(res) console.log('res', res) }) return file}, 如果你嫌压缩的时间太长了,可以控制视频的分辨率   代码如下:

getVideoData () { return new Promise((resolve, reject) => { const videoElement = document.getElementById('video') videoElement.addEventListener('loadedmetadata', function () { resolve({ width: this.videoWidth, height: this.videoHeight, duration: this.duration }) }) })},

拿到视频的宽高,压缩的时候可以等比缩放一下

这里有个坑值得注意一下:如果页面上没有加载出来视频的话,就不会触发得到视频宽高的,需要先把视频加载出来才行  代码如下:

getObjectURL (file) { let url = null window.URL = window.URL || window.webkitURL if (window.URL) { url = window.URL.createObjectURL(file) } else { url = URL.createObjectURL(file) } return url}

献上所有代码

<template> <div class="video-box"> <video id="video" controls object-fill="fill"></video><br /> <input id="upload" type="file" accept="video/mp4" capture="camcorder" @change="upload"> </div></template><script>import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'export default { data () { return { msg: '', videoWidth: '', videoHeight: '', duration: '' } }, methods: { // 选择文件 async upload (e) { console.log('start', e) console.log('start', e.target.files[0]) var _this = this if (e.target.files[0]) { var filename = e.target.files[0].name var filetype = e.target.files[0].type const videoUrl = _this.getObjectURL(e.target.files[0]) const video = document.getElementById('video') video.src = videoUrl this.getVideoData().then((videoObj) => { const file = e.target.files[0] console.log('videoObj:', videoObj) const { width, height } = videoObj const result = _this.squeezVideo(file, filename, filetype, width, height, _this.msg) result.then(res => { console.log('resultFile', res) }) }) } }, // 压缩视频 async squeezVideo (file, filename, filetype, width, height) { console.log('squeezingVideo file name: ', file.name) console.log('squeezingVideo file type: ', file.type) console.log('squeezingVideo file path: ', file.path) console.log('squeezingVideo file size: ', file.size) console.log('squeezingVideo file lastModified: ', file.lastModified) console.log('squeezingVideo file lastModifiedDate: ', file.lastModifiedDate) const _this = this // 分辨率 const resolution = `${width / 2}x${height / 2}` // 实例化ffmpeg const ffmpeg = createFFmpeg({ // ffmpeg路径 corePath: 'ffmpeg-core.js', // 日志 log: true, // 进度 progress: ({ ratio }) => { _this.msg = `完成率: ${(ratio * 100.0).toFixed(1)}%` } }) var { name } = file this.msg = '正在加载 ffmpeg-core.js' // 开始加载 await ffmpeg.load() this.msg = '开始压缩' // 把文件加到ffmpeg 写文件 ffmpeg.FS('writeFile', name, await fetchFile(file)) // await ffmpeg.run('-i', name, '-b', '2000000', '-fs', '4194304', '-preset medium', 'superfast', 'put.mp4') // 开始压缩视频 await ffmpeg.run('-i', name, '-b', '2000000', '-crf', '23', '-fs', '4194304', '-s', resolution, 'put.mp4') this.msg = '压缩完成' // 压缩所完成, 读文件 压缩后的文件名称为 put.mp4 const data = ffmpeg.FS('readFile', 'put.mp4') // 转换压缩后的视频格式 当前为 blob 格式 var filed = _this.transToFile(data) console.log('transToFile: ', filed) return new Promise((resolve, reject) => { if (filed) { resolve({ squzingFile: filed }) } }) }, // 获取视频的宽高分辨率 getVideoData () { return new Promise((resolve, reject) => { const videoElement = document.getElementById('video') videoElement.addEventListener('loadedmetadata', function () { resolve({ width: this.videoWidth, height: this.videoHeight, duration: this.duration }) }) }) }, // 获取上传视频的url getObjectURL (file) { let url = null window.URL = window.URL || window.webkitURL if (window.URL) { url = window.URL.createObjectURL(file) } else { url = URL.createObjectURL(file) } return url }, // 类型转换 blob 转换 file transToFile (data) { console.log(data) const _this = this var file = [] // 转换bolb类型 const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) // 这么写是因为文件转换是异步任务 const transToFile = async (blob, fileName, fileType) => { return new window.File([blob], fileName, { type: fileType }) } const textContain = transToFile(blob, 'put.mp4', 'video/mp4') // 转换完成后可以将file对象传给接口 textContain.then((res) => { file.push(res) console.log('res', res) // _this.confirm(file) }) return file } }}</script>

 可以封装压缩视频代码

目录src/utils/ffmpeg.js

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'// 压缩视频const squeezVideo = async (file, filename, filetype, width, height) => { console.log('file', file) console.log('filename', filename) console.log('filetype', filetype) console.log('width', width) console.log('height', height) // 分辨率 const resolution = `${width / 2}x${height / 2}` // 实例化ffmpeg const ffmpeg = createFFmpeg({ // ffmpeg路径 corePath: 'ffmpeg-core.js', // 日志 log: true // 进度 // progress: ({ ratio }) => { // msg = `完成率: ${(ratio * 100.0).toFixed(1)}%` // } }) console.log('file---', file) var { name } = file // msg = '正在加载 ffmpeg-core.js' // 开始加载 await ffmpeg.load() // msg = '开始压缩' // 把文件加到ffmpeg 写文件 ffmpeg.FS('writeFile', name, await fetchFile(file)) // await ffmpeg.run('-i', name, '-b', '2000000', '-fs', '4194304', '-preset medium', 'superfast', 'put.mp4') // 开始压缩视频 await ffmpeg.run('-i', name, '-b', '2000000', '-crf', '23', '-fs', '4194304', '-s', resolution, 'put.mp4') // msg = '压缩完成' // 压缩所完成, 读文件 压缩后的文件名称为 put.mp4 const data = ffmpeg.FS('readFile', 'put.mp4') // 转换压缩后的视频格式 当前为 blob 格式 var res = transToFile(data, filename, filetype) return new Promise((result, reject) => { result({ filed: res }) })}// 类型转换 blob 转换 fileconst transToFile = (data, filename, filetype) => { var filed = [] console.log(data) // 转换bolb类型 const blob = new Blob([data], { type: 'text/plain;charset=utf-8' }) // 这么写是因为文件转换是异步任务 const transToFile = async (blob, fileName, fileType) => { return new window.File([blob], fileName, { type: fileType }) } const textContain = transToFile(blob, filename, filetype) // 转换完成后可以将file对象传给接口 textContain.then((res) => { filed.push(res) console.log('res', res) }) return filed}export { squeezVideo }

注意事项:

ffmpeg是一款很强大的视频编辑工具,你可以自己研究研究,上述的代码可以自己封装一下,另外ffmpeg不能用于微信环境或者是企微环境,代码执行不下去。

效果视频

ffmpeg压缩视频

如果各位视频看不到的话,请移步到我的主页进行观看

 还有一种插件好像可以  video-conversion.js  但是没有找到官网,有小伙伴研究成功的话,踢我一下哈

文章若有不足之处  烦请指正



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。