Vue前端ffmpeg压缩视频再上传(全网唯一公开真正实现)

news2025/1/13 10:23:57

1.Vue项目中安装插件ffmpeg

1.1 插件版本依赖配置

两个插件的版本  "@ffmpeg/core": "^0.10.0",  "@ffmpeg/ffmpeg": "^0.10.1"
package.jsonpackage-lock.json 都加入如下ffmpeg的版本配置:

1.2 把ffmpeg安装到项目依赖目录里

terminal运行命令:

install -i
或直接运行命令安装ffmpeg:
npm install @ffmpeg/ffmpeg @ffmpeg/core -S;
安装后package-lock.json会自动写入如下配置:
1.2.1 报错处理

如果出现安装问题:

①先在npm -i命令后加--legacy-peer-deps 或者   --force运行

npm i --force

②如果上步不行,尝试删除这个安装依赖目录node_modules和package-lock.json文件,重试npm -i

请参考:

npm ERR! code ERESOLVEnpm ERR! ERESOLVE could not resolve 报错,版本冲突,最全解决步骤(#^.^#)_npm err! code eresolve npm err! eresolve could not-CSDN博客

1.2.2 镜像过期

 安装ffmpeg可能提示镜像证书过期

你使用的镜像地址可能还是这个过期的淘宝镜像:https://registry.npm.taobao.org/

按如下步骤重设镜像地址:

①查看镜像:npm config list

②清理镜像缓存:npm cache clean --force

③设置镜像:npm config set registry https://registry.npmmirror.com/(国内推荐淘宝新镜像)

也可:npm config set registry https://registry.npmjs.org/

 1.3 项目里安装ffmpeg

在项目里的ffmpeg插件目录下找到: 
复制到项目代码的public目录里

1.3.1 ffmpeg压缩参数配置

-b:指定视频比特率

 -crf:恒定速率因子,控制输出视频质量的参数。

这个参数的取值范围为0~51,其中0为无损模式。数值越大,画质越差,生成的文件却越小。

从主观上讲,18~28是一个合理的范围。18被认为是视觉无损的(从技术角度上看当然还是有损的),它的输出视频质量和输入视频相当。

-fs:压缩到指定大小,单位Byte

-s:分辨率

 控制压缩后视频质量的最重要的是后面三个参数:crf、fs、s

1.4 项目里引用并封装ffmpeg

在util目录下封装ffmpeg.js以便项目全局引用

封装的工具通过:

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'

 把ffmpeg插件引入到项目里使用。

完整ffmpeg.js代码:

import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'
let ffmpeg = {};

ffmpeg.squeezVideo = async function(file, filename, filetype, width, height, msg) {
    console.log('file', file)
    console.log('filename', filename)
    console.log('filetype', filetype)
    console.log('width', width)
    console.log('height', height)

    // 分辨率
    const resolution = `${width}x${height}`
    // 实例化ffmpeg
    const ffmpeg = createFFmpeg({
        // ffmpeg路径
        corePath: 'ffmpeg-core.js',
        // 日志
        log: true,
        // 进度
        progress: ({ ratio }) => {
            msg = `完成率: ${(ratio * 100.0).toFixed(1)}%`
        }
    })

    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', 'output.mp4')
    // 开始压缩视频
    await ffmpeg.run('-i', name, '-b', '2000000', '-crf', '18', '-fs', '31457280', '-s', resolution, 'output.mp4')
    // msg = '压缩完成'
    // 压缩所完成,   读文件  压缩后的文件名称为 output.mp4
    const data = ffmpeg.FS('readFile', 'output.mp4')

    // 转换bolb类型
    const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })

    return new Promise((resolve, reject) => {
        const file = new window.File([blob], filename, { type: filetype })
        resolve(file)
    })
}

// 获取上传视频的url
ffmpeg.getObjectURL = function(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
}

// 获取视频的宽高分辨率
ffmpeg.getVideoData = function() {
    return new Promise((resolve, reject) => {
        const videoElement = document.getElementById('video')
        videoElement.addEventListener('loadedmetadata', function () {
            resolve({
                width: this.videoWidth,
                height: this.videoHeight,
                duration: this.duration
            })
        })
    })
}

export default ffmpeg

2.视频上传元素

<template>
    <el-upload
        ref='operationVideoUpload'
        :limit="1"
        list-type='text'
        :class="{disabled:addModelParam.attachments.operationVideo.length>0}"
        :action='actionUrl'
        :on-success="(res,file)=>handleVideoSuccess(res,file,'operationVideo')"
        :before-upload='beforeAvatarUploadVideo'
        :on-remove="(file,fileList)=>handleRemove(file,fileList,'operationVideo')"
        :auto-upload='true'
        :on-exceed="handelFileExceed"
        accept='.mp4,.mov,.wmv,.flv,.mvi,.mkv'>
      <el-button style="position: relative; margin: -5px"><i  class="el-icon-circle-plus-outline" style="color: #66b1ff;">上传附件</i></el-button>
      <br/><br/>
      <p>{{ msg }}</p>
    </el-upload>
    <video id="video" hidden controls object-fill="fill"></video>
  </template>

3.上传压缩脚本

<script>
import ffmpeg from "@/utils/ffmpeg";

export default {
    data() {
        return {
            msg: '',
            videoWidth: '',
            videoHeight: '',
            duration: '',
            actionUrl: ''
        }
    },
    created() {
        this.actionUrl = "你的后端上传文件接口地址URL";
    },
    methods: {
        handleVideoSuccess(res, file, code) {
          this.msg = "已完成视频压缩后上传!";
          file.url = res.data.url;
          file.fileId = res.data.fileId;
          this.addModelParam.attachments[code].push(file.fileId);
        },

        handleAvatarSuccess(res, file, code) {
            file.url = res.data.url;
            file.fileId = res.data.fileId;
            this.addModelParam.attachments[code].push(file.fileId);
        },

        handleRemove(file, fileList, code) {
            this.addModelParam.attachments[code].splice(this.addModelParam.attachments[code].indexOf(file.fileId),1)
        },
        beforeAvatarUploadVideo(file) {
         const isLarge = file.size / 1024 / 1024 > 30;
         if (isLarge) {
           this.msg = "请稍等,过大的视频正在压缩上传中...";
           //压缩视频
           return this.uploadCompressVideo(file);
         }
        },
        handelFileExceed(){
            this.$message('文件数量超出限制!');
        },
        
        // 上传视频文件压缩后再上传
        uploadCompressVideo(file) {
            if (file) {
                let filename = file.name;
                let filetype = file.type;

                const videoUrl = ffmpeg.getObjectURL(file)
                const video = document.getElementById('video')
                video.src = videoUrl;
                return ffmpeg.getVideoData().then((videoObj) => {
                    const {width, height} = videoObj;
                    return ffmpeg.squeezVideo(file, filename, filetype, width, height, this.msg);
                })
            }
        },
},

}
</script>

 注意异步处理:异步压缩,再上传

可使用Promise

4.其他配置:

4.1 vue项目根目录下的vue.config.js里加配置

headers: {
    'Cross-Origin-Opener-Policy': 'same-origin',
    'Cross-Origin-Embedder-Policy': 'require-corp'
}
module.exports = {
    publicPath: './',
    devServer: {
        client: {
            overlay: false,
        },
        port: 9002,
        headers: {
            'Cross-Origin-Opener-Policy': 'same-origin',
            'Cross-Origin-Embedder-Policy': 'require-corp'
        }
    },
    transpileDependencies: []
  }

以免出现如下SharedArrayBuffer的报错:

5.其他实现方案

插件video-conversion.js

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1819786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

飞腾派初体验(2)

水个字数&#xff0c;混个推广分&#xff0c;另外几个点还是想吐槽一下 - 1&#xff0c;上篇文章居然没有给开发板一个硬照&#xff0c;补上 - 飞腾派 自拍 2. 现在做镜像用Win32DiskImager的多吗&#xff1f;我记得当年都是dd命令搞定&#xff0c;玩树莓派的应该记得这个命令…

离散化——Acwing.802区间和

离散化 定义 离散化可以简单理解为将连续的数值或数据转换为离散的、有限个不同的值或类别。离散化就是将一个可能具有无限多个取值或在一个较大范围内连续取值的变量&#xff0c;通过某种规则或方法&#xff0c;划分成若干个离散的区间或类别&#xff0c;并将原始数据映射到…

【课程总结】Day8(下):计算机视觉基础入门

前言 数据结构 在人工智能领域&#xff0c;机器可以处理的数据类型如上图&#xff0c;大约可以分为以上类别。其中较为常用的数据类别有&#xff1a; 表格类数据 数据特点&#xff1a; 成行成列&#xff1a;一行一个样本&#xff0c;一列一个特征特征之间相互独立&#xff0…

最实用的AI软件开发工具CodeFlying测评

就在上个月&#xff0c;OpenAI宣布GPT-4o支持免费试用&#xff0c;调用API价格降到5美元/百万token。 谷歌在得到消息后立马将Gemini 1.5 的价格下降到0.35美元/百万token。 Anthropic的API价格&#xff0c;直接干到了0.25美元/百万token。 国外尚且如此&#xff0c;那么国内…

科技赋能,避震婴儿车或成为行业硬通货

全球知识经济发展发展到今天&#xff0c;消费者对于品质、服务、体验的要求越来越高&#xff0c;与之对应的产品也就越来越科技化、智能化、个性化&#xff0c;品牌化和差异化逐步成为产品的竞争核心。 婴儿推车作为关系婴幼儿出行安全的支柱性产业之一&#xff0c;从车架结构…

分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了

前言 实际上&#xff0c;.NET Core 内部也内置了一套日志系统&#xff0c;它是一个轻量级的日志框架&#xff0c;用于记录应用程序的日志信息。 它提供了 ILogger 接口和 ILoggerProvider 接口&#xff0c;以及一组内置的日志提供程序&#xff08;如 Console、Debug、EventSo…

STM32-17-DAC

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD STM32-15-DMA…

uni-ui:基于uni-app的全端兼容高性能UI框架

一、引言 在移动应用开发领域&#xff0c;跨平台框架因其能够降低开发成本、提高开发效率而备受开发者青睐。其中&#xff0c;uni-app作为一个使用Vue.js开发所有前端应用的框架&#xff0c;不仅支持编译到iOS、Android、H5、以及各种小程序等多个平台&#xff0c;还因其丰富的…

实现开发板三盏灯点亮熄灭

实现开发板三盏灯点亮熄灭 typedef struct {volatile unsigned int MODER; // 0x00volatile unsigned int OTYPER; // 0x04volatile unsigned int OSPEEDR; // 0x08volatile unsigned int PUPDR; // 0x0Cvolatile unsigned int IDR; // 0x10volatile unsigned int OD…

无线网络与物联网技术[1]之近距离无线通信技术

无线网络与物联网技术 近距离无线通信技术WIFIWi-Fi的协议标准Wi-Fi的信道Wi-Fi技术的术语Wi-Fi的组网技术Ad-hoc模式无线接入点-APAP&#xff1a;FAT AP vs FIT AP Wi-Fi的特点与应用Wi-Fi的安全技术 Bluetooth蓝牙技术概论蓝牙的技术协议蓝牙的组网技术微微网piconet(了解)散…

C#聊天室①

聊天室服务器&#xff1a; 创建项目 桌面不需要使用控件 Program.cs internal class Program {static TcpListener server;[STAThread]static void Main(){Program p new Program(); p.start();}void start(){server new TcpListener(IPAddress.Parse(GetIP()), 33…

58.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露(6)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;57.WEB渗透测试-信息收集- 端口、目录扫描、源码泄露&#xff08;5&#xff09;-CSDN博客…

模拟笔试 - 卡码网周赛第二十一期(23年美团笔试真题)

第一题&#xff1a;小美的排列询问 解题思路: 简单题&#xff0c;一次遍历数组&#xff0c;判断 是否有和x、y相等并且相连 即可。 可优化逻辑&#xff1a;因为x和y是后输入的&#xff0c;必须存储整个数组&#xff0c;但是上面说了 **排列是指一个长度为n的数组&#xff0…

学习笔记——网络管理与运维——概述(网络管理)

二、概述 1、什么是网络管理&#xff1f; 网络管理是通过对网络中设备的管理&#xff0c;保证设备工作正常&#xff0c;使通信网络正常地运行&#xff0c;以提供高效、可靠和安全的通信服务&#xff0c;是通信网络生命周期中的重要一环。 2、网络管理分类 网络管理(Network …

C++ 24 之 拷贝构造函数

c24拷贝构造函数.cpp #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;class Person3 { private:int p_age; public:// 构造函数分类&#xff1a;// 按参数分类&#xff1a;1、有参 2、无参// 按类型分类:普通、拷贝&#xff08;复制&…

Charles代理https接口到本地

一、操作手册 1、安装工具 1.1、安装代理软件Charles 软件下载地址&#xff1a;Download a Free Trial of Charles • Charles Web Debugging Proxy 1.2、安装https代理插件&#xff1a;&#xff08;有问题自行百度解决&#xff09; 2、配置策略 以下以https接口为例&…

型号设计师和指挥调度体系: 中国航天系统工程的组织方式

在中国航天&#xff0c;系统工程的组织管理体系是设计师系统和指挥调度系统。 型号设计师系统是型号的技术体系&#xff0c;总设计师是研制任务的技术总负责人&#xff0c;是设计技术方面的组织者、指挥者&#xff0c;重大技术问题的决策者。 指挥调度系统是型号行政管理系统…

C++switch陈述

C 使用关键字 switch、case、default对一个常数执行不同的分流&#xff0c;这构成多重选择的结构&#xff0c;形式如下 简单来说&#xff0c;switch后头接一小括弧&#xff0c;小括弧内为一常数运算式&#xff0c;计算出常数值若与其后case的位标(label) 相符&#xff0c;就会执…

文件没有权限问题:cannot create /opt/apollo/neo/data/log/monitor.log: Permission denied

问题描述 执行 aem bootstrap start --plus 命令启动 Dreamview 提示错误&#xff1a; /bin/sh: 1: cannot create /opt/apollo/neo/data/log/monitor.log: Permission denied [ERROR] Failed to start Dreamview. Please check /opt/apollo/neo/data/log/dreamview.log or /op…

Windows环境RocketMQ启动broker时启动失败

在输入”start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnabletrue“命令启动失败后&#xff0c;会出现如下图所示的样子。 但我们重新启动电脑、重新启动RocketMQ或者杀死进程等方法都对此无效&#xff0c;还是没有启动成功。 启动失败的原因&#xff1a; 中途断网或…