Skip to content

Latest commit

 

History

History
283 lines (242 loc) · 10.1 KB

README_CN.md

File metadata and controls

283 lines (242 loc) · 10.1 KB

English | 简体中文

Web-Demuxer

使用WebAssembly在浏览器中对媒体文件进行解封装, 专门为WebCodecs设计

目的

WebCodecs只提供了decode的能力,但没有提供demux的能力。有一些JS解封装mp4box.js很酷,但它只支持mp4,Web-Demuxer的目的是一次性支持更多媒体格式

特征

  • 🪄 专门为WebCodecs设计,API对于WebCodecs开发而言十分友好,可以轻松实现媒体文件的解封装
  • 📦 一次性支持多种媒体格式,比如mov/mp4/mkv/webm/flv/m4v/wmv/avi等等
  • 🧩 支持自定义打包,可以调整配置,打包出指定格式的demuxer

安装

NPM

npm install web-demuxer

CDN

<script type="module">
  import { WebDemuxer } from 'https://cdn.jsdelivr.net/npm/web-demuxer/+esm';
</script>

使用

import { WebDemuxer } from "web-demuxer"

const demuxer = new WebDemuxer({
  // ⚠️ 你需要将npm包中dist/wasm-files文件放到类似public的静态目录下
  // 并确保wasm-files中的js和wasm文件在同一目录下
  wasmLoaderPath: "https://cdn.jsdelivr.net/npm/web-demuxer@latest/dist/wasm-files/ffmpeg.min.js",
})

// 以获取指定时间点的视频帧为例
async function seek(file, time) {
  // 1. 加载视频文件
  await demuxer.load(file);

  // 2. 解封装视频文件并生成WebCodecs所需的VideoDecoderConfig和EncodedVideoChunk
  const videoDecoderConfig = await demuxer.getVideoDecoderConfig();
  const videoChunk = await demuxer.seekEncodedVideoChunk(time);

  // 3. 通过WebCodecs去解码视频帧
  const decoder = new VideoDecoder({
    output: (frame) => {
      // draw frame...
      frame.close();
    },
    error: (e) => {
      console.error('video decoder error:', e);
    }
  });

  decoder.configure(videoDecoderConfig);
  decoder.decode(videoChunk);
  decoder.flush();
}

样例

API

new WebDemuxer(options: WebDemuxerOptions)

创建一个新的WebDemuxer实例

参数:

  • options: 必填, 配置选项
    • wasmLoaderPath: 必填,wasm对应的js loader文件地址(对应npm包中dist/wasm-files/ffmpeg.jsdist/wasm-files/ffmpeg-mini.js

    ⚠️ 你需要确保将wasm 和js loader文件放在同一个可访问目录下,js loader会默认去请求同目录下的wasm文件

load(file: File): Promise<void>

加载文件并等待wasm worker加载完成。需要等待load方法执行成功后,才可以继续调用后续的方法

参数:

  • file: 必填,需要处理的File对象
getVideoDecoderConfig(): Promise<VideoDecoderConfig>

解析视频流,获取文件的VideoDecoderConfig, 返回值可直接作为VideoDecoderconfigure方法的入参

getAudioDecoderConfig(): Promise<AudioDecoderConfig>

解析音频流,获取文件的AudioDecoderConfig, 返回值可直接作为AudioDecoderconfigure方法的入参

seekEncodedVideoChunk(time: number, seekFlag?: AVSeekFlag): Promise<EncodedVideoChunk>

根据传入时间点,获取指定时间点的视频数据(默认取关键帧),返回值可直接作为VideoDecoderdecode方法的入参

参数:

  • time: 必填,单位为s
  • seekFlag: 寻址标志, 默认值为1 (向后寻址). 详情请查看 AVSeekFlag
seekEncodedAudioChunk(time: number, seekFlag?: AVSeekFlag): Promise<EncodedAudioChunk>

根据传入时间点,获取指定时间点的音频数据,返回值可直接作为AudioDecoderdecode方法的入参

参数:

  • time: 必填,单位为s
  • seekFlag: 寻址标志, 默认值为1 (向后寻址). 详情请查看 AVSeekFlag
readAVPacket(start?: number, end?: number, streamType?: AVMediaType, streamIndex?: number, seekFlag?: AVSeekFlag): ReadableStream<WebAVPacket>

返回一个ReadableStream, 用于流式读取packet数据

参数:

  • start: 读取开始时间点,单位为s,默认值为0,从头开始读取packet
  • end: 读取结束时间点,单位为s, 默认值为0,读取到文件末尾
  • streamType: 媒体流类型,默认值为0, 即视频流,1为音频流。其他具体见AVMediaType
  • streamIndex: 媒体流索引,默认值为-1,即自动选择
  • seekFlag: 寻址标志, 默认值为1 (向后寻址). 详情请查看 AVSeekFlag

基于readAVPacket的语义简化方法:

  • readVideoPacket(start?: number, end?: number, seekFlag?: AVSeekFlag): ReadableStream<WebAVPacket>
  • readAudioPacket(start?: number, end?: number, seekFlag?: AVSeekFlag): ReadableStream<WebAVPacket>
getAVStream(streamType?: AVMediaType, streamIndex?: number): Promise<WebAVStream>

获取媒体文件中指定stream的信息

参数:

  • streamType: 媒体流类型,默认值为0, 即视频流,1为音频流。其他具体见AVMediaType
  • streamIndex: 媒体流索引,默认值为-1,即自动选择

基于getAVStream的语义简化方法:

  • getVideoStream(streamIndex?: number): Promise<WebAVStream>
  • getAudioStream(streamIndex?: number): Promise<WebAVStream>
getAVStreams(): Promise<WebAVStream[]>

获取媒体文件中所有的stream

getAVPacket(time: number, streamType?: AVMediaType, streamIndex?: number, seekFlag?: AVSeekFlag): Promise<WebAVPacket>

获取媒体文件中指定时间点的数据

参数:

  • time: 必填,单位为s
  • streamType: 媒体流类型,默认值为0, 即视频流,1为音频流。其他具体见AVMediaType
  • streamIndex: 媒体流索引,默认值为-1,即自动选择
  • seekFlag: 寻址标志, 默认值为1 (向后寻址). 详情请查看 AVSeekFlag

基于getAVPacket的语义简化方法:

  • seekVideoPacket(time: number, seekFlag?: AVSeekFlag): Promise<WebAVPacket>
  • seekAudioPacket(time: number, seekFlag?: AVSeekFlag): Promise<WebAVPacket>
getAVPackets(time: number, seekFlag?: AVSeekFlag): Promise<WebAVPacket[]>

同时获取某个时间点,所有stream上的packet数据, 并按照stream数组顺序返回

参数:

  • time: 必填,单位为s
  • seekFlag: 寻址标志, 默认值为1 (向后寻址). 详情请查看 AVSeekFlag
getMediaInfo(): Promise<WebMediaInfo> // 2.0新增

获取文件的媒体信息, 输出结果参考自ffprobe

{
    "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
    "duration": 263.383946,
    "bit_rate": "6515500",
    "start_time": 0,
    "nb_streams": 2,
    "streams": [
        {
            "id": 1,
            "index": 0,
            "codec_type": 0,
            "codec_type_string": "video",
            "codec_name": "h264",
            "codec_string": "avc1.640032",
            "profile": "High",
            "pix_fmt": "yuv420p",
            "level": 50,
            "width": 1080,
            "height": 2336,
            "channels": 0,
            "sample_rate": 0,
            "sample_fmt": "u8",
            "bit_rate": "6385079",
            "extradata_size": 36,
            "extradata": Uint8Array,
            "r_frame_rate": "30/1",
            "avg_frame_rate": "30/1",
            "sample_aspect_ratio": "N/A",
            "display_aspect_ratio": "N/A",
            "start_time": 0,
            "duration": 263.33333333333337,
            "rotation": 0,
            "nb_frames": "7900",
            "tags": {
                "creation_time": "2023-12-10T15:50:56.000000Z",
                "language": "und",
                "handler_name": "VideoHandler",
                "vendor_id": "[0][0][0][0]"
            }
        },
        {
            "id": 2,
            "index": 1,
            "codec_type": 1,
            "codec_type_string": "audio",
            "codec_name": "aac",
            "codec_string": "mp4a.40.2",
            "profile": "",
            "pix_fmt": "",
            "level": -99,
            "width": 0,
            "height": 0,
            "channels": 2,
            "sample_rate": 44100,
            "sample_fmt": "",
            "bit_rate": "124878",
            "extradata_size": 2,
            "extradata": Uint8Array,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "sample_aspect_ratio": "N/A",
            "display_aspect_ratio": "N/A",
            "start_time": 0,
            "duration": 263.3839455782313,
            "rotation": 0,
            "nb_frames": "11343",
            "tags": {
                "creation_time": "2023-12-10T15:50:56.000000Z",
                "language": "und",
                "handler_name": "SoundHandler",
                "vendor_id": "[0][0][0][0]"
            }
        }
    ]
}
setLogLevel(level: AVLogLevel) // 2.0新增

参数:

  • level: 必填,输出日志等级, 详见AVLogLevel
destroy(): void

销毁实例,释放worker

自定义Demuxer

目前默认提供两个版本的demuxer, 用于支持不同的格式:

  • dist/wasm-files/ffmpeg.js: 完整版(gzip: 996 kB), 体积较大,支持mov,mp4,m4a,3gp,3g2,mj2,avi,flv,matroska,webm,m4v,mpegi,asf
  • dist/wasm-files/ffmpeg-mini.js: 精简版本(gzip: 456 kB),体积小,仅支持mov,mp4,m4a,3gp,3g2,matroska,webm,m4v

如果你想使用体积更小的版本,可以使用1.0版本的web-demuxer,精简版本仅115KB
1.0版本使用C编写,聚焦WebCodecs,体积小,2.0版本使用C++ Embind,提供了更丰富的媒体信息输出,更易维护,体积大

你也可以通过自定义配置,实现指定格式的demxuer:

首先,修改Makefile中的enable-demuxer配置

DEMUX_ARGS = \
	--enable-demuxer=mov,mp4,m4a,3gp,3g2,mj2,avi,flv,matroska,webm,m4v,mpegi,asf

然后先执行npm run dev:docker:arm64(如果是windows, 请执行npm run dev:docker:x86_64),启动docker环境。

最后,执行npm run build:wasm,构建指定格式的demxuer

License

MIT