视频播放器的编解码

编码

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

编码格式的概念。 我们常见的视频编码格式有 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...

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax