flv.js怎麼用?全面解讀flv.js程式碼
首先聲明,我不太懂JavaScript,我只是熟悉音視頻處理部分,有錯誤在所難免,歡迎指正。
flv.js專案的程式碼有一定規模,如果要研究的話,我建議從demux入手,理解了demux就掌握了媒體資料處理的關鍵步驟,前面的媒體數據下載和後面的媒體資料播放就變得容易理解了。
先普及點背景知識,為什麼HTML5影片播放要用 flv 格式?
因為Flash。我標題圖片用的是“flash RIP”,flash快死了,但是它的影響力還在,flash技術是過去10多年的互聯網視頻基礎技術,大量相關基礎設施都是圍繞Flash構建的,比如CDN 普遍支援的RTMP 和flv over http協定。做網路直播的公司為了能相容於Web上的Flash播放,不約而同地選擇了flv的媒體格式。在從Flash到 HTML5過渡的時期,如果HTML5能支援flash的協定是再好不過了,可以平滑過渡,然而HTML5並不原生支援flash協定。 flv.js這個計畫解決了HTML5支援flash協議的問題,這就是flv.js應運而生短期爆紅的歷史背景。
flv.js 中的demux就是一套 FLV 媒體資料格式的解析器,如果要理解FLV格式,下面的文件是必須熟讀的。
Adobe官方的flv格式說明
http://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf
# flv. js怎麼用? 下面進入正題,flv.js程式碼解讀:demux部分
開啟程式碼 https://github.com/Bilibili/flv.js/blob/master/src/demux/flv-demuxer.js
static probe(buffer) { let data = new Uint8Array(buffer); let mismatch = {match: false}; if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) { return mismatch; }
0x46 0x4c 0x56 這幾個數字其實就是 'F' 'L' 'V' 的ascii碼,表示flv檔頭,後面的0x01是flv格式的版本號,用這來偵測資料是不是 flv 格式。
let hasAudio = ((data[4] & 4) >>> 2) !== 0; let hasVideo = (data[4] & 1) !== 0;
取出第五個字節,它的第六 和 第八 bit 分別表示是否存在 音訊和視訊數據,其它位是保留位可以忽略。
這個probe是被 parseChunks 調用的,當讀取了至少13個位元組後,就判斷下是否是一個flv數據,然後再繼續後面的分析。為什麼是13,因為flv的文件頭就是13個字節,參考上面PDF裡的“The FLV header”,這13個字節包括了後面的一個四字節的size,這個size表示前一個tag的大小,但是由於第一個tag是不存在前一個的,所以第一個size總是0。
parseChunks 後面的程式碼就是在不斷解析tag,flv把一段媒體資料稱為TAG,每個tag有不同的type,實際上真正用到的只有三種type,8、9、18 分別對應,音訊、視訊和Script Data。
if (tagType !== 8 && tagType !== 9 && tagType !== 18) { Log.w(this.TAG, `Unsupported tag type ${tagType}, skipped`); // consume the whole tag (skip it) offset += 11 + dataSize + 4; continue; }
這段程式碼就在判斷tag type,注意看 那個 數字 11,因為tag header是11個位元組,後面就是tag body了,所以offset加上這些偏移是為了跳到下一個tag的位置。
tag header的格式為:UI 表示 unsigned int,後面的是bit數。
UI8 tag type
UI24 data size
UI24 timestamp
UI8 TimestampExtended
UI24 StreamID
你看是不是正好11 個字節,adobe為了節約流量,能用24bit表示的絕不用32bit,但是還是給timestamp設置了一個擴展位存放最高位的字節,這個設計很蛋疼,於是導致了下面這段奇葩程式碼,先取三個位元組依照Big-Endian轉換成整數再在高位放上第四個位元組。
let ts2 = v.getUint8(4); let ts1 = v.getUint8(5); let ts0 = v.getUint8(6); let ts3 = v.getUint8(7); let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24);
解析完了 tag header後面分別依照不同的 tag type呼叫不同的解析函數。
switch (tagType) { case 8: // Audio this._parseAudioData(chunk, dataOffset, dataSize, timestamp); break; case 9: // Video this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset); break; case 18: // ScriptDataObject this._parseScriptData(chunk, dataOffset, dataSize); break; }
TAG type:8 音訊
音訊結構比較簡單,AUDIODATA的第一個位元組表示音訊格式,其實基本上都是ACC 16bit 立體聲44.1kHz取樣,所以最常見的數字就是0xAF,後面一般就是AACAUDIODATA了
TAG type : 9 影片
重點看的是視頻,
let frameType = (spec & 240) >>> 4; let codecId = spec & 15;
這裡取兩個重要的值,frameType表示幀類型 1 是關鍵幀 2 是非關鍵幀,codeId是編碼類型。雖然flv支援 六種視訊格式,但實際上網路點播直播真正在用的基本只有H.264一種。所以codecId基本上都是7。這裡作者用了十進制的數,其實就是按位取值,用16進制的數會更好理解。
_parseAVCVideoPacket 用來解析 AVCVIDEOPACKET 結構,就是H.264的視訊包
let packetType = v.getUint8(0); let cts = v.getUint32(0, !le) & 0x00FFFFFF;
解釋下CTS的概念,CompositionTime,我們前面在tag header裡拿到過一個timestamp,這個在影片裡對應DTS,就是解碼時間戳,而CTS其實是一個offset,表示PTS相對於DTS的偏移量,就是PTS和DTS的差值。
这里有个坑,参考adobe的文档,这是CTS是个有符号的24位整数,SI24,就是说它有可能是个负数,所以我怀疑flv.js解析cts的代码有bug,没有处理负数情况。因为负数的24位整型到32位负数转换的时候要手工处理高位的符号位和补码问题。(我只是怀疑,没有调试确认过,但是我在处理YY直播数据的时候是踩过这个坑的,个别包含 B frame的视频是会出现CTS为负数的情况的)
packetType有两种,0 表示 AVCDecoderConfigurationRecord,这个是H.264的视频信息头,包含了 sps 和 pps,AVCDecoderConfigurationRecord的格式不是flv定义的,而是264标准定义的,如果用ffmpeg去解码,这个结构可以直接放到 codec的extradata里送给ffmpeg去解释。
flv.js作者选择了自己来解析这个数据结构,也是迫不得已,因为JS环境下没有ffmpeg,解析这个结构主要是为了提取 sps和pps。虽然理论上sps允许有多个,但其实一般就一个。
let config = SPSParser.parseSPS(sps);
pps的信息没什么用,所以作者只实现了sps的分析器,说明作者下了很大功夫去学习264的标准,其中的Golomb解码还是挺复杂的,能解对不容易,我在PC和手机平台都是用ffmpeg去解析的。SPS里面包括了视频分辨率,帧率,profile level等视频重要信息。
packetTtype 为 1 表示 NALU,NALU= network abstract layer unit,这是H.264的概念,网络抽象层数据单元,其实简单理解就是一帧视频数据。
NALU的头有两种标准,一种是用 00 00 00 01四个字节开头这叫 start code,另一个叫mp4风格以Big-endian的四字节size开头,flv用了后一种,而我们在H.264的裸流里常见的是前一种。
TAG type : 18 Script Data
除了音视频数据外还有 ScriptData,这是一种类似二进制json的对象描述数据格式,JavaScript比较惨只能自己写实现,其它平台可以用 librtmp的代码去做。
我觉得作者处理解决flv播放问题外,也为前端贡献了 amf 解析,sps解析,Golomb解码等基础代码,这些是可以用在其他项目里的。
在用传输协议获取了flv数据流后,用demux分离出音视频数据的属性和数据包,这为后面的播放打下了基础,从demux入手去读代码是个不错的切入点,而且一定要配合 flv file format spec一起看,反复多看几遍争取熟记在心。我现在已经可以从wireshark的抓包数据里人肉分析flv数据包了,对于debug相当有帮助。
相关文章:
如何看待B站 (bilibili) 开源 HTML5 播放器内核 flv.js?
以上是flv.js怎麼用?全面解讀flv.js程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Win11系統下如何顯示檔案後綴?詳細解讀在Windows11作業系統中,檔案後綴是指檔案名稱後面的點及其後面的字符,用來表示檔案的類型。在預設情況下,Windows11系統會隱藏檔案的後綴,這樣在檔案總管中只能看到檔案的名稱而無法直觀地了解檔案的類型。然而,對於某些使用者來說,顯示文件後綴是非常必要的,因為它能幫助他們更好地辨識文件類型以及進行相關操

隨著網路的不斷發展,人們越來越離不開瀏覽器。而在瀏覽器中,大家都會或多或少用到cookie這個東西。然而,很多人並不知道cookie資料在哪個資料夾中,今天就來詳細解讀一下。首先,我們要先了解cookie是什麼。簡單來說,cookie是由瀏覽器儲存的一段文字訊息,用於保存使用者在瀏覽器中的一些個人設定或記錄使用者的歷史操作等等。當使用者再次開啟同一個網站時,c

LinuxBashrc是Linux系統中的一個設定文件,用於設定使用者的Bash(BourneAgainShell)環境。 Bashrc檔案儲存了使用者登入時所需的環境變數、啟動腳本等訊息,可以客製化使用者的Shell環境。在Linux系統中,每個使用者都有一個對應的Bashrc文件,位於使用者的家目錄下的隱藏資料夾中。 Bashrc檔案的作用主要有以下幾點:設定環

CryptoGPT是什麼?為什麼說3EX的CryptoGPT是幣圈新入口? 7月5日訊息,3EXAI交易平台正式推出CryptoGPT,這是一個基於AI技術和大數據的創新項目,旨在為全球加密投資者提供全面、智慧的資訊查詢和AI投資建議。 CryptoGPT已收錄CoinMarketCap排名前200的代幣和上百個優質項目方信息,併計劃持續擴展。透過CryptoGPT,用戶可免費取得詳盡的交易諮詢報告和AI投資建議,實現資訊諮詢服務到智慧策略創建及自動執行交易的全端式閉環。目前,該服務已免費開放。有需

Java文件解讀:System類別的exit()方法用法解析,需要具體程式碼範例System類別是Java中的重要類,它提供了許多與系統相關的功能和方法。其中,exit()方法是System類別中常用的方法,用於終止目前正在執行的Java虛擬機器。在本文中,我們將對exit()方法的用法進行解析,並給出具體的程式碼範例。 exit()方法的定義如下:public

鏈上資產代幣化正在成為一個重要的長期趨勢,前景龐大。其中,國債RWA正成為重要的分支。這一板塊在2023年實現了近7倍的成長,在2023年年末經歷短暫回落後,又迅速重回上升通道。本篇BingVentures研究文章將討論國債RWA以及整個RWA版的現況與重要發展趨勢。 RWA生態現狀在當前市場環境中,DeFi收益率相對較低,同時實際利率上升,這促進了代幣化國債等RWA類資產的成長。投資者更傾向於穩定、可預測收益的資產,這一趨勢在金融市場和加密貨幣市場之間尋求平衡的投資者中尤其明顯。代幣化國債等

Java文檔解讀:Short類別的toHexString()方法功能解析在Java程式設計中,我們經常需要進行數值的轉換和處理。 Short類是Java中的一個包裝類,用來處理short類型的資料。其中,Short類別提供了一個toHexString()方法,用於將short類型的資料轉換為十六進位形式的字串。本文將對toHexString()方法的功能進行解析,並

HTTP狀態碼是Web開發中經常遇到的資訊回饋機制。它用來指示HTTP請求的處理結果,不同的狀態碼代表著不同的意義和處理方式。然而,有時我們會遇到一些異常的狀態碼,這時候我們需要對其進行解讀和解決。本文將重點放在一些常見的HTTP狀態碼異常以及應對方法。一、404NotFound404是最常見的狀態碼之一,它表示所要求的資源在伺服器上不存在。這可能是
