视频播放器的编解码

编码

原始的媒体文件的数据量都是十分庞大的,而为了便于传输和存储,我们通常会编码来对媒体文件进行压缩,这里就涉及到了

编码格式的概念。 我们常见的视频编码格式有 H.264、H.265、MJPEG等

  • H.264 (AVC) 是当前应用最广泛、兼容性最好的视频编码标准,是"主流"。

    • 块划分: H.264 可以将画面分割成不同大小的块,并为每个块选择最适合的编码方式,提高了效率。
    • 帧内预测: 利用同一帧内相邻块的信息来预测当前块,减少冗余。
    • 帧间预测: 利用过去或未来的帧中的相似块来预测当前块(运动补偿),这是视频压缩的核心技术之一。
  • H.265 (HEVC) 是 H.264 的进化版,在压缩效率上大幅提升,特别适合高分辨率视频,是未来的趋势,相对于H.264来说,在相同图像质量下,可以提供约50%的码率降低。但对硬件要求更高且兼容性不如 H.264。

媒体封装格式

一个视频内除了包含编码压缩后的视频流,音频流,还能够包含章节信息、字幕等数据,把这些数据都包装到一个文件容器内,就是封装的过程。我们常用的封装格式有: .mp4, .mkv, .avi, .mov等。

视频播放器的工作原理

  1. 解封装

识别出不同的媒体流,并将它们分离成独立的、编码后的视频数据包、音频数据包和字幕数据包。

  1. 解码

将经过压缩编码的音视频数据解码成原始的数据

3.音视频同步及渲染

通常,音频作为主时钟。播放器会尝试将视频的播放速度调整到与音频时钟保持一致。

将解码后的图像数据绘制到屏幕上。由 GPU 进行最终的渲染和合成,然后显示到屏幕上。

解码器类型1-WebAssembly(wasm)(软解)

WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。

WebAssembly 可以看作是一种中间表示,它以紧凑的二进制形式存在。

  • 从高级语言到 .wasm 文件的过程,涉及到将高级语义编码成 WebAssembly 指令。
  • .wasm 文件到实际执行的过程,涉及到将二进制指令解码并转换为机器可执行的指令。

WebAssembly的由来

JavaScript代码在引擎中会经历什么?

  • JavaScript文件会被下载下来。
  • 然后进入Parser,Parser主要任务是词法分析 (Lexical Analysis) 和语法分析 (Syntactic Analysis)。会把代码转化成AST(abstract syntax tree 抽象语法树)。词法分析: 将原始的字符流分解成有意义的词法单元 (Tokens) ,例如关键字 (function, var, if)、标识符 (myVariable)、运算符 (+, =)、字面量 ("hello", 123) 等。语法分析: 接收这些 Tokens,并根据 JavaScript 的语法规则,构建一个抽象语法树 (AST) 。AST 是代码结构的树状表示,它反映了代码的逻辑和层级关系。
  • 然后根据抽象语法树AST,Bytecode Compiler字节码编译器会生成引擎能够直接阅读、执行的中间表示,通常为**字节码 (Bytecode) **。
  • 字节码进入翻译器,将字节码一行一行的翻译成效率十分高的Machine Code.
  • 解释器解释执行字节码, 在解释执行过程中,监测"热点"字节码。
  • 当识别出"热点"时,JIT 编译器会将这些字节码编译成机器码。
  • 引擎直接执行优化后的机器码。

性能瓶颈

在业务需求越来越复杂的现在,前端的开发逻辑越来越复杂,相应的代码量随之变的越来越多。相应的,整个项目的起步的时间越来越长。在性能不好的电脑上,启动一个前端的项目甚至要花上十多秒。

但是除了逻辑复杂、代码量大,还有另一个原因是JavaScript这门语言本身的缺陷,JavaScript没有静态变量类型。在JS中,变量类型不是预先声明的,而是可以根据赋值类型随时改变的。例如,let x = 5; x = "hello";

现代JavaScript引擎非常依赖JIT(Just In Time)编译技术。JIT编译器会在代码执行过程中,识别出"热点"代码(经常被执行的代码),然后将其从解释执行转换为高度优化的机器码。

如果有静态类型,JIT编译器在编译代码时就能确切地知道变量和函数参数的类型。例如,如果知道 ab 总是数字,那么 a + b 就可以直接编译成一条高效的整数加法指令。但是,由于JavaScript的动态类型,JIT编译器很难做出这样的保证。

asm.js出现

所以为了解决这个问题,WebAssembly的前身,asm.js诞生了。asm.js强制静态类型

asm.js不能解决所有的问题

无论asm.js对静态类型的问题做的再好,它始终逃不过要经过Parser,要经过ByteCode Compiler,而这两步是JavaScript代码在引擎执行过程当中消耗时间最多的两步。而WebAssembly不用经过这两步。这就是WebAssembly比asm.js更快的原因。

WebAssembly与JavaScript很实际的一个性能对比。几乎稳定的是JavaScript的两倍

什么时候使用WebAssembly?

说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。

  • 对性能有很高要求的App/Module/游戏
  • 在Web中使用C/C++/Rust/Go的库(wasm能将他们转换成目标平台(例如 Windows x86、macOS ARM、Linux x64)特定的机器码)

解码器类型2-MSE(Media Source Extensions)

URL.createObjectURL()

他允许在浏览器中直接处理和展示内存中的二进制数据,而无需将这些数据先上传到服务器或进行复杂的编码转换。

传统做法(没有 createObjectURL 技术时)的问题:

  1. 上传到服务器再下载:

    • 场景: 用户通过 <input type="file"> 选择了一张图片,想在网页上立即预览这张图片。

    • 旧方法: 以前你可能需要将这张图片(通过 AJAX 提交)上传到服务器。服务器接收到图片后,将其保存起来,然后返回一个该图片在服务器上的 URL(例如 http://yourserver.com/images/user_upload_123.jpg)。然后,你才能将这个 URL 设置给 <img> 标签的 src 属性进行显示。

    • 缺点:

      • 网络延迟: 数据需要上传和下载,涉及网络传输,速度慢。
      • 服务器负担: 服务器需要接收、存储、管理这些临时文件。
      • 复杂性: 需要前端和后端都编写代码来处理文件上传和管理。
      • 隐私风险: 用户的数据在上传过程中可能会有隐私泄露的风险。
  2. 复杂的编码转换(例如Base64):

    • 场景: 你想在不上传服务器的情况下显示图片,但又不能直接引用本地文件路径(出于安全考虑,浏览器不允许直接访问本地文件系统)。

    • 一种替代方案: 将二进制图片数据转换为 Base64 编码的字符串。Base64 编码可以将二进制数据表示为纯文本字符。然后,你可以将这个 Base64 字符串直接嵌入到 HTML 元素的 src 属性中,例如 <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />

    • 缺点:

      • 数据膨胀: Base64 编码会使数据体积增大约 33%。对于大文件来说,这会消耗更多内存和网络带宽(即使是本地内存中),加载速度也可能变慢。
      • CPU开销: 进行 Base64 编码和解码需要 CPU 资源。
      • 处理不便: 并非所有二进制数据都能方便地转换为 Base64 并直接使用(例如音频/视频流)。

使用该方法:

js 复制代码
const fileInput = document.getElementById('fileInput');
const imagePreview = document.getElementById('imagePreview');
let objectURL; // 存储生成的URL以便后续释放

fileInput.addEventListener('change', (event) => {
  const file = event.target.files[0];
  if (file) {
    // 如果之前有生成的URL,先释放掉
    if (objectURL) {
      URL.revokeObjectURL(objectURL);
    }

    objectURL = URL.createObjectURL(file);
    imagePreview.src = objectURL;
  }
});

但是要注意,浏览器并不知道你何时不再需要这个通过 createObjectURL 创建的 URL。它会一直将对应的文件数据保留在内存中,直到:

  • 你明确调用 URL.revokeObjectURL() 来释放它。
  • 页面被卸载(浏览器会清除所有与该页面相关的对象 URL)。

Media Source Extensions

虽然浏览器自带的播放器标签已经拥有解封装和解码的功能,我们只需要提供一个MP4或者是WEBM格式的视频地址给标签,标签就能播放这个视频。

但是标签支持的媒体封装格式是十分有限的(W3C标准中只支持MP4),同时它也只能满足一次播放整个曲目的需要,无法实现拆分/合并数个缓冲文件。

MSE 提供了将单个媒体文件的 src 值替换成引用 MediaSource 对象(一个包含即将播放的媒体文件的准备状态等信息的容器),以及引用多个 SourceBuffer 对象(代表多个组成整个串流的不同媒体块)的元素。

MSE 的核心是 MediaSource 对象。 MediaSource 对象代表一个媒体资源,它可以包含多个 SourceBuffer 对象。 SourceBuffer 对象用于存储媒体数据(例如,视频帧、音频帧)。

对于 MSE,你不能直接将 MediaSource 对象传递给 <video><audio> 元素的 src 属性。 你需要使用 URL.createObjectURL() 方法创建URL喂给标签

原始视频/音频 -> 编码器 -> 容器封装器 -> 分段后的容器数据 -> MediaSource → SourceBuffer -> 浏览器媒体播放器。

Webcodecs(硬解)

一、什么是WebCodecs

WebCodecs是浏览器提供的一套音视频编解码的API。相比较于我们自行编写的wasm进行软编软解,Web Codecs可以利用浏览器自带的FFmpeg,其执行效率是高于webassembly的,而且可以充分利用GPU。但其缺点就是编解码的兼容性较差,对于浏览器未支持或者未开放的编解码格式,都无法进行处理。关于WebCodecs的使用其实非常简单,基本上就是配置一个编码器(或者解码器),喂给它视频帧(或者编码块),得到编码块(或者视频帧)。

二、Web编解码器API出现的原因

现在已经有很多 Web API 进行媒体操作:Media Stream API、Media Recording API、Media Source API、WebRTC API,但是没有提供一些底层 API 给到 Web 开发者进行帧操作或者对已经编码的视频进行解封装操作。

很多音视频编辑器为了解决这个问题,使用了 WebAssembly 把音视频编解码带到了浏览器,但是有个问题是现在的浏览器很多已经在底层支持了音视频编解码,并且还进行了很多硬件加速的调优,如果使用 WebAssembly 重新打包这些能力,似乎浪费人力和计算机资源。

而虽然MSE 允许开发者将自定义的媒体数据(如分段编码的视频)注入到播放器中,但最终的解码过程仍然是由浏览器内部处理,开发者无法直接控制使用硬件解码器还是软件解码器,也无法对解码过程进行细粒度干预。对于一些需要低延迟、高效率的实时通信(如视频会议、直播推流)场景,MSE 的这种"黑箱"式解码不够灵活。

所以就诞生了WebCodecs API,暴露媒体 API 来使用浏览器已经有的一些能力,例如:

  • 视频和音频解码
  • 视频和音频编码
  • 原始视频帧
  • 图像解码器

三、WebCodecs的视频编解码处理流程

frames 是视频处理的核心,因此 WebCodecs 大多数类要么消耗 frames 要么生产 frames 。Video encoders 把 frames 转换为 encoded chunks ,Video Decoders 把 encoded chunks 转换为 frames。这一切都在非主线程异步处理,所以可以保证主线程速度。

当前,在 WebCodecs 中,在页面上显示 frame 的唯一方法是将其转换为 ImageBitmap并在 canvas 上绘制位图或将其转换为WebGLTexture。

音视频录制和播放

音视频录制原理

音视频播放原理

参考

juejin.cn/post/734902...

相关推荐
Chaoran5 小时前
浏览器加载外部资源性能优化
前端·javascript·面试
子兮曰5 小时前
别再手动处理API数据了!这个BFF数据转换层让你的开发效率提升300%
前端·javascript·api
知识分享小能手5 小时前
React学习教程,从入门到精通, React 入门指南:创建 React 应用程序的语法知识点(7)
前端·javascript·vue.js·学习·react.js·前端框架·anti-design-vue
golang学习记5 小时前
从0死磕全栈第2天:Vite + React 配置全解析,让你的开发效率飞起来
前端
liusheng5 小时前
fly.js 对 DELETE 请求无法传入 body 的问题
前端·javascript
前端_小_小白5 小时前
前端程序员修炼手册:从像素仔到体验守护者
前端·javascript
子兮曰5 小时前
Flex布局完全指南:20种实战方案带你玩转页面排版
前端·css·flexbox
风舞5 小时前
一文搞定JavaScript Reflect最佳实践
前端·javascript
小高0076 小时前
⚡CSS 原子化:30 行代码让样式复用率飙升 300%
前端·面试