這篇文章主要介紹了flv.js的使用方法,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站建設(shè)、做網(wǎng)站與策劃設(shè)計(jì),長沙縣網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:長沙縣等地區(qū)。長沙縣做網(wǎng)站價(jià)格咨詢:18980820575flv.js項(xiàng)目的代碼有一定規(guī)模,如果要研究的話,我建議從demux入手,理解了demux就掌握了媒體數(shù)據(jù)處理的關(guān)鍵步驟,前面的媒體數(shù)據(jù)下載和后面的媒體數(shù)據(jù)播放就變得容易理解了。
先普及點(diǎn)背景知識,為什么HTML5視頻播放要用 flv 格式?
因?yàn)镕lash。我標(biāo)題圖片用的是“flash RIP”,flash快死了,但是它的影響力還在,flash技術(shù)是過去10多年的互聯(lián)網(wǎng)視頻基礎(chǔ)技術(shù),大量相關(guān)基礎(chǔ)設(shè)施都是圍繞Flash構(gòu)建的,比如 CDN 普遍支持的 RTMP 和 flv over http協(xié)議。做互聯(lián)網(wǎng)直播的公司為了能兼容Web上的Flash播放,不約而同地選擇了flv的媒體格式。在從Flash到 HTML5過渡的時(shí)期,如果HTML5能支持flash的協(xié)議是再好不過了,可以平滑過渡,然而HTML5并不原生支持flash協(xié)議。flv.js這個(gè)項(xiàng)目解決了HTML5支持flash協(xié)議的問題,這就是flv.js應(yīng)運(yùn)而生短期爆紅的歷史背景。
flv.js 中的demux就是一套 FLV 媒體數(shù)據(jù)格式的解析器,如果要理解FLV格式,下面的文檔是必須熟讀的。
Adobe官方的flv格式說明
http://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf
flv.js怎么用?下面進(jìn)入正題,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 這幾個(gè)數(shù)字其實(shí)就是 'F' 'L' 'V' 的ascii碼,表示flv文件頭,后面的0x01是flv格式的版本號,用這來檢測數(shù)據(jù)是不是 flv 格式。
let hasAudio = ((data[4] & 4) >>> 2) !== 0; let hasVideo = (data[4] & 1) !== 0;
取出第五個(gè)字節(jié),它的第六 和 第八 bit 分別表示是否存在 音頻和視頻數(shù)據(jù),其它位是保留位可以忽略。
這個(gè)probe是被 parseChunks 調(diào)用的,當(dāng)讀取了至少13個(gè)字節(jié)后,就判斷下是否是一個(gè)flv數(shù)據(jù),然后再繼續(xù)后面的分析。為什么是13,因?yàn)閒lv的文件頭就是13個(gè)字節(jié),參考 上面 PDF里的 “The FLV header”,這13個(gè)字節(jié)包括了后面的一個(gè)四字節(jié)的size,這個(gè)size表示前一個(gè)tag的大小,但是由于第一個(gè)tag是不存在前一個(gè)的,所以第一個(gè)size總是 0。
parseChunks 后面的代碼就是在不斷解析 tag,flv把一段媒體數(shù)據(jù)稱為 TAG,每個(gè)tag有不同的type,實(shí)際上真正用到的只有三種type,8、9、18 分別對應(yīng),音頻、視頻和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,注意看 那個(gè) 數(shù)字 11,因?yàn)閠ag header是11個(gè)字節(jié),后面就是tag body了,所以offset加上這些偏移是為了跳到下一個(gè)tag的位置。
tag header的格式為:UI 表示 unsigned int,后面的是bit數(shù)。
UI8 tag type
UI24 data size
UI24 timestamp
UI8 TimestampExtended
UI24 StreamID
你看是不是正好 11 個(gè)字節(jié),adobe為了節(jié)約流量,能用24bit表示的絕不用32bit,但是還是給timestamp設(shè)置了一個(gè) 擴(kuò)展位存放高位的字節(jié),這個(gè)設(shè)計(jì)很蛋疼,于是導(dǎo)致了下面這段奇葩代碼,先取三個(gè)字節(jié)按照Big-Endian轉(zhuǎn)換成整數(shù)再在高位放上第四個(gè)字節(jié)。
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調(diào)用不同的解析函數(shù)。
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 音頻
音頻結(jié)構(gòu)比較簡單,AUDIODATA的第一個(gè)字節(jié)表示音頻格式,其實(shí)基本都是 ACC 16bit 立體聲 44.1kHz采樣,所以最常見的數(shù)字就是 0xAF,后面一般就是 AACAUDIODATA了
TAG type : 9 視頻
重點(diǎn)看的是視頻,
let frameType = (spec & 240) >>> 4; let codecId = spec & 15;
這里取兩個(gè)重要的值,frameType表示幀類型 1 是關(guān)鍵幀 2 是非關(guān)鍵幀,codeId是編碼類型。雖然flv支持 六種視頻格式,但是實(shí)際上互聯(lián)網(wǎng)點(diǎn)播直播真正在用的基本只有H.264一種。所以codecId基本都是7。這里作者用了十進(jìn)制的數(shù),其實(shí)就是按位取值,用16進(jìn)制的數(shù)會更好理解。
_parseAVCVideoPacket 用來解析 AVCVIDEOPACKET 結(jié)構(gòu),就是H.264的視頻包
let packetType = v.getUint8(0); let cts = v.getUint32(0, !le) & 0x00FFFFFF;
解釋下 CTS的概念,CompositionTime,我們前面在tag header里拿到過一個(gè) timestamp,這個(gè)在視頻里對應(yīng)于DTS,就是解碼時(shí)間戳,而CTS實(shí)際上是一個(gè)offset,表示 PTS相對于DTS的偏移量,就是 PTS和DTS的差值。
這里有個(gè)坑,參考adobe的文檔,這是CTS是個(gè)有符號的24位整數(shù),SI24,就是說它有可能是個(gè)負(fù)數(shù),所以我懷疑flv.js解析cts的代碼有bug,沒有處理負(fù)數(shù)情況。因?yàn)樨?fù)數(shù)的24位整型到32位負(fù)數(shù)轉(zhuǎn)換的時(shí)候要手工處理高位的符號位和補(bǔ)碼問題。(我只是懷疑,沒有調(diào)試確認(rèn)過,但是我在處理YY直播數(shù)據(jù)的時(shí)候是踩過這個(gè)坑的,個(gè)別包含 B frame的視頻是會出現(xiàn)CTS為負(fù)數(shù)的情況的)
packetType有兩種,0 表示 AVCDecoderConfigurationRecord,這個(gè)是H.264的視頻信息頭,包含了 sps 和 pps,AVCDecoderConfigurationRecord的格式不是flv定義的,而是264標(biāo)準(zhǔn)定義的,如果用ffmpeg去解碼,這個(gè)結(jié)構(gòu)可以直接放到 codec的extradata里送給ffmpeg去解釋。
flv.js作者選擇了自己來解析這個(gè)數(shù)據(jù)結(jié)構(gòu),也是迫不得已,因?yàn)镴S環(huán)境下沒有ffmpeg,解析這個(gè)結(jié)構(gòu)主要是為了提取 sps和pps。雖然理論上sps允許有多個(gè),但其實(shí)一般就一個(gè)。
let config = SPSParser.parseSPS(sps);
pps的信息沒什么用,所以作者只實(shí)現(xiàn)了sps的分析器,說明作者下了很大功夫去學(xué)習(xí)264的標(biāo)準(zhǔn),其中的Golomb解碼還是挺復(fù)雜的,能解對不容易,我在PC和手機(jī)平臺都是用ffmpeg去解析的。SPS里面包括了視頻分辨率,幀率,profile level等視頻重要信息。
packetTtype 為 1 表示 NALU,NALU= network abstract layer unit,這是H.264的概念,網(wǎng)絡(luò)抽象層數(shù)據(jù)單元,其實(shí)簡單理解就是一幀視頻數(shù)據(jù)。
NALU的頭有兩種標(biāo)準(zhǔn),一種是用 00 00 00 01四個(gè)字節(jié)開頭這叫 start code,另一個(gè)叫mp4風(fēng)格以Big-endian的四字節(jié)size開頭,flv用了后一種,而我們在H.264的裸流里常見的是前一種。
TAG type : 18 Script Data
除了音視頻數(shù)據(jù)外還有 ScriptData,這是一種類似二進(jìn)制json的對象描述數(shù)據(jù)格式,JavaScript比較慘只能自己寫實(shí)現(xiàn),其它平臺可以用 librtmp的代碼去做。
我覺得作者處理解決flv播放問題外,也為前端貢獻(xiàn)了 amf 解析,sps解析,Golomb解碼等基礎(chǔ)代碼,這些是可以用在其他項(xiàng)目里的。
在用傳輸協(xié)議獲取了flv數(shù)據(jù)流后,用demux分離出音視頻數(shù)據(jù)的屬性和數(shù)據(jù)包,這為后面的播放打下了基礎(chǔ),從demux入手去讀代碼是個(gè)不錯的切入點(diǎn),而且一定要配合 flv file format spec一起看,反復(fù)多看幾遍爭取熟記在心。我現(xiàn)在已經(jīng)可以從wireshark的抓包數(shù)據(jù)里人肉分析flv數(shù)據(jù)包了,對于debug相當(dāng)有幫助。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“flv.js的使用方法”這篇文章對大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!
當(dāng)前文章:flv.js的使用方法-創(chuàng)新互聯(lián)
URL分享:http://jinyejixie.com/article38/jjdsp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)、App開發(fā)、移動網(wǎng)站建設(shè)、ChatGPT、軟件開發(fā)、營銷型網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)