下载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
// 实例化ffmpeg
const 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.mp4
const data = ffmpeg.FS('readFile', 'put.mp4')
名词解释:-b: 比特率(也就是速度大小) -crf: 压缩的视频质量 -fs: 把视频压缩到指定大小(有可能会压缩到指定大小,但是可能会剪切指定大小以后的视频片段并删除超出的部分) -preset medium: 压缩速度 -s: 分辨率 (可以用于指定视频的分辨率 分辨率越大 压缩时间越慢 越小 时间越快) put.mp4:压缩完成后的文件名称
- 压缩完的视频格式是blob格式按照需要可以将视频格式转换成file格式,代码如下:
// 类型转换 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)
})
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 转换 file
const 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 但是没有找到官网,有小伙伴研究成功的话,踢我一下哈
文章若有不足之处 烦请指正
版权归原作者 小卷不卷 所有, 如有侵权,请联系我们删除。