本文由字節(jié)跳動張華挺分享,原題“你不知道的前端音視頻知識”,下文有修訂和重新排版。
1、前言
本文回顧了Web端音視頻的發(fā)展歷程,同時還介紹了視頻的編碼、幀率、比特率等概念,提到了Canvas作為視頻播放的替代方案,以及FFmpeg在音視頻處理中的重要作用等知識。

- 移動端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點此)
2、遠古時期的HTML
Web端音視頻的發(fā)展史得從刀耕火種的年代——早期 HTML說起。
在早期的 HTML,由于帶寬、技術(shù)等各種因素限制,網(wǎng)頁主要以簡單的靜態(tài)內(nèi)容為主,只支持一些文字圖片內(nèi)容和簡單的排版,不支持在線觀看音視頻。
圖為 1994 年的 Yahoo!:

3、 Flash的興起與淘汰
20 世紀初,隨著互聯(lián)網(wǎng)的發(fā)展,各種 Web 應(yīng)用和門戶網(wǎng)站不斷出現(xiàn),人們渴望在網(wǎng)頁上看到更加豐富多彩的內(nèi)容,比如視頻、動畫等等,于是 Flash 進入了人們的視野。
彼時的 Flash 沒有像現(xiàn)在大家印象中的那么臃腫,剛誕生的 Flash 小巧、高效、跨平臺,同時憑借幾十 K 的體積做出放大也不會失真的各種矢量彩色動畫,在還是撥號上網(wǎng),帶寬條件受限,加載一個在線視頻需要好幾分鐘的年代脫穎而出,甚至可以做出各種令人沉迷的 Flash 小游戲。
Flash 塑造了很多經(jīng)典的小游戲角色,火柴人就是其中之一:

Flash 的興起,得益于當時 HTML 對于媒體文件支持的匱乏。Flash 以插件的形式,干著平臺才需要負擔的繁重工作,并得益于 Adobe 的大力推廣,F(xiàn)lash 先后增加了對 Javascrip、HTML、XML 的支持,并增強了影音方面的功能。同時由于 Flash 跨平臺的特性,非常容易被移植,市面上稍微高端點的設(shè)備,也得乖乖地給 Adobe 交授權(quán)費。
然而 2007 年推出的 iPhone 并不買賬,他們以增加續(xù)航、安全為由拋棄了 Flash,很多人一開始對此嗤之以鼻,但事實證明蘋果對此確實有遠見,大量低質(zhì)量的 Flash 使當時續(xù)航本就有限的移動設(shè)備更加不堪重負。2012 年,Android 也宣布不再支持 Flash,F(xiàn)lash 在移動市場不再有立足之地。
在桌面市場上,F(xiàn)lash 的日子也并不好過。Chrome 從的 Chrome 42 開始,就已經(jīng)強制把 Flash 裝入沙箱,以 PPAPI 的形式運行;而從 Chromium 版本號 88 開始,已經(jīng)徹底不再支持 Flash 技術(shù)了。微軟的 Edge 瀏覽器也同步不支持 Flash。Chrome 的前輩 Firefox 更加激進,從 2016 年就已經(jīng)默認禁止 Flash 運行了。
至于 Flash 為什么走向了淘汰,除了它的效率變低,不安全因素過多,穩(wěn)定性不足外,還有一個重要原因:Web 音視頻解決方案有了更好的替代品—— HTML5。
4、HTML5的到來
其實,對于 HTML5 是否可以真正替代 Flash,尤大在 2011 年已經(jīng)給出了預(yù)言:

事實正如預(yù)言所預(yù)料,HTML5 在 2008 年發(fā)布后,經(jīng)過不斷改進完善,基本上能包辦 Flash 所有能干的事情了。HTML5 引入了許多新特性和新功能,其中就包含了 video 和 audio 標簽,也就是對音視頻的支持。使用了支持 HTML5 標準的網(wǎng)絡(luò)瀏覽器訪問 HTML5 站點,用戶無需在電腦上安裝 Flash 插件就可以在線觀看視頻,擺脫了對 Flash 的依賴。

2021 年 1 月 20 日,chrome 88 正式發(fā)布,徹底的禁止使用 Flash。自此,F(xiàn)lash 算是徹底退出了歷史舞臺。
5、到底什么是視頻
視頻,其實就是一系列連續(xù)播放的圖片,如果一秒鐘播放 24 張圖片,那么人眼看到的就不再是一張張獨立的圖片,而是動起來的畫面。
其中一張圖片稱為一幀,1s 播放的圖片數(shù)稱為幀率。由于人類眼睛的特殊生理結(jié)構(gòu),如果所看畫面之幀率高于每秒約 10-12 幀的時候,就會認為是連貫的;當看到幀率為 24 fps 以上時,大腦會認為這是流暢播放的視頻。所以一般有聲電影的拍攝及播放幀率大約為每秒 24 幀,歐美、日本那邊由于電視制式不同,大約為 30 幀。
關(guān)于視頻及視頻編碼相關(guān)的入門文章可以繼續(xù)詳讀以下資料:
- 《零基礎(chǔ),史上最通俗視頻編碼技術(shù)入門》
- 《一文讀懂視頻的顏色模型轉(zhuǎn)換和色域轉(zhuǎn)換》
- 《愛奇藝技術(shù)分享:輕松詼諧,講解視頻編解碼技術(shù)的過去、現(xiàn)在和將來》
6、電影的幀率與游戲的幀率
為什么 24 幀的電影比 30 幀的游戲要流暢許多?這其中的原因就在于,電影和游戲的圖像生成原理不同。
電影的 24 fps,是每 1/24 秒拍攝一副畫面,如果你玩過相機的手動設(shè)置,你應(yīng)該知道如果以 1/24 秒的快門速度拍攝一個運動的物體會“糊”掉,而正是這樣“糊”掉的畫面連起來才讓我們的眼睛看上去很“流暢”。
而游戲畫面不是按 1/24 秒快門拍出來的,而是每一幅畫面都是獨立渲染出來的,之所以跑成 24fps 是因為顯卡處理能力不夠而“丟棄”了其中的一些畫面,這樣一來每兩幅畫面之間就不連續(xù)了,自然看上去會“卡”。
舉個例子:一個圓從左上角移動到右下角,如果是電影,第一幀與第二幀可能是類似下圖這樣的。

如果是游戲畫面,第一幀與第二幀會類似下面這兩張圖:

此外,幀與幀之間間隔恒定:人眼對于動態(tài)視頻的捕捉是非常敏感的,電影幀率是固定不變,肉眼很難察覺出異常。
而游戲的幀率卻是很容易變化的——如果手動鎖定幀數(shù),顯卡會默認渲染最高幀率。
玩家觸發(fā)的很多劇情往往伴隨劇烈的畫面變動,這時顯卡的幀率就會出現(xiàn)下降,前后不一致的幀率很容易被肉眼捕捉,這時我們就會覺得,游戲變“卡”了。
7、視頻的編碼
7.1 概述
視頻是由圖片構(gòu)成的,圖片是由像素構(gòu)成的,假設(shè)尺寸為 1980*1080。每個像素由 RGB 構(gòu)成,每個 8 位,共 24 位。
假設(shè)幀率是 24,那么每秒鐘的視頻的尺寸如下:

一分鐘視頻的尺寸就是 9237888000 Bytes 已經(jīng)是 8.8 個 G 了??梢钥吹剑绻遣粚σ曨l做任何處理,是非常不方便對于視頻做傳輸與存儲的,所以需要對視頻進行壓縮,也就是編碼。
7.2 視頻編碼
視頻圖像數(shù)據(jù)有很強的相關(guān)性,也就是說有大量的冗余信息。其中冗余信息可分為空域冗余信息和時域冗余信息。壓縮技術(shù)就是將數(shù)據(jù)中的冗余信息去掉(去除數(shù)據(jù)之間的相關(guān)性),壓縮技術(shù)包含幀內(nèi)圖像數(shù)據(jù)壓縮技術(shù)、幀間圖像數(shù)據(jù)壓縮技術(shù)和熵編碼壓縮技術(shù)。
經(jīng)過編碼之后,視頻由一幀幀的圖片,變成了一串串讓人看不懂的二進制代碼,因為編碼的方式(算法)的不同,所以就有了編碼格式的區(qū)分。常見的編碼格式有 H.264,MPEG-4,VP8 等。
我們前端開發(fā)只需要記住一點,主流瀏覽器支持的視頻編碼格式是 H.264。
7.3 音頻編碼
CD 音質(zhì)的音頻,存放一分鐘數(shù)據(jù)需要的大小為 10M,太大了,也需要壓縮(編碼)。
常見的編碼方式有:WAV、MP3 和 AAC 格式。
音頻的編碼方式不像視頻那樣那么多,而且音頻在各個瀏覽器基本上都可以播放。具體的每種編碼格式包含的音頻是怎么構(gòu)成的,這里就不講了。

關(guān)于音頻及音頻編碼相關(guān)的入門文章可以繼續(xù)詳讀以下資料:
7.4 封裝格式
我們把視頻數(shù)據(jù)、音頻數(shù)據(jù)打包到一起,然后再添加一些基本信息,例如分辨率、時長、標題等,構(gòu)成一個文件,這個文件稱為封裝格式。常見的封裝格式有 MP4、AVI、RMVB 等。

可以看出:視頻的封裝格式和視頻的編碼格式往往是無關(guān)的。一個 mp4 文件里面的視頻流編碼可以是 h264也可以是 mpeg-4。所以就會出現(xiàn),同樣都是 mp4 文件,有的瀏覽器可以放,有的瀏覽器就放不了的問題,因為能不能放是由視頻碼流的編碼格式?jīng)Q定的。
8、視頻的碼率
碼率,也叫比特率,幀率是 1s 播放多少幀,類比一下,比特率就是 1s 的視頻有多少 bit。這個參數(shù)直接決定了視頻的大小與清晰程度。
一般網(wǎng)上流傳的電影 MKV(BDrip-1080P)的碼率是 10Mb/s 左右,藍光原盤是 20Mb/s 左右,這兩者都是 H.264 編碼的。另外一些 MV、PV、演示片什么的除了 H.264 編碼,可能還有 MPEG-2 編碼,碼率大小不等,像 youtube 那些在線的 1080P 的視頻,碼率可能只有 5Mb/s,而一些 MV 的碼率可以高到離譜,可以達到 110Mb/s 的,3 分多鐘的 MV 差不多有 3GB 大小。
而一般的視頻剪輯、后期軟件,在輸出序列的時候,都會有碼率這個選項。

9、視頻播放器的原理
播放視頻的基本流程是:解協(xié)議 → 解封裝 → 解碼 → 視音頻同步。如果播放本地文件則不需要解協(xié)議。

解協(xié)議的作用,就是將流媒體協(xié)議的數(shù)據(jù),解析為標準的相應(yīng)的封裝格式數(shù)據(jù)。視音頻在網(wǎng)絡(luò)上傳播的時候,常常采用各種流媒體協(xié)議,例如 HTTP、RTMP或是 MMS 等等。這些協(xié)議在傳輸視音頻數(shù)據(jù)的同時,也會傳輸一些信令數(shù)據(jù)。這些信令數(shù)據(jù)包括對播放的控制(播放、暫停、停止),或者對網(wǎng)絡(luò)狀態(tài)的描述等。解協(xié)議的過程中會去除掉信令數(shù)據(jù)而只保留視音頻數(shù)據(jù)。
解封裝的作用,就是將輸入的封裝格式的數(shù)據(jù),分離成為音頻流壓縮編碼數(shù)據(jù)和視頻流壓縮編碼數(shù)據(jù)。封裝格式種類很多,例如 MP4、MKV、RMVB、TS、FLV、AVI 等等,它的作用就是將已經(jīng)壓縮編碼的視頻數(shù)據(jù)和音頻數(shù)據(jù)按照一定的格式放到一起。例如,F(xiàn)LV 格式的數(shù)據(jù),經(jīng)過解封裝操作后,輸出 H.264 編碼的視頻碼流和 AAC 編碼的音頻碼流。
解碼的作用,就是將視頻/音頻壓縮編碼數(shù)據(jù),解碼成為非壓縮的視頻/音頻原始數(shù)據(jù)。音頻的壓縮編碼標準包含 AAC、MP3、AC-3 等等,視頻的壓縮編碼標準則包含 H.264、MPEG2、VC-1 等等。解碼是整個系統(tǒng)中最重要也是最復(fù)雜的一個環(huán)節(jié)。通過解碼,壓縮編碼的視頻數(shù)據(jù)輸出成為非壓縮的顏色數(shù)據(jù),例如 YUV420P、RGB 等等;壓縮編碼的音頻數(shù)據(jù)輸出成為非壓縮的音頻抽樣數(shù)據(jù),例如 PCM 數(shù)據(jù)。
視音頻同步的作用,就是根據(jù)解封裝模塊處理過程中獲取到的參數(shù)信息,同步解碼出來的視頻和音頻數(shù)據(jù),并將視頻音頻數(shù)據(jù)送至系統(tǒng)的顯卡和聲卡播放出來。
10、HTML5的canvas播放視頻
如果我們碰到一些特殊機型或者特殊情況 HTML5 的 video 解決方案不是很好處理,也可以采用 Canvas 去播放這個視頻。
使用 Canvas 播放視頻主要是利用 ctx.drawImage(video, x, y, width, height) 來對視頻當前幀的圖像進行繪制,其中 video 參數(shù)就是頁面中的 video 對象。所以如果我們按照特定的頻率不斷獲取 video 當前畫面,并渲染到 Canvas 畫布上,就可以實現(xiàn)使用 Canvas 播放視頻的功能。
<video id="video" controls="controls" style="display: none;">
<source src="https://xxx.com/vid_159411468092581" />
</video>
<canvas id="myCanvas" width="460" height="270" style="border: 1px solid blue;" ></canvas>
<div>
<button id="playBtn">播放</button>
<button id="pauseBtn">暫停</button>
</div>
const video = document.querySelector("#video");
const canvas = document.querySelector("#myCanvas");
const playBtn = document.querySelector("#playBtn");
const pauseBtn = document.querySelector("#pauseBtn");
const context = canvas.getContext("2d");
let timerId = null;
function draw() {
if (video.paused || video.ended) return;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(video, 0, 0, canvas.width, canvas.height);
timerId = setTimeout(draw, 0);
}
playBtn.addEventListener("click", () => {
if (!video.paused) return;
video.play();
draw();
});
pauseBtn.addEventListener("click", () => {
if (video.paused) return;
video.pause();
clearTimeout(timerId);
});
事實上,市面上已經(jīng)有不少 Canvas 播放視頻的解決方案,比較出名的是這個 JSMpeg。它和 PIXI 一樣,可以選擇 WebGL 渲染視頻也可以直接用 Canvas 渲染視頻。
JSMpeg 是沒有 npm 包的,但是社區(qū)上有開發(fā)者基于 JSMpeg 封裝了一個 npm 包:https://github.com/cycjimmy/jsmpeg-player。
在官網(wǎng)上是這么介紹的:
JSMpeg is a Video Player written in JavaScript. It consists of an MPEG-TS Demuxer, WebAssembly MPEG1 Video & MP2 Audio Decoders, WebGL & Canvas2D Renderers and WebAudio Sound Output. JSMpeg can load static files via Ajax and allows low latency streaming (~50ms) via WebSocktes.
由于它所支持的編碼格式不是常規(guī)的 H.264,而是比較老的 MPEG1,并且解封裝器為 MPEG-TS。所以一般我們使用它去渲染視頻的格式為 TS。TS 是日本高清攝像機拍攝下進行的封裝格式,全稱為 MPEG2-TS。它的特點就是要求從視頻流的任一片段開始都是可以獨立解碼的。
TS 文件通常作為多個文件保存在 DVD 上,雖然它可以在高清攝像機、藍光 DVD 中無需借助其他軟件就能直接打開,但是 TS 視頻文件與大多數(shù)的媒體播放器、便攜式播放器或視頻編輯工具都不兼容,所以這個時候,F(xiàn)Fmpeg 就可以出場了。
11、視頻操作神器——FFmpeg
FFmpeg是一個開源的軟件,我們直接用 homebrew 就可以安裝:
1brew install ffmpeg
如果我們想轉(zhuǎn)換為 jsmpeg 所需的 ts 格式視頻,可以執(zhí)行:
$ ffmpeg -i input.mp4 -f mpegts \
-codec:v mpeg1video -s 640x360 -b:v 1500k -r 25 -bf 0 \
-codec:a mp2 -ar 44100 -ac 1 -b:a 64k \
output.ts
- 1)i:指定輸入文件,這里指定為 input.mp4;
- 2)f 指明輸出文件的封裝格式,這里為 jsmpeg 所需的 mpegts;
- 3)codec:v 指明輸出文件的視頻編碼,這里指明為 jsmpeg 所需的 mpeg1video;
- 4)s 設(shè)置視頻分辨率,參數(shù)格式為w*h或w×h;
- 5)b:v 設(shè)置視頻碼率,一般如果想得到高清的效果,至少需要 4000k 以上,如果對視頻體積有要求,可以視情況小一點;
- 6)r 設(shè)置幀率(fps),一般都為 25;
- 7)bf bframe 數(shù)目控制,一般為 0。
B 幀法(B frame)是雙向預(yù)測的幀間壓縮算法。當把一幀壓縮成 B 幀時,它根據(jù)相鄰的前一幀、本幀以及后一幀數(shù)據(jù)的不同點來壓縮本幀,也即僅記錄本幀與前后幀的差值。
- 1)codec:a 指明輸出文件的音頻編碼;
- 2)ar 設(shè)置音頻編碼采樣率,單位kHz,一般網(wǎng)上的音頻,大多為 44100;
音頻采樣率是指錄音設(shè)備在單位時間內(nèi)對模擬信號采樣的多少,采樣頻率越高,機械波的波形就越真實越自然;
- 3)ac 設(shè)置音頻編碼聲道數(shù);
- 4)b:a 設(shè)置音頻碼率;
音頻碼率,指一個音頻流中每秒鐘能通過的數(shù)據(jù)量,碼率越大的話,音質(zhì)越好。
- 最后一個參數(shù)即為輸出文件位置與名稱和后綴格式。
FFmpeg 是一個非常強大的音視頻轉(zhuǎn)換工具,不僅可以視頻轉(zhuǎn)換,還可以視頻尺寸裁剪、視頻時長裁剪、視頻拼接等等功能,目前很多在線視頻剪輯工具基本是基于 FFmpeg 開發(fā)的。
12、音視頻的一些資源推薦
國內(nèi)學(xué)習(xí)音視頻相關(guān)的開發(fā),繞不過的一個大神是雷霄驊,大佬已經(jīng)去世了,但是留下的文章永垂不朽。本文也是參考了雷霄驊的部分博客,如果感興趣,可以從這篇文章看起:《視音頻編解碼技術(shù)零基礎(chǔ)學(xué)習(xí)方法》。
對于直播 webrtc 感興趣的,也可以看一下 Real time communication with WebRTC,國內(nèi)慕課網(wǎng)上李超老師也有不錯的教程。
對 ffmpeg 感興趣的,可以看一下這里:https://github.com/leandromoreira/ffmpeg-libav-tutorial。
13、參考資料
[1] 即時通訊音視頻開發(fā)(十八):詳解音頻編解碼的原理、演進和應(yīng)用選型
[2] 即時通訊音視頻開發(fā)(十九):零基礎(chǔ),史上最通俗視頻編碼技術(shù)入門
[3] 即時通訊音視頻開發(fā)(二十):一文讀懂視頻的顏色模型轉(zhuǎn)換和色域轉(zhuǎn)換
[4] 實時語音聊天中的音頻處理與編碼壓縮技術(shù)簡述
[5] 網(wǎng)易視頻云技術(shù)分享:音頻處理與壓縮技術(shù)快速入門
[6] 福利貼:最全實時音視頻開發(fā)要用到的開源工程匯總
[9] 愛奇藝技術(shù)分享:輕松詼諧,講解視頻編解碼技術(shù)的過去、現(xiàn)在和將來
[10] 零基礎(chǔ)入門:實時音視頻技術(shù)基礎(chǔ)知識全面盤點
[11] 實時音視頻面視必備:快速掌握11個視頻技術(shù)相關(guān)的基礎(chǔ)概念
[12] 實時音視頻開發(fā)理論必備:如何省流量?視頻高度壓縮背后的預(yù)測技術(shù)
[13] 視頻直播技術(shù)干貨(十三):B站實時視頻直播技術(shù)實踐和音視頻知識入門
[14] 零基礎(chǔ)入門:基于開源WebRTC,從0到1實現(xiàn)實時音視頻聊天功能
[15] 實時音視頻入門學(xué)習(xí):開源工程WebRTC的技術(shù)原理和使用淺析
[16] 零基礎(chǔ)快速入門WebRTC:基本概念、關(guān)鍵技術(shù)、與WebSocket的區(qū)別等
posted @ 2025-06-26 15:25 Jack Jiang 閱讀(24) | 評論 (0) | 編輯 收藏