公司目前的业务会接触比较多的音视频,所以有必要了解一些基本概念。
  文章涉及的一些源码已上传至[Github](https://github.com/pwstrick/webrtc),可随意下载。
## 一、基础概念
  本节音视频的基础概念摘自书籍《FFmpeg入门详解 音视频原理及应用》。
**1)音频**
  声音的三要素为频率、振幅和波形,即声音的音调、声波的响度和声音的音色。
  音频是一种利用数字化手段对声音进行录制、存放、编辑、压缩和播放的技术,相关概念包括采样、量化、编码、采样率、声道数和比特率等。
  采样是指只在时间轴上对信号进行数字化。
  量化是指在幅度轴上对信号进行数字化。
  每个量化都是一个采样,将这么多采样进行存储就叫做编码。
  声道数是指所支持的能发不同声音的音响个数,常见的有单声道、立体声道等。
  比特率,也叫码率(b/s)指一个数据流中每秒能通过的信息量。
  WebRTC 对音频的噪声抑制和回声消除做了很好的处理。
  音频格式是指要在计算机内播放或处理的音频文件的格式,是对声音文件进行数、模转换的过程,常见的有 MP3、WAV、AAC 等。
  音频信号能压缩的依据包括声音信号中存在大量的冗余度,以及人的听觉具有强音能抑制同时存在的弱音现象。
  压缩编码原理是在压缩掉冗余的信号,冗余信号是指不能被人耳感知到的信息,包括听觉范围之外以及被掩蔽掉的音频信号,压缩编码分为 2 类。
1. 无损压缩:熵编码,包括哈夫曼、算术和行程等编码。
2. 有损压缩:波形、参数、混合等编码,波形编码包括 PCM、DPCM、ADPCM、子带编码、矢量量化等。
**2)视频**
  视频泛指将一系列静态影像以电信号的方式加以捕捉、记录、处理、存储、传送与重现的各种技术。
  帧(Frame)是视频的一个基本概念,表示一副画面,一段视频由许多帧组成。
  视频帧又分为 I 帧、P 帧和 B 帧:
1. I 帧是帧内编码帧,是一个完整都关键帧,无需辅助就能完整显示画面;
2. P 帧是前向预测编码帧,是一个非完整帧,需要参考前面的 I 帧或 P帧生成画面;
3. B 帧是双向预测编码帧,需要参考前后图像帧编码生成。
  帧率(f/s 或 Hz)是单位时间内帧的数量,电视一般 1 秒 24 帧,帧率越高,画面越流畅、逼真。
  码率即比特率(b/s),指单位时间内播放连续媒体(如压缩后的音频或视频)的比特数量,码率越高带宽消耗得就越多。
  视频格式非常多,包括视频文件格式、视频封装格式和视频编码格式等。
  视频文件格式有 MP4、RMVB、MKV、FLV、TS、M3U8 等。FLV 是一种流媒体格式,TS 广泛应用于数字广播系统。
  M3U8 是使用 HLS 协议格式的基础,文件内容是一个播放列表(Playlist),采用 UTF-8 编码,记录了一些列媒体片段资源,顺序播放片段即可完整展示资源,如下所示。
~~~
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/low/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
http://example.com/lo_mid/index.m3u8
#EXTINF:15.169000
94256c7244451f8fd_20221020113637199.ts
#EXT-X-ENDLIST
~~~
  其中 codecs 参数提供解码特定流所需的编解码器的完整信息。之所以使用 ts 格式的片段是为了可以无缝拼接,让视频连续。
  HLS(HTTP Live Steaming,HTTP 直播流协议)的工作原理是把整个流分成一个一个的基于 HTTP 的文件来下载,每次只下载部分。
  视频封装格式也叫容器,可以将已经编码并压缩好的视频轨和音频轨按照一定的格式放到一个文件中。
  视频编码格式能够对数字视频进行压缩或解压缩的程序或设备,也可以指通过特定的压缩技术,将某种视频格式转换成另一种视频格式。
  常见的视频编码格式有几个大系列,包括 MPEG-X、H.26X 和 VPX 等。
  H.264(H.264/MPEG-4 或 AVC)是一种被广泛使用的高精度视频的录制、压缩和发布格式,H.265 是它的继任者。
  一个原始视频,若没有编码,则体积会非常大。假设图的分辨率是 1920\*1080,帧率为 30,每像素占 24b,那没张图占 6.22MB左右,1 秒的视频大小是 186.6MB左右,1 分钟就是 11G了。
  对原始视频进行压缩的目的是去除冗余信息,这些信息包括:
1. 空间冗余,在图像数据中,像素间在行、列方向上都有很大的相关性,相邻像素的值比较接近或者完全相同。
2. 时间冗余,在视频图像序列中,相邻两帧又许多共同的地方,可采用运动补偿算法来去掉冗余。
3. 视觉冗余,相对于人眼的视觉特性而言,人类视觉系统对图像的敏感性是非均匀和非线性的,并不是所有变化都能被观察到。
4. 结构冗余,在图像的纹理区,以及图像的像素值存在明显的分布模式。
5. 知识冗余,对许多图像的理解与某些先验知识有相当大的相关性,这类规律可由先验知识和背景知识得到。
  视频播放器播放本地视频文件或互联网上的流媒体文件大概需要解协议、解封装、解码、音视频同步、渲染等几个步骤,如下图所示。
:-: ![](https://img.kancloud.cn/f8/0d/f80dddf89c893355b247016ab2efa6ce_940x1606.png =400x)
## 二、Web中的音视频
  HTML5 标准推出后,提供了播放视频的 video 元素,以及播放音频的 audio 元素。
  为了能更精准的控制时间、容器格式转换、媒体质量和内存释放等复杂的媒体处理,W3C 推出了[MSE](https://developer.mozilla.org/zh-CN/docs/Web/API/Media_Source_Extensions_API)(Media Source Extensions)媒体源扩展标准。
  若要访问浏览器中已有的编解码器,可以试试[WebCodecs](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API),它可以访问原始视频帧、音频数据块、图像解码器、音频和视频编码器和解码器。
  在浏览器中主流的视频编码格式是 H.264/MPEG-4,不过需要支付专利费。
:-: ![](https://img.kancloud.cn/cb/4c/cb4c2d88469c39a1519736cceab1e6e9_960x520.png =400x)
  而 Google 推出的开源编码格式:VP8,除了 IE 之外,其他浏览器的高版本都能支持。
:-: ![](https://img.kancloud.cn/5e/ea/5eea28beb855e7e9736d10951abfac48_958x642.png =400x)
  最新的 H.265 和 VP9 在浏览器的兼容性上都不理想,有些第三方库会自己写一个 H.265 的解码器脚本,然后来播放视频。
**1)播放器**
  直播使用 video 元素播放视频很多功能都无法满足,因此很多时候都会引入一个播放器,例如[video.js](https://videojs.com/)、[react-player](https://github.com/cookpete/react-player)等。
  这些播放器都能支持多种格式的视频,例如 flv、m3u8、mp4 等;并且有完整的控制键,例如音量、缩放、倍速等,覆盖移动和 PC 两个平台,以及可引入插件等。
  下图是一种播放器的整体架构图,来源于《[Web端H.265播放器研发解密](https://fed.taobao.org/blog/taofed/do71ct/web-player-h265/)》。
:-: ![](https://img.kancloud.cn/38/a3/38a37feea51bcbaa5fc44da00d11f6bc_1617x856.png =800x)
  除了常规的使用 video 元素播放视频之外,还可以用 canvas 播放,具体实现可以参考[JSMpeg](https://github.com/phoboslab/jsmpeg)。
**2)MSE**
  在 MSE 规范中,提供了[MediaSource](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaSource)对象,它可以附着在 HTMLMediaElement 中,即 video 元素的 src 的属性值可以是它。
  一个 MediaSource 包含一个或多个 SourceBuffer 实例(下图来源于[W3C官网](https://w3c.github.io/media-source/pipeline_model_description.html)),SourceBuffer 表示通过 MediaSource 传递到 HTMLMediaElement 并播放的媒体片段。
:-: ![](https://img.kancloud.cn/4a/75/4a75b1bd3383a1b0a2cff1a47a966970_1301x1032.png =600x)
  下面是一个使用 MSE 的完整示例,修改了 MDN 中的代码首先是声明视频路径和 MIME 参数,注意,要正确指定 codecs 参数,否则视频无法播放。
~~~
const video = document.getElementById('video');
const assetURL = 'demo.mp4';
const mime = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
~~~
  然后实例化 MediaSource 类,并将其与 video 元素关联,注册 sourceopen 事件。
~~~
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen);
~~~
  最后实现 sourceOpen 函数,通过 fetch() 请求视频资源,将读取到的 ArrayBuffer 数据附加到 sourceBuffer 中。
~~~
function sourceOpen(e) {
URL.revokeObjectURL(video.src);
const mediaSource = e.target;
// 创建指定 MIME 类型的 SourceBuffer 并添加到 MediaSource 的 SourceBuffers 列表
const sourceBuffer = mediaSource.addSourceBuffer(mime);
// 请求资源
fetch(assetURL)
.then(function(response) {
return response.arrayBuffer(); // 转换成 ArrayBuffer
})
.then(function(buf) {
sourceBuffer.addEventListener('updateend', function() {
if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
mediaSource.endOfStream(); // 视频流传输完成后关闭流
video.play();
}
});
sourceBuffer.appendBuffer(buf); // 添加已转换成 ArrayBuffer 的视频流数据
});
}
~~~
  为 sourceBuffer 注册 updateend 事件,并在视频流传输完成后关闭流。
  注意,要想看到视频的播放,不能直接静态 HTML 文件,需要将文件附加到 HTTP 服务器中。
  本文借助 Node.js,搭建了一个极简的 HTTP 服务器,当然也可以将 HTML 文件挂载到 Nginx 或 IIS 服务器中。
~~~
const http = require('http');
const fs = require('fs');
// HTTP服务器
const server = http.createServer((req, res) => {
// 实例化 URL 类
const url = new URL(req.url, 'http://localhost:1000');
const { pathname } = url;
// 路由
if(pathname === '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(fs.readFileSync('./index.html'));
}else if(pathname === '/demo.mp4') {
res.writeHead(200, { 'Content-Type': 'video/mp4' });
res.end(fs.readFileSync('./demo.mp4'));
}else if(pathname === '/client.js') {
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(fs.readFileSync('./client.js'));
}
});
server.listen(1000);
~~~
  B站的[flv.js](https://github.com/bilibili/flv.js)播放器是依赖 MSE,可自动解析 flv 格式的文件并在 video 元素中播放,完全抛弃了 Flash。
  顺便说一句,flv 格式的数据传输一般采用 RTMP(Real Time Messaging Protocol)直播协议,这是由 Adobe 公司提出的私有协议,工作在 TCP 协议之上。
参考资料:
[视频和音频内容](https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content)
[网页视频编码指南](https://developer.mozilla.org/zh-CN/docs/Web/Media/Formats/Video_codecs)
[Support for ISOBMFF-based MIME types in Browsers](https://cconcolato.github.io/media-mime-support/)
[WebRTC应用该使用哪种音视频编解码器](https://www.agora.io/cn/community/blog/21703)
[三种视频流浏览器播放解决方案](https://juejin.cn/post/6844903953126129671)
[Web端H.265播放器研发解密](https://fed.taobao.org/blog/taofed/do71ct/web-player-h265/)
[从 Chrome 源码 video 实现到 Web H265 Player](https://developer.aliyun.com/article/782832)
[Web音视频串流](https://jackym06.github.io/2021/04/21/Web%E9%9F%B3%E8%A7%86%E9%A2%91%E4%B8%B2%E6%B5%81/)
[WebCodecs对音视频进行编码解码](https://chenng.cn/posts/WebCodecs%E5%AF%B9%E9%9F%B3%E8%A7%86%E9%A2%91%E8%BF%9B%E8%A1%8C%E7%BC%96%E7%A0%81%E8%A7%A3%E7%A0%81/)
[Media Source Extensions](https://web.dev/media-mse-basics/)
[快速播放音频和视频预加载](https://web.dev/fast-playback-with-preload/)
[Web视频播放原理:介绍](https://paopaolee.github.io/web/LP20190706A/)
[流式播放器的实现原理](https://www.jianshu.com/p/af4a36a8b5ec)
[「1.4 万字」玩转前端 Video 播放器](https://xie.infoq.cn/article/8d2b0ba59ea03dd458a902ef0)
*****
> 原文出处:
[博客园-HTML躬行记](https://www.cnblogs.com/strick/category/1770829.html)
[知乎专栏-HTML躬行记](https://zhuanlan.zhihu.com/c_1250826149041238016)
已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎阅读。
![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、扩展运算符和剩余参数
- 3、解构
- 4、模板字面量
- 5、对象字面量的扩展
- 6、Symbol
- 7、代码模块化
- 8、数字
- 9、字符串
- 10、正则表达式
- 11、对象
- 12、数组
- 13、类型化数组
- 14、函数
- 15、箭头函数和尾调用优化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、类
- 21、类的继承
- 22、Promise
- 23、Promise的静态方法和应用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基础实践
- 3、WebRTC视频通话
- 4、Web音视频基础
- CSS进阶
- 1、CSS基础拾遗
- 2、伪类和伪元素
- 3、CSS属性拾遗
- 4、浮动形状
- 5、渐变
- 6、滤镜
- 7、合成
- 8、裁剪和遮罩
- 9、网格布局
- 10、CSS方法论
- 11、管理后台响应式改造
- React
- 1、函数式编程
- 2、JSX
- 3、组件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表单
- 8、样式
- 9、组件通信
- 10、高阶组件
- 11、Redux基础
- 12、Redux中间件
- 13、React Router
- 14、测试框架
- 15、React Hooks
- 16、React源码分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基础
- 4、webpack进阶
- 5、Git
- 6、Fiddler
- 7、自制脚手架
- 8、VSCode插件研发
- 9、WebView中的页面调试方法
- Vue.js
- 1、数据绑定
- 2、指令
- 3、样式和表单
- 4、组件
- 5、组件通信
- 6、内容分发
- 7、渲染函数和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、数据类型
- 2、接口
- 3、类
- 4、泛型
- 5、类型兼容性
- 6、高级类型
- 7、命名空间
- 8、装饰器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系统和网络
- 3、命令行工具
- 4、自建前端监控系统
- 5、定时任务的调试
- 6、自制短链系统
- 7、定时任务的进化史
- 8、通用接口
- 9、微前端实践
- 10、接口日志查询
- 11、E2E测试
- 12、BFF
- 13、MySQL归档
- 14、压力测试
- 15、活动规则引擎
- 16、活动配置化
- 17、UmiJS版本升级
- 18、半吊子的可视化搭建系统
- 19、KOA源码分析(上)
- 20、KOA源码分析(下)
- 21、花10分钟入门Node.js
- 22、Node环境升级日志
- 23、Worker threads
- 24、低代码
- 25、Web自动化测试
- 26、接口拦截和页面回放实验
- 27、接口管理
- 28、Cypress自动化测试实践
- 29、基于Electron的开播助手
- Node.js精进
- 1、模块化
- 2、异步编程
- 3、流
- 4、事件触发器
- 5、HTTP
- 6、文件
- 7、日志
- 8、错误处理
- 9、性能监控(上)
- 10、性能监控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 监控系统
- 1、SDK
- 2、存储和分析
- 3、性能监控
- 4、内存泄漏
- 5、小程序
- 6、较长的白屏时间
- 7、页面奔溃
- 8、shin-monitor源码分析
- 前端性能精进
- 1、优化方法论之测量
- 2、优化方法论之分析
- 3、浏览器之图像
- 4、浏览器之呈现
- 5、浏览器之JavaScript
- 6、网络
- 7、构建
- 前端体验优化
- 1、概述
- 2、基建
- 3、后端
- 4、数据
- 5、后台
- Web优化
- 1、CSS优化
- 2、JavaScript优化
- 3、图像和网络
- 4、用户体验和工具
- 5、网站优化
- 6、优化闭环实践
- 数据结构与算法
- 1、链表
- 2、栈、队列、散列表和位运算
- 3、二叉树
- 4、二分查找
- 5、回溯算法
- 6、贪心算法
- 7、分治算法
- 8、动态规划
- 程序员之路
- 大学
- 2011年
- 2012年
- 2013年
- 2014年
- 项目反思
- 前端基础学习分享
- 2015年
- 再一次项目反思
- 然并卵
- PC网站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端学习之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 日志
- 2020