0


【新项目开发】vue3+ts+elementPlus+ffmpegjs开发纯web端的视频编辑器

新项目开发的流程

当在项目中使用新技术时,我们应该首先进行调研,了解其特点和使用方法。在实现功能时,我们可以采用最简单的方式,而不必过于关注项目的设计和结构。一旦掌握了新技术,我们可以根据其API属性进行代码设计,以便更好地开发。以开发一个纯web端的视频编辑处理器为例,我们打算采用vite、vue3、elementPlus、pinia和ffmpegjs等技术。虽然第一次接触ffmpegjs库可能会感到陌生,但我们需要先了解其API属性,并能够实现所需的功能。在此基础上,我们可以考虑如何将ffmpegjs集成到项目中,并设计好项目架构和代码结构,以便更好地使用和维护。

视频编辑器项目操作介绍:

web视频编辑器

1.功能列表:

  1. 项目运行
  • pnpm i安装依赖
  • pnpm dev 项目本地运行
  • pnpm build 项目打包

ffmpegjs是什么

ffmpegjs是一个基于JavaScript的开源库,它提供了一个在浏览器中运行FFmpeg的解决方案。FFmpeg是一个广泛使用的开源跨平台音视频处理工具,可以用于音视频的编解码、转码、剪辑、合并等操作。ffmpegjs库通过将FFmpeg编译为WebAssembly格式,并使用JavaScript封装其API,使得我们可以在浏览器中使用FFmpeg进行音视频处理,而无需安装任何本地软件。ffmpegjs库可以用于开发各种基于Web的音视频应用,如视频编辑器、音视频转换器、在线直播等。

安装ffmpegjs

需要安装 @ffmpeg/ffmpeg @ffmpeg/core 这两个npm包

pnpm install @ffmpeg/ffmpeg @ffmpeg/core

在项目中引入

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

项目核心模块

import{ fetchFile, createFFmpeg }from'@ffmpeg/ffmpeg'import{ processData, tickCounts }from'../store'// @ts-ignore// const { fetchFile, createFFmpeg } = FFmpegconst ffmpeg =createFFmpeg({corePath:'/plugin/ffmpeg-core.js'})
ffmpeg.load()
ffmpeg.setProgress(progress=>{
  processData.value = progress.ratio
})const videoInfo ={duration:'',bitRate:''}
ffmpeg.setLogger(logs=>{if(logs.message.includes('Duration')){
    videoInfo.duration = logs.message.slice(
      logs.message.indexOf('Duration:')+'Duration: '.length,
      logs.message.indexOf(','))
    videoInfo.bitRate = logs.message.slice(
      logs.message.indexOf('bitrate:')+'bitrate: '.length
    )
    console.log(videoInfo)}})let videoName ='initVideo'/**ffmpeg导入视频 */exportconstinitVideo=async(video: Blob)=>{
  ffmpeg.FS('writeFile', videoName,awaitfetchFile(video))await ffmpeg.run('-i', videoName)}let fontName ='font1'/** ffmpeg导入字体 */exportconstwriteFontFile=async(font: Blob)=>{
  ffmpeg.FS('writeFile', fontName,awaitfetchFile(font))}let imageName ='imageMark'/**ffmpeg导入贴图 */exportconstwriteImage=async(image: Blob)=>{
  ffmpeg.FS('writeFile', imageName,awaitfetchFile(image))}let subTitle ='subtitle.srt'/**ffmpeg导入字幕文件 */exportconstwriteSubTitle=async(subtitle: Blob)=>{
  ffmpeg.FS('writeFile', subTitle,awaitfetchFile(subtitle))}/**通过url获取文件blob数据 */exportconst urlGetData =async(fileUrl: string, type ='video/mp4')=>{const tmp ='tmpFile'
  ffmpeg.FS('writeFile', tmp,awaitfetchFile(fileUrl))const outputData = ffmpeg.FS('readFile', tmp)returnnewBlob([outputData.buffer],{ type })}/** 切分视频 */exportconstffmpegSliceVideo=async(fileUrl: string,fileName: string,middleTime: string)=>{
  ffmpeg.FS('writeFile', fileName,awaitfetchFile(fileUrl))// 将视频分割为两个部分const command =`-i ${fileName} -t ${middleTime} -c copy output1.mp4 -ss ${middleTime} -c copy output2.mp4`await ffmpeg.run(...command.split(' '))const video1 = ffmpeg.FS('readFile','output1.mp4')const video1Url =URL.createObjectURL(newBlob([video1.buffer],{type:'video/mp4'}))const video2 = ffmpeg.FS('readFile','output2.mp4')const video2Url =URL.createObjectURL(newBlob([video2.buffer],{type:'video/mp4'}))return[video1Url, video2Url]}/**给视频添加字幕 */exportconstaddSubTitle=async(fileUrl: string,fileName: string)=>{
  ffmpeg.FS('writeFile', fileName,awaitfetchFile(fileUrl))const cmd =`-i ${fileName} -vf subtitles=${subTitle} output.mp4`await ffmpeg.run(...cmd.split(' '))const outputData = ffmpeg.FS('readFile','output.mp4')const outputBlob =newBlob([outputData.buffer],{type:'video/mp4'})returnURL.createObjectURL(outputBlob)}/** 获取视频的每一秒帧 */exportconst gVideoFrame =async(fileUrl: string,timeArr: number[],videoName: string ='initVideo')=>{const frameDir = videoName
  ffmpeg.FS('writeFile', videoName,awaitfetchFile(fileUrl))
  ffmpeg.FS('mkdir', frameDir +'Frame')const second = tickCounts.value / timeArr[timeArr.length -1]let cmd =`-i ${videoName} -vf fps=${second} -q:v 5 -s 320x240 -an -preset fast /${frameDir}Frame/%3d.jpeg -hide_banner`let args = cmd.split(' ')await ffmpeg.run(...args)const fileList = ffmpeg.FS('readdir','/'+ frameDir +'Frame')leturls:{url: string }[]=[]
  fileList.forEach(v=>{if(v !=='.'&& v !=='..'){const path = frameDir +'Frame'+'/'+ v
      const img = ffmpeg.FS('readFile', path)let url =URL.createObjectURL(newBlob([img.buffer],{type:'image/jpeg'}))
      urls.push({
        url
      })}})return urls
}/** 给视频添加文字 */exportconst addText =async(fileUrl: string,videoName: string ='initVideo',text: string ='hello',startT: number =5,endT: number =7)=>{
  ffmpeg.FS('writeFile', videoName,awaitfetchFile(fileUrl))const cmd =`-re -i ${videoName} -vf`const textT =`drawtext=fontfile=font1:text=${text}`+`:fontcolor=white:fontsize=80:x=100:y=10:box=1:boxcolor=#0000007d:enable='between(t,${startT},${endT})'`let args = cmd.split(' ')await ffmpeg.run(...args, textT,'outfile.mp4')const data = ffmpeg.FS('readFile','outfile.mp4')returnURL.createObjectURL(newBlob([data.buffer],{type:'video/mp4'}))}/** 给视频添加贴图 */exportconst addImage =async(fileUrl: string,videoName: string ='initVideo',startT: number =5,endT: number =7)=>{
  ffmpeg.FS('writeFile', videoName,awaitfetchFile(fileUrl))const cmd =`-i ${videoName} -i ${imageName} -filter_complex overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2:enable='between(t,${startT},${endT})' outfile.mp4`let args = cmd.split(' ')await ffmpeg.run(...args,'-hide_banner')const data = ffmpeg.FS('readFile','outfile.mp4')returnURL.createObjectURL(newBlob([data.buffer],{type:'video/mp4'}))}/** 获取视频的第一帧图片 */exportconst getFirstFrame =async(fileUrl: string,fileName: string,
  initTime ='00:00:00.001')=>{
  ffmpeg.FS('writeFile', fileName,awaitfetchFile(fileUrl))
  console.log('视频的第一帧图片')await ffmpeg.run('-hwaccel','auto','-i',
    fileName,'-ss',
    initTime,'-vframes','1','-s','640x480','-an','-threads','4','-preset','fast','output.jpg')const data = ffmpeg.FS('readFile','output.jpg')const url =URL.createObjectURL(newBlob([data.buffer],{type:'image/jpeg'}))return{
    url,videoInfo:JSON.stringify(videoInfo)}}/**导出视频,降帧 */exportconstvideoLower=async(fileUrl: string,fileName: string)=>{
  ffmpeg.FS('writeFile', fileName,awaitfetchFile(fileUrl))const cmd =`-i ${fileName} -b:v 2000k -q:v 2 -r 24 -s 1240x960 output.mp4`let args = cmd.split(' ')await ffmpeg.run(...args)const data = ffmpeg.FS('readFile','output.mp4')const url =URL.createObjectURL(newBlob([data.buffer],{type:'video/mp4'}))return url
}

项目常见的问题:

1、shareArrayBuffer问题,shareArrayBuffer not defined
给服务响应头加入

'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Embedder-Policy': 'require-corp'

这两个字段,引发这个问题主要是因为浏览器厂商因为内存安全问题而对shareArrayBuffer这个api做了限制,只需要在服务器响应头中设置这个字段即可解决。

server:{open:true,host:'0.0.0.0',headers:{'Cross-Origin-Opener-Policy':'same-origin','Cross-Origin-Embedder-Policy':'require-corp'},proxy:{}},

本文转载自: https://blog.csdn.net/qq_42146383/article/details/130341111
版权归原作者 h沐 所有, 如有侵权,请联系我们删除。

“【新项目开发】vue3+ts+elementPlus+ffmpegjs开发纯web端的视频编辑器”的评论:

还没有评论