FFmpeg介绍

1. FFmpeg

mpeg 是领先的多媒体框架,能够解码、编码、 转码、复用、解复用、流式传输、过滤和播放人类和机器创建的几乎任何内容。它支持最晦涩的古代格式直至最前沿。无论它们是由某个标准委员会、社区还是公司设计的。它还具有高度可移植性:FFmpeg 在 Linux、Mac OS X、Microsoft Windows、BSD、Solaris 等各种构建环境、机器架构和配置下编译、运行并通过我们的测试基础设施FATE。

2.jsdelivr

jsDelivr 是一个免费开源的 CDN 解决方案,用于帮助开发者和站长。包含 JavaScript 库、jQuery 插件、CSS 框架、字体等等 Web 上常用的静态资源。

3. 相关网站

#英文官网:
https://ffmpeg.github.net.cn/

#中文翻译:
https://ffmpeg.github.net.cn/

#cdn
https://www.jsdelivr.com/

#必要的js
https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/dist/umd/ffmpeg.js
https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/dist/umd/814.ffmpeg.js

FFmpeg.wasm介绍

1. wasm

WebAssembly(wasm)是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如 C、C++和Rust等低级源语言提供一个高效的编译目标。

2. FFmpeg.wasm

FFmpeg.wasm 是 FFmpeg 的纯 WebAssembly / JavaScript 端实现的 FFmpeg , 将FFmpeg的强大功能直接带入了WebAssembly 和 JavaScript 的世界,让你可以在浏览器中无缝地进行视频和音频的记录、转换和流媒体传输。

FFmpeg命令介绍

# -ss 裁剪视频的开始时间
# -i 指定要处理文件
# -t 指定从开始时间处理多少秒 (需要与-ss同时使用)
# -c:v libx264 - 指定输出的视频编码器 较慢
# -c:a aac - 指定输出的音频编码器
# -c copy 原样输出 比较快 但若需要合成的话可能会出现问题
# -f 指定输入或输出文件的格式
#    mp4 avi mkv wav flv gif 等
#    concat 适用于所有视频文件具有相同的编解码器
# -safe 参数用于控制输入文件列表中的路径安全性
#    0 允许使用不安全的文件路径
#    1 限制只使用安全的文件路径
# -vf 指定文件分辨率 scale=1280:720 设置分辨率
# -an 静音输出
# -strict
#    experimental 允许使用实验性的功能
#    unofficial 允许使用非官方的特性
#    normal 表示使用非实验性的功能
# -shortest 输出文件的长度将等于较短的输入流的长度(即视频或音频中的较短者)
# -preset  用于控制编码速度和压缩效率
#    ultrafast:最快的编码速度,文件体积最大。
#    superfast:比 ultrafast 稍慢,压缩效果稍好。
#    veryfast:快速编码,适合实时录制。
#    faster:在速度和质量之间取得更好的平衡。
#    fast:相对较快的编码速度。
#    medium:默认设置,速度和质量的平衡。
#    slow:编码较慢,但质量更高。
#    veryslow:非常慢的编码,通常能提供最高的质量。
#    placebo:极慢,几乎不推荐使用,效果提升微乎其微。
ffmpeg -options

FFmpeg.wasm使用

// 1. 创建ffmpeg实例

// 使用cdn
// https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/dist/umd/ffmpeg.js
// https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/dist/umd/814.ffmpeg.js
const ffmpeg = new FFmpegWASM.FFmpeg();

// npm安装
// npm install @ffmpeg/ffmpeg @ffmpeg/util
const ffmpeg = new FFmpeg();

// 2. 加载ffmpeg

// ffmpeg.loaded 是否已经加载了ffmpeg
if(!ffmpeg.loaded){
    // 在web worker中加载ffmpeg-core。需要首先调用此方法,因为它包含WebAssembly和其他基本变量。
    await ffmpeg.load();	
}

// 3. 开启日志

ffmpeg.on("log", ({ type, message }) => {
    console.log(type, " =====>>>>> " ,message)
    // const timeRegex = /time=(\d{2}):(\d{2}):(\d{2})/;
    // const match = message.match(timeRegex);
    // if(match != null){
    //     console.log(match[1],match[2],match[3])
    // }
})

// 4. 读取文件

async function readFileData(url){
    const response = await fetch(url);
    if(response.ok){
        const buffer = await response.arrayBuffer();
        // ffmpeg.wasm 处理文件视频的数据类型为 Uint8Array 
        return new Uint8Array(buffer);
    }else{
        return ""
    }
}

// 5. 处理视频
// 假如视频文件地址为: http://domain.com/video1.mp4 - http://domain.com/video2.mp4 - http://domain.com/video3.mp4

// 文件写入到缓存区
const videoUrls = ["http://domain.com/video1.mp4" , "http://domain.com/video2.mp4" , "http://domain.com/video3.mp4"];
for (let index = 0; index < videoUrls.length; index++) {
    let url = URL.createObjectURL(videoUrls[index])
    // 读取文件数据
    const fileData = await readFileData(url);
    console.log(videoUrls[index].name)
    // 将文件写入到缓存区(创建了./video1.mp4 ./video2.mp4 ./video3.mp4)
    await ffmpeg.writeFile(videoUrls[index].name,fileData)
}

// exec 处理视频
// -ss 裁剪视频的开始时间
// -i 输入要处理的视频
// -t 从开始时间处理多少秒 (需要与-ss同时使用)
// -c:v libx264 - 指定输出的视频编码器 较慢
// -c copy 原样输出 比较快 但若需要合成的话可能会出现问题
// -an 静音输出
// -preset  用于控制编码速度和压缩效率
//    ultrafast:最快的编码速度,文件体积最大。
//    superfast:比 ultrafast 稍慢,压缩效果稍好。
//    veryfast:快速编码,适合实时录制。
//    faster:在速度和质量之间取得更好的平衡。
//    fast:相对较快的编码速度。
//    medium:默认设置,速度和质量的平衡。
//    slow:编码较慢,但质量更高。
//    veryslow:非常慢的编码,通常能提供最高的质量。
//    placebo:极慢,几乎不推荐使用,效果提升微乎其微。
for (let index = 0; index < videoUrls.length; index++) {
    let execArr = [
        "-ss", "00:00:03","-i",videoUrls[index].name,"-t","20","-c:v","libx264","-an","-preset","ultrafast",`cache-${videoUrls[index].name}`
    ];
    // 将文件写入到缓存区(创建了./cache-video1.mp4 ./cache-video2.mp4 ./cache-video3.mp4)
    exec.push(ffmpeg.exec(execArr))
}

// 创建一个文件concat.txt
// 文件内容为:
// file cache-video1.mp4
// file cache-video2.mp4
// file cache-video3.mp4
let concat = "";
for (let index = 0; index < videoUrls.length; index++) {
    if(videoUrls[index].type.indexOf("video") >= 0){
        concat += `file cache-${videoUrls[index].name}\n`
    }
}
await ffmpeg.writeFile("concat.txt",concat)

// 合并多个视频
// -f 指定输入或输出文件的格式 mp4 avi mkv wav flv gif 等 concat 适用于所有视频文件具有相同的编解码器
// -safe 参数用于控制输入文件列表中的路径安全性 0 允许使用不安全的文件路径。1 限制只使用安全的文件路径
// -i 输入文件 - 包含要合并的视频文件名称
// -vf 指定文件分辨率 scale=1280:720 设置分辨率
// -c:v libx264 - 同上 - 指定输出的视频编码器 较慢 若指定分辨率则不可用-c copy
// -an 同上 - 静音输出
// -preset 同上 - 用于控制编码速度和压缩效率

// 合并生成一个video.mp4文件
await ffmpeg.exec(["-f","concat","-safe","0", '-i',"concat.txt", "-vf","scale=1280:720","-c:v","libx264","-an","-preset","ultrafast","video.mp4"]);

// 合并多个视频
// -i 输入文件 - 要合并的视频 、音频文件名称
// -c:v libx264 - 同上 - 指定输出的视频编码器
// -c:a aac - 指定输出的音频编码器
// -strict - experimental 允许使用实验性的功能 unofficial 允许使用非官方的特性 normal 表示使用非实验性的功能
// -shortest 输出文件的长度将等于较短的输入流的长度(即视频或音频中的较短者)
await ffmpeg.exec(["-i","video.mp4","-i","mp3","-c:v","copy","-c:a","aac","-strict","experimental" ,"-shortest","output.mp4"]);

// 将最终生成好的文件生成一个url
const data = await ffmpeg.readFile("output.mp4");
var blob = new Blob([data.buffer], { type: 'video/mp4' });
var url = URL.createObjectURL(blob);

// 创建下载链接并下载
const a = document.createElement('a');
a.href = url;
a.download = 'output.mp4';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);