ffmpeg转码视频播放

【实战技巧】前端利用 ffmpeg 播放本地视频及api

ffmpeg.wasm

先来学习一下这个库的基本用法,全部的 ​​API​​ 都在下面了。

  • ​ ​createFFmpeg()​​
  • ​ ​ffmpeg.load()​​
  • ​ ​ffmpeg.run()​​
  • ​ ​ffmpeg.FS()​​
  • ​ ​ffmpeg.exit()​​
  • ​ ​ffmpeg.setLogging()​​
  • ​ ​ffmpeg.setLogger()​​
  • ​ ​ffmpeg.setProgress()​​
  • ​ ​fetchFile()​​

安装

npm install @ffmpeg/ffmpeg @ffmpeg/core -S

在模块中导出这两个方法 ​​createFFmpeg​​​, ​​fetchFile​​。

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

createFFmpeg

​createFFmpeg​​​ 是一个创建 ​​ffmpeg​​ 实例的工厂函数。

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

const ffmpeg = createFFmpeg({})

参数如下

  • corePath: 指定 ​​ffmpeg-core.js​​ 的加载路径。
  • log: 是否打开所有日志,默认为 ​​false​
  • logger: 获取日志消息的函数,例: ​​({ message }) => console.log(message)​
  • progress: 跟踪进度的函数,例: ​​p => console.log(p)​

ffmpeg.load

​ffmpeg.load()​​​ 返回一个 ​​Promise​​​,用来加载 ​​ffmpeg-core.js​​​ 核心包,在浏览器环境中,​​ffmpeg.wasm-core​​​ 脚本默认是从 ​​CDN​​​ 中获取的,可以在创建 ​​ffmpeg​​​ 实例时通过 ​​corePath​​ 来指定到本地路径。

(async () => {
  await ffmpeg.load();
})();

ffmpeg.run

​ffmpeg.run(...args)​​​ 返回一个 ​​Promise​​​,官网说这个方法和原生的 ​​ffmpeg​​ 一样,需要传递的参数也一样,但我原生的也不会啊😂😂,算了,知道用法就行了。

(async () => {
  /* 等价于执行了 `$ ffmpeg -i flame.avi -s 1920x1080 output.mp4` */
  await ffmpeg.run('-i', 'flame.avi', '-s', '1920x1080', 'output.mp4');
})();

​ffmpeg​​ 参数说明:

参数

说明

基本选项:

-formats

输出所有可用格式

-f fmt

指定格式(音频或视频格式)

-i filename

指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头

-y

覆盖已有文件

-t duration

记录时长为t

-fs limit_size

设置文件大小上限

-ss time_off

从指定的时间(s)开始, [-]hh:mm:ss[.xxx]的格式也支持

-itsoffset time_off

设置时间偏移(s),该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset秒。 [-]hh:mm:ss[.xxx]的格式也支持

-title string

标题

-timestamp time

时间戳

-author string

作者

-copyright string

版权信息

-comment string

评论

-album string

album名

-v verbose

与log相关的

-target type

设置目标文件类型("vcd", "svcd", "dvd", "dv", "dv50", "pal-vcd", "ntsc-svcd", ...)

-dframes number

设置要记录的帧数

视频选项:

-b

指定比特率(bits/s),似乎ffmpeg是自动VBR的,指定了就大概是平均比特率

-bitexact

使用标准比特率

-vb

指定视频比特率(bits/s)

-vframes number

设置转换多少桢(frame)的视频

-r rate

帧速率(fps) (可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97)

-s size

指定分辨率 (320x240)

-aspect aspect

设置视频长宽比(4:3, 16:9 or 1.3333, 1.7777)

-croptop size

设置顶部切除尺寸(in pixels)

-cropbottom size

设置底部切除尺寸(in pixels)

-cropleft size

设置左切除尺寸 (in pixels)

-cropright size

设置右切除尺寸 (in pixels)

-padtop size

设置顶部补齐尺寸(in pixels)

-padbottom size

底补齐(in pixels)

-padleft size

左补齐(in pixels)

-padright size

右补齐(in pixels)

-padcolor color

补齐带颜色(000000-FFFFFF)

-vn

取消视频

-vcodec codec

强制使用codec编解码方式('copy' to copy stream)

-sameq

使用同样视频质量作为源(VBR)

-pass n

选择处理遍数(1或者2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率

-passlogfile file

选择两遍的纪录文件名为file

-newvideo

在现在的视频流后面加入新的视频流

高级视频选项:

-pix_fmt format

set pixel format, 'list' as argument shows all the pixel formats supported

-intra

仅适用帧内编码

-qscale q

以<数值>质量为基础的VBR,取值0.01-255,约小质量越好

-loop_input

设置输入流的循环数(目前只对图像有效)

-loop_output

设置输出视频的循环数,比如输出gif时设为0表示无限循环

-g int

设置图像组大小

-cutoff int

设置截止频率

-qmin int

设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31

-qmax int

设定最大质量

-qdiff int

量化标度间最大偏差 (VBR)

-bf int

使用frames B 帧,支持mpeg1,mpeg2,mpeg4

音频选项:

-ab

设置比特率(单位:bit/s,也许老版是kb/s)前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换 默认比特率都较小,要听到较高品质声音的话建议设到160kbps(80)以上。

-aframes number

设置转换多少桢(frame)的音频

-aq quality

设置音频质量 (指定编码)

-ar rate

设置音频采样率 (单位:Hz),PSP只认24000

-ac channels

设置声道数,1就是单声道,2就是立体声,转换单声道的TVrip可以用1(节省一半容量),高品质的DVDrip就可以用2

-an

取消音频

-acodec codec

指定音频编码('copy' to copy stream)

-vol volume

设置录制音量大小(默认为256) <百分比> ,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍

-newaudio

在现在的音频流后面加入新的音频流

字幕选项:

-sn

取消字幕

-scodec codec

设置字幕编码('copy' to copy stream)

-newsubtitle

在当前字幕后新增

-slang code

设置字幕所用的ISO 639编码(3个字母)

Audio/Video 抓取选项:

-vc channel

设置视频捕获通道(只对DV1394)

-tvstd standard

设置电视标准 NTSC PAL(SECAM)

ffmpeg.FS

​ffmpeg.FS(method, ...args)​​​ 用来运行 ​​FS​​ 操作。

对于 ​​ffmpeg.wasm​​​ 的输入/输出文件,需要先将它们保存到 ​​MEMFS​​​ 以便 ​​ffmpeg.wasm​​​ 能够使用它们。这里我们依赖 ​​Emscripten​​​ 提供的 ​​FS​​ 方法♂️。

参数如下

  • method:需要执行的方法名。
  • args:执行方法对应的参数。
/* Write data to MEMFS, need to use Uint8Array for binary data */
// 把文件存入内存中
ffmpeg.FS('writeFile', 'video.avi', new Uint8Array(...));
/* Read data from MEMFS */
// 在内存中读取
ffmpeg.FS('readFile', 'video.mp4');
/* Delete file in MEMFS */
// 在内存中删除
ffmpeg.FS('unlink', 'video.mp4');

ffmpeg.exit

​ffmpeg.exit()​​​ 用来杀死程序的执行,同时删除 ​​MEMFS​​ 以释放内存。

ffmpeg.setLogging

​ffmpeg.setLogging(logging)​​ 控制是否将日志信息输出到控制台。

参数如下

  • logging:在控制台中打开/关闭日志消息。
ffmpeg.setLogging(true);

ffmpeg.setLogger

​ffmpeg.setLogger(logger)​​​ 设置和获取 ​​ffmpeg.wasm​​ 的输出消息。。

参数如下

  • logger:处理消息的函数。
ffmpeg.setLogger(({ type, message }) => {
  console.log(type, message);
  /*
   * type can be one of following:
   *
   * info: internal workflow debug messages
   * fferr: ffmpeg native stderr output
   * ffout: ffmpeg native stdout output
   */

ffmpeg.setProgress

​ffmpeg.setProgress(progress)​​​ 进度处理程序,用于获取 ​​ffmpeg​​ 命令的当前进度。

参数如下

  • progress:处理进度信息的函数。
ffmpeg.setProgress(({ ratio }) => {
  console.log(ratio);
  /*
   * ratio is a float number between 0 to 1. 0 到 1之间的数字
   */

fetchFile

​fetchFile(media)​​​ 返回一个 ​​Promise​​​, 用于从各种资源中获取文件。要处理的视频/音频文件可能位于远程 ​​URL​​​ 或本地文件系统中的某个位置。这个函数帮助你获取文件并返回一个 ​​Uint8Array​​​ 变量供 ​​ffmpeg.wasm​​ 使用。

参数如下

  • media:​URL​​​ 字符串、​​base64​​​ 字符串或​​File​​​、​​Blob​​​、​​Buffer​​ 对象。
(async () => {
  const data = await fetchFile('https://github.com/ffmpegwasm/testdata/raw/master/video-3s.avi');
  /*
   * data will be in Uint8Array format
   */

补充

设置 corePath

​corePath​​​ 支持引入 ​​cdn​

const ffmpeg = createFFmpeg({
  corePath: 'https://unpkg.com/@ffmpeg/core@0.8.5/dist/ffmpeg-core.js',
})

但是业务需要部署在内网,访问不了 ​​cdn​​​,幸好 ​​corePath​​ 也支持加载本地文件。

这里应该是支持绝对路径,默认会去访问 ​​public​​ 下面的文件。

const ffmpeg = createFFmpeg({
  corePath: 'ffmpeg-core.js',
})

把 ​​node_modules\@ffmpeg\core\dist​​​ 下面的三个文件拷贝到 ​​public​​ 中。

设置日志

在创建实例的时候,通过传入 ​​log: true​​,开启日志。

const ffmpeg = createFFmpeg({
  log: true,
})

也可以通过 ​​ffmpeg.setLogger​​ 自定义日志格式,比如

ffmpeg.setLogger(({ type, message }) => {
  console.log('🚀🚀 ~ message', message);
  console.log('🚀🚀 ~ type', type);
});

还可以直接在创建实例的时候传入 ​​logger​​​ 属性,效果是一样的,建议把 ​​log​​​ 属性改为 ​​false​​,不然日志会重复。

const ffmpeg = createFFmpeg({
  corePath: 'ffmpeg-core.js',
  log: false,
  logger: ({ type, message }) => {
    console.log('🚀🚀 ~ message', message);
    console.log('🚀🚀 ~ type', type);
  }
})

获取进度

如何获取上传文件的进度呢,可以通过 ​​ffmpeg.setProgress​

ffmpeg.setProgress(({ ratio }) => {
  console.log('🚀🚀 ~ ratio', ratio);
});

也可以直接在创建实例的时候传入 ​​progress​​ 属性,效果是一样的。

const ffmpeg = createFFmpeg({
  corePath: 'ffmpeg-core.js',
  log: false,
  progress: ({ ratio }) => {
    console.log('🚀🚀 ~ ratio', ratio);
  }
})

解决错误

如果产生下面这个错误

本地开发的时候需要在 ​​vue.config.js​​ 中添加

devServer: {
    headers: {
      "Cross-Origin-Opener-Policy": "same-origin",
      "Cross-Origin-Embedder-Policy": "require-corp",
    },
}

如果是 ​​vite​​​ 的项目这样修改 ​​vite.config.ts​

plugins: [
      vue(),
      vueJsx(),
      {
        name: 'configure-response-headers',
        configureServer: server {
          server.app.use('/node_modules/',(_req, res, next) => {
            res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
            res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
            next();
          });
        }
      }
    ],

部署的时候需要配置 ​​nginx​​ 或者在后端配置。

add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;

本地上传视频

上传文件功能我们用 ​​IView​​ 组件库的上传组件,组件库基本都有上传组件,道理是一样的。

页面组件 ​​main.vue​

<template>
  <div style="width:100%; height:100%; background-color: #DAE4E4;padding:50px">
    <upload-btn @before-upload="beforeUpload"></upload-btn>
    <video-ref :video-file-url="videoFileUrl"></video-ref>
  </div>
</template>

<script>import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg'
import videoRef from '../../components/video/Video.vue'
import UploadBtn from '../../components/video/UploadBtn.vue'

export default {
  data() {
    return {
      ffmpeg: {},
      videoFileUrl: "",
    }
  },
  components: {
    videoRef,
    UploadBtn
  },
  computed: {},
  methods: {
    // 页面初始化逻辑
    async init() {
      await this.initFfmpeg()
    },
    // 初始化 ffmpeg 功能
    async initFfmpeg() {
      this.ffmpeg = createFFmpeg({
        corePath: 'ffmpeg-core.js',
        log: true,
      })
    },
    // 上传文件前的钩子
    async beforeUpload(file) {
      console.log('🚀🚀 beforeUpload ~ file', file);
      await this.ffmpeg.load();
      await this.getVideoFileUrl(file)
    },
    async getVideoFileUrl(file) {
      // 获取资源文件
      const result = await fetchFile(file)
      console.log('🚀🚀 ~ result', result);
      // 对于 ffmpeg.wasm 的输入/输出文件,需要先将它们保存到 MEMFS 以便 ffmpeg.wasm 能够使用它们
      this.ffmpeg.FS('writeFile', `${file.name}`, result);
      await this.ffmpeg.run('-i', `${file.name}`, '-acodec', 'aac', '-vcodec', 'libx264', '-y', `${file.name.split('.')[0]}.mp4`);
      // 在内存中读取文件
      const data = this.ffmpeg.FS('readFile', `${file.name.split('.')[0]}.mp4`);
      // 获取内存中的播放地址
      this.videoFileUrl = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }))
      console.log('🚀🚀 ~  this.videoFileUrl', this.videoFileUrl);
    }
  },
  async mounted() {
    // 页面初始化逻辑
    await this.init()
  }
}
</script>
 

子组件上传按钮 ​​UploadBtn.vue​

<template>
  <div class="upload-btn">
    <i-upload :before-upload="beforeUpload"
               :show-upload-list="false"
               action=""
               name="file"
               accept="video/*">
      <i-button>点击上传视频</i-button>
      <br>
      <br>
    </i-upload>
  </div>
</template>

<script>export default {
  methods: {
    // 上传文件前的钩子
    async beforeUpload(file) {
      this.$emit("before-upload", file)
    },
  }
}
</script>
 

子组件视频播放器 ​​Video.vue​

// 播放视频我们采用 `HTML5` 原生标签 `video`。
<template>
  <div class="video-box">
    <video id="videoRef"
           ref="videoRef"
           v-if="videoFileUrl"
           class="videoRef"
           controls>
      <source :src="videoFileUrl"
              type="video/mp4"
    </video>
  </div>
</template>

<script>export default {
  props: {
    videoFileUrl: {
      default: () => (''),
    },
  },
}
</script>

转载于:https://blog.51cto.com/u_15344825/5646451

使用FFmpeg将视频编码格式转化为H264编码

背景介绍:

    web开发中涉及到视频播放的前端一般会使用h5的video标签对后端提供的视频文件url直接加载进行播放,虽然视频文件后缀都是mp4,但并非所有视频文件的编码格式都支持用video播放,目前支持的格式有以下几种:

MP4 = MPEG4文件使用H264视频编解码器和AAC音频编解码器
WebM = WebM文件使用VP8视频编解码器和Vorbis音频编解码器
Ogg =   Ogg文件使用Theora视频编解码器和Vorbis音频编解码器

当视频文件不支持video标签播放时需要将其格式用编码工具转化为支持的格式。
    本文是通过Java调用FFmpeg命令将MPEG编码格式转化为H264格式,当视频文件比较少时可以采用播放器软件(QQ影音就可以)来转。

Linux下安装FFmpeg

  • 先安装yasm
# 下载yasm
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
# 解压
tar -xzvf yasm-1.3.0.tar.gz
# 配置安装
cd yasm-1.3.0
./configure
make
make install
  • 编译安装H264模块
# 下载x264
git clone https://code.videolan.org/videolan/x264.git
# 切换到x264目录
cd x264
# --prefix=/usr/local/x264 指定安装目录,这里不要指定,否则会出现库找不到的问题
# 执行配置
./configure --enable-shared --enable-static --enable-yasm
# 编译安装
make & make install
  • 安装FFmpeg
# 下载
wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
tar -jxvf ffmpeg-4.2.2.tar.bz2
cd ffmpeg-4.2.2
# 安装
./configure --enable-gpl --enable-libx264
make & make insatll

注:以上安装都没有指定安装路径,即为默认路径,无需为FFmpeg添加系统变量,找不到h264模块时应该做入下配置项

  • 修改系统库的配置
vim vim /etc/ld.so.conf
# 添加以下行
/usr/local/lib
# 保存退出后执行以下命名使lib库生效
ldconfig
  • 测试ffmpeg是否安装成功
ffmpeg -version

显示如下即为安装成功
在这里插入图片描述

Java调用系统FFmpeg命令实现转码

/*** 调用系统命令转码每个文件* @param inputFile 输入视频文件路径* @param outputDir 输出目录路径*/
private static void transferToH264(File inputFile,File outputDir){if (!outputDir.exists()){outputDir.mkdirs();}String outputFile = outputDir.getPath()+"/"+inputFile.getParent();File outDir = new File(outputFile);if(!outDir.exists()){outDir.mkdirs();}System.out.println(outputFile+"/"+inputFile.getName());outputFile = outputFile+"/"+inputFile.getName();String command = "ffmpeg -threads 5 -i " + inputFile.getAbsolutePath() + " -vcodec libx264 -preset ultrafast -r 29 -s 1920x1080 "+outputFile;try {Runtime rt = Runtime.getRuntime();Process proc = rt.exec(command);InputStream stderr = proc.getErrorStream();InputStreamReader isr = new InputStreamReader(stderr);BufferedReader br = new BufferedReader(isr);String line = null;while ( (line = br.readLine()) != null)System.out.println(line);int exitVal = proc.waitFor();System.out.println("Process exitValue: " + exitVal);} catch (Throwable t) {t.printStackTrace();}}

FFmpeg常用命令以及参数

-i 指定要转换视频的源文件
-s 视频转换后视频的分辨率
-vcodec 视频转换时使用的编解码器(-codecs)
-r 视频转换换的桢率(默认25桢每秒)
-b 视频转换换的bit率
-ab 音频转换后的bit率(默认64k)
-acodec 制定音频使用的编码器(-codecs)
-ac 制定转换后音频的声道
-ar 音频转换后的采样率
-an 禁用音频
-vn 禁用视频
-acodec copy 复制音频,不转码
-vcodec copy 复制视频,不转码
# 设置参数转
ffmpeg -i 88885.mp4 -vcodec h264 -r 29 -b:v 43382k 88885_h264.mp4
# 直接转码
# 视频文件转化为h264编码格式,直接转为h264编码格式
ffmpeg -i 88885.mp4 -vcodec h264 88885_h264.mp4
  • FFmpeg转码速率问题
    转码默认参数比较慢,可以指定以下参数来选择合适的速率。
ultrafast:最快的编码方式
superfast:超级快速的编码方式
veryfast:非常快速的编码方式
faster:稍微快速的编码方式
fast:快速的编码方式
medium:折中的编码方式
slow:慢的编码方式
slower:更慢的编码方式
veryslow:非常慢的编码方式
placebo:最慢的编码方式

更多参数请参考这篇文章和官方提供的文档
https://www.jianshu.com/p/49fcae61e58c

转载于:https://betheme.net/news/txtlist_i32420v.html?action=onClick


 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值