vue+ffmpeg实现前端视频转码

简介: 如何使用ffmpeg的wasm文件在浏览器实现视频转码

因为做过的项目中,有涉及到视频上传的模块,常规视频的mp4格式可以直接进行预览和展示,但是部分视频文件无法直接预览,需要进行转码成mp4进行处理,我在GitHub上寻找到了ffmpeg的wasm版本,并成功在项目中实现转码。
这里给出一个将asf格式的视频转换mp4的示例
转码.png

这里给出解决思路
首先,我们安装 @ffmpeg/core,@ffmpeg/ffmpeg 两个库,网址如下https://ffmpegwasm.netlify.app/#demo,这里写的简单示例是用element-plus的upload组件+video标签实现
代码如下

  
<script setup lang="ts">
import { UploadFilled } from '@element-plus/icons-vue'
import type { UploadProps } from 'element-plus';
import { fetchFile } from '@ffmpeg/ffmpeg';
import { ComponentInternalInstance, getCurrentInstance, ref } from 'vue';
import { createFFmpeg } from '@ffmpeg/ffmpeg';
const {
  appContext: {
    config: { globalProperties }
  }
} = getCurrentInstance() as ComponentInternalInstance;
const videoPlayURL = ref('')
const videoPreview = ref({} as HTMLVideoElement);

const uploadFileChange: UploadProps['onChange'] = async (uploadFile: any) => {
 
  let ffmpegInstance: any
  if (!globalProperties.$ffmpeg) {
    globalProperties.$ffmpeg = createFFmpeg({
      log: false
    });
  }
  let file: File = uploadFile.raw as File;
  ffmpegInstance = globalProperties.$ffmpeg;
  videoTranscodeFunc(ffmpegInstance, file, 'test').then(() => {
    // 转码进度
    let curProgress: number = 0; // 进度取值不稳定,作为对比
    let startTranscode = false;
    let progressCounter = 0;
    ffmpegInstance.setProgress(({ ratio }: any) => {
      console.log('ratio', ratio);
      // 开始转码时跳转到进度页,只执行一次
      if (!startTranscode) {
        startTranscode = true;

      }
      // ratio is a float number between 0 to 1.
      // ratio可能小于0或者大于1,需要处理
      if (ratio > 0) {
        let progress = parseInt(ratio * 100 + ''); //转为两位数
        if (progress < 100) {
          if (progressCounter === 0 && progress > 90) return; // 规避转码异常
          curProgress = Math.max(curProgress, progress); //取相对更大的值,避免进度倒着走
          curProgress = curProgress < 1 ? 1 : curProgress; // 大文件转码初始进度0.00X,出现长时间为0的情况优化
          progressCounter++;
        }
      }
    });
    // 通过文件格式校验的,在解析时也加一层拦截,检查异常文件,报错中断
    ffmpegInstance.setLogger(({ type, message }: any) => {
      let name = 'test';
      // 异常情况判断
      let invalidMessage = ['Conversion failed!', 'pthread sent an error!'];
      let invalid = invalidMessage.some((item) => message.includes(item));
      if (type === 'fferr' && invalid) {
        console.log("转码异常2")
        return;
      }

      // 转码结束处理
      if (type === 'ffout') {
        try {
          const data = ffmpegInstance.FS('readFile', `${name}.mp4`);
          console.log('转码结束');
          console.timeEnd('videoTranscode');
          let videoUrl = '';
          videoUrl = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
          videoPlayURL.value = videoUrl
          var result = new File([data.buffer], `${name}.mp4`, {
            type: 'video/mp4',
            lastModified: Date.now()
          });
          uploadFile.size = result.size;
          uploadFile.raw = result;
          uploadFile.status = 'ready';

          // 防止ffmpeg返回值异常,只在结束时才赋值100
          ffmpegInstance.FS('unlink', `${name}.mp4`); // 释放内存中的文件
        } catch (err) {
          console.log('转码失败', err);
          return;
        }
      }
    });
  });
};


const videoTranscodeFunc = async (ffmpeg: any, file: any, name: string) => {

  try {
    if (!ffmpeg.isLoaded()) await ffmpeg.load(); // load过的不需要重复load,否则会转码失败
    // console.log('1', name, `${name}.mp4`);
    ffmpeg.FS('writeFile', name, await fetchFile(file));
    // console.log('2', name, `${name}.mp4`);
    ffmpeg.run('-i', name, `${name}.mp4`);
  } catch (err) {
    console.log('ffmpeg run error', err);
  }
};
</script>


<template>
  <el-upload class="upload-demo" :on-change="uploadFileChange" drag :auto-upload="false">
    <el-icon class="el-icon--upload"><upload-filled /></el-icon>
    <div class="el-upload__text">
      Drop file here or <em>click to upload</em>
    </div>

  </el-upload>

  <div class="videoContainer">
    <video :src="videoPlayURL" :controls="true" ref="videoPreview" crossorigin="Anonymous" class="videoDom"></video>
  </div>
</template>



<style scoped>
.videoDom {
  width: 800px;
  height: 400px;
  border: solid 1px red;
}
</style>
  
相关文章
|
1月前
|
JavaScript 前端开发 jenkins
抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
本文探讨了在不依赖Node和VSCode的情况下,仅使用记事本和浏览器开发一个完整的Vue3前端项目的方法。通过CDN引入Vue、Vue Router、Element-UI等库,直接编写HTML文件实现页面功能,展示了前端开发的本质是生成HTML。虽然日常开发离不开现代工具,但掌握这种基础方法有助于快速实现想法或应对特殊环境限制。文章还介绍了如何用Node简单部署HTML文件到服务器,提供了一种高效、轻量的开发思路。
64 10
|
3月前
|
JavaScript 前端开发 搜索推荐
Vue的数据驱动视图与其他前端框架的数据驱动方式有何不同?
总的来说,Vue 的数据驱动视图在诸多方面展现出独特的优势,其与其他前端框架的数据驱动方式的不同之处主要体现在绑定方式、性能表现、触发机制、组件化结合、灵活性、语法表达以及与后端数据交互等方面。这些差异使得 Vue 在前端开发领域具有独特的地位和价值。
118 58
|
3月前
|
编解码 监控 网络协议
如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频
本文详细介绍了如何使用FFmpeg实现RTSP推送H.264和H.265(HEVC)编码视频。内容涵盖环境搭建、编码配置、服务器端与客户端实现等方面,适合视频监控系统和直播平台等应用场景。通过具体命令和示例代码,帮助读者快速上手并实现目标。
876 6
|
3月前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
3月前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略
|
4月前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
4月前
|
前端开发 JavaScript 安全
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
283 4
|
4月前
|
Java 数据安全/隐私保护
Java ffmpeg 实现视频加文字/图片水印功能
【10月更文挑战第22天】在 Java 中使用 FFmpeg 实现视频加文字或图片水印功能,需先安装 FFmpeg 并添加依赖(如 JavaCV)。通过构建 FFmpeg 命令行参数,使用 `drawtext` 滤镜添加文字水印,或使用 `overlay` 滤镜添加图片水印。示例代码展示了如何使用 JavaCV 实现文字水印。
333 1
|
3月前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】 React与Vue:前端框架的巅峰对决与选择策略
|
4月前
|
前端开发 JavaScript API
2025年前端框架是该选vue还是react?有了大模型-例如通义灵码辅助编码,就不用纠结了!vue用的多选react,react用的多选vue
本文比较了Vue和React两大前端框架,从状态管理、数据流、依赖注入、组件管理等方面进行了详细对比。当前版本和下载量数据显示React更为流行,但Vue在国内用户量增长迅速。Vue 3通过组合式API提供了更灵活的状态管理和组件逻辑复用,适合中小型项目;React则更适合大型项目和复杂交互逻辑。文章还给出了选型建议,强调了多框架学习的重要性,认为技术问题已不再是选型的关键,熟悉各框架的最佳实践更为重要。
1204 0

热门文章

最新文章

  • 1
    Ubuntu编译ffmpeg解决错误:ERROR: avisynth/avisynth_c.h not found
  • 2
    【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    详解智能编码在前端研发的创新应用
  • 5
    巧用通义灵码,提升前端研发效率
  • 6
    智能编码在前端研发的创新应用
  • 7
    VSCode AI提效工具,通义灵码前端开发体验
  • 8
    大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
  • 9
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 10
    以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡