成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

談起音視頻,前端能做些什么

魏明 / 1814人閱讀

摘要:音視頻音視頻隨著互聯(lián)網(wǎng)的發(fā)展,對音視頻的需求越來越多,然而音視頻無亂是播放還是編解碼,封裝對性能要求都比較高,那現(xiàn)階段的前端再音視頻領(lǐng)域都能做些什么呢。

@(音視頻)[Audio|Video|MSE]

音視頻隨著互聯(lián)網(wǎng)的發(fā)展,對音視頻的需求越來越多,然而音視頻無亂是播放還是編解碼,封裝對性能要求都比較高,那現(xiàn)階段的前端再音視頻領(lǐng)域都能做些什么呢。

[TOC]

音頻或視頻的播放 html5 audio

提起音視頻的播放,我萌首先想到的是HTMLMediaElement,video播放視頻,audio播放音頻。舉個栗子:

controls指定瀏覽器渲染成html5 audio.

autoplay屬性告訴瀏覽器,當加載完的時候,自動播放.

loop屬性循環(huán)播放.

preload當渲染到audio元素時,便加載音頻文件.

移動端的瀏覽器并不支持autoplaypreload 屬性,即不會自動加載音頻文件,只有通過一些事件觸發(fā),比如touchclick事件等觸發(fā)加載然后播放.

媒體元素還有一些改變音量,某段音頻播放完成事件等,請閱讀HTMLMediaElement.

當然如果你的網(wǎng)頁是跑在WebView中,可以讓客戶端設置一些屬性實現(xiàn)預加載和自動播放。

AudioContext

雖然使用html5的audio可以播放音頻,但是正如你看到存在很多問題,同時我萌不能對音頻的播放進行很好的控制,比如說從網(wǎng)絡中獲取到音頻二進制數(shù)據(jù),有的時候我萌想順序播放多段音頻,對于使用audio元素也是力不從心,處理起來并不優(yōu)雅。
舉個栗子:

function queuePlayAudio(sounds) {
    let index = 0;
    function recursivePlay(sounds, index) {
        if(sounds.length == index) return;
        sounds[index].play();
        sounds[index].onended = recursivePlay.bind(this, sounds, ++index);
    }
}

監(jiān)聽audio元素的 onended 事件,順序播放。

為了更好的控制音頻播放,我萌需要AudioContext.

AudioContext接口表示由音頻模塊連接而成的音頻處理圖,每個模塊對應一個AudioNode。AudioContext可以控制它所包含的節(jié)點的創(chuàng)建,以及音頻處理、解碼操作的執(zhí)行。做任何事情之前都要先創(chuàng)建AudioContext對象,因為一切都發(fā)生在這個環(huán)境之中。

可能理解起來比較晦澀,簡單的來說,AudioContext 像是一個工廠,對于一個音頻的播放,從音源到聲音控制,到鏈接播放硬件的實現(xiàn)播放,都是由各個模塊負責處理,通過connect 實現(xiàn)流程的控制。

現(xiàn)在我萌便能實現(xiàn)音頻的播放控制,比如從網(wǎng)絡中獲取。利用AJAX中獲取 arraybuffer類型數(shù)據(jù),通過解碼,然后把音頻的二進制數(shù)據(jù)傳給AudioContext創(chuàng)建的BufferSourceNode,最后通過鏈接 destination 模塊實現(xiàn)音頻的播放。

   export default class PlaySoundWithAudioContext {
    constructor() {
            if(PlaySoundWithAudioContext.isSupportAudioContext()) {
                this.duration = 0;
                this.currentTime = 0;
                this.nextTime = 0;
                this.pending = [];
                this.mutex = false;
                this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
            }
        }
        static isSupportAudioContext() {
            return window.AudioContext || window.webkitAudioContext;
        }

       play(buffer) {
            var source = this.audioContext.createBufferSource(); 
            source.buffer = buffer;                  
            source.connect(this.audioContext.destination); 
            source.start(this.nextTime);
            this.nextTime += source.buffer.duration;
        }

    addChunks(buffer) {
        this.pending.push(buffer);
        let customer = () => {
            if(!this.pending.length) return;
            let buffer = this.pending.shift();
            this.audioContext.decodeAudioData(buffer, buffer => {
            this.play(buffer);
            console.log(buffer)
            if(this.pending.length) {
                customer()
            }
            }, (err) => {
                console.log("decode audio data error", err);
            });
        }
        if(!this.mutex) {
            this.mutex = true;
            customer()
        }
       
    }

    clearAll() {
        this.duration = 0;
        this.currentTime = 0;
        this.nextTime = 0;
    }
}

AJAX調(diào)用

function xhr() {
    var XHR = new XMLHttpRequest();
   XHR.open("GET", "http://example.com/audio.mp3");
  XHR.responseType = "arraybuffer";
  XHR.onreadystatechange = function(e) {
      if(XHR.readyState == 4) {
         if(XHR.status == 200) {
       playSoundWithAudioContext.addChunks(XHR.response);
    }
      }
   }
  XHR.send();
}

使用Ajax播放對于小段的音頻文件還行,但是一大段音頻文件來說,等到下載完成才播放,不太現(xiàn)實,能否一邊下載一邊播放呢。這里就要利用 fetch 實現(xiàn)加載stream流。

fetch(url).then((res) => {
    if(res.ok && (res.status >= 200 && res.status <= 299)) {
        readData(res.body.getReader())
    } else {
        that.postMessage({type: constants.LOAD_ERROR})
    }
})

function readData(reader) {
    reader.read().then((result) => {
        if(result.done) {
            return;
        }
        console.log(result);
        playSoundWithAudioContext.addChunks(result.value.buffer);
    })
}

簡單的來說,就是fetchresponse返回一個readableStream接口,通過從中讀取流,不斷的喂給audioContext 實現(xiàn)播放,測試發(fā)現(xiàn)移動端不能順利實現(xiàn)播放,pc端瀏覽器可以。

PCM audio

實現(xiàn)audioContext播放時,我萌需要解碼,利用decodeAudioDataapi實現(xiàn)解碼,我萌都知道,一般音頻都要壓縮成mp3,aac這樣的編碼格式,我萌需要先解碼成PCM數(shù)據(jù)才能播放,那PCM 又是什么呢?我萌都知道,聲音都是由物體振動產(chǎn)生,但是這樣的聲波無法被計算機存儲計算,我萌需要使用某種方式去刻畫聲音,于是乎便有了PCM格式的數(shù)據(jù),表示麥克風采集聲音的頻率,采集的位數(shù)以及聲道數(shù),立體聲還是單聲道。

Media Source Extensions

Media Source Extensions可以動態(tài)的給AudioVideo創(chuàng)建stream流,實現(xiàn)播放,簡單的來說,可以很好的播放進行控制,比如再播放的時候?qū)崿F(xiàn) seek 功能什么的,也可以在前端對某種格式進行轉(zhuǎn)換進行播放,并不是支持所有的格式的。

通過將數(shù)據(jù)append進SourceBuffer中,MSE把這些數(shù)據(jù)存進緩沖區(qū),解碼實現(xiàn)播放。這里簡單的舉個使用MSE播放 audio的栗子:

export default class PlaySoundWithMSE{
    constructor(audio) {
        this.audio = audio;
        if(PlaySoundWithMSE.isSupportMSE()) {
            this.pendingBuffer = [];
            this._mediaSource = new MediaSource();
            this.audio.src = URL.createObjectURL(this._mediaSource);
            this._mediaSource.addEventListener("sourceopen", () => {
                this.sourcebuffer = this._mediaSource.addSourceBuffer("audio/mpeg");
                this.sourcebuffer.addEventListener("updateend", 
                this.handleSourceBufferUpdateEnd.bind(this));
            })
        }
    }

    addBuffer(buffer) {
        this.pendingBuffer.push(buffer);
    }

    handleSourceBufferUpdateEnd() {
        if(this.pendingBuffer.length) {
            this.sourcebuffer.appendBuffer(this.pendingBuffer.shift());
        } else {
            this._mediaSource.endOfStream();
        }
    }

    static isSupportMSE() {
        return !!window.MediaSource;
    }
}
HTML5 播放器

談起html5播放器,你可能知道bilibili的flv.js,它便是依賴Media Source Extensions將flv編碼格式的視頻轉(zhuǎn)包裝成mp4格式,然后實現(xiàn)播放。

從流程圖中可以看到,IOController實現(xiàn)對視頻流的加載,這里支持fetch的 stream能力,WebSocket等,將得到的視頻流,這里指的是flv格式的視頻流,將其轉(zhuǎn)封裝成MP4格式,最后將MP4格式的數(shù)據(jù)通過appendBuffer將數(shù)據(jù)喂給MSE,實現(xiàn)播放。

未來

上面談到的都是視頻的播放,你也看到,即使播放都存在很多限制,MSE的瀏覽器支持還不多,那在視頻的編碼解碼這些要求性能很高的領(lǐng)域,前端能否做一些事情呢?
前端性能不高有很多原因,在瀏覽器這樣的沙盒環(huán)境下,同時js這種動態(tài)語言,性能不高,所以有大佬提出把c++編譯成js ,然后提高性能,或許你已經(jīng)知道我要說的是什么了,它就是ASM.js,它是js的一種嚴格子集。我萌可以考慮將一些視頻編碼庫編譯成js去運行提高性能,其中就不得不提到的FFmpeg,可以考慮到將其編譯成asm,然后對視頻進行編解碼。

寫在最后

我萌可以看到,前端對音視頻的處理上由于諸多原因,可謂如履薄冰,但是在視頻播放上,隨著瀏覽器的支持,還是可以有所作為的。

招納賢士

今日頭條長期大量招聘前端工程師,可選北京、深圳、上海、廈門等城市。歡迎投遞簡歷到 [email protected] / [email protected]

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/108640.html

相關(guān)文章

  • VR大潮來襲 ---前端開發(fā)做些什么

    摘要:通過佩戴的分離式頭顯瀏覽連接在主機端的網(wǎng)頁,現(xiàn)支持的瀏覽器主要是火狐的和設置的谷歌。的概念大概就如此,這次我們將采用的方式來測試我們的場景,現(xiàn)在踏上我們的開發(fā)之旅。 showImg(https://segmentfault.com/img/remote/1460000008904026); WebVR即web + VR的體驗方式,我們可以戴著頭顯享受沉浸式的網(wǎng)頁,新的API標準讓我們可...

    mikyou 評論0 收藏0
  • 云主機能干什么-云主機能做什么?

    摘要:如果你有一臺云主機,你會用來做什么呢擁有一臺云服務器,你可以做很多很多有趣的事情呀,不會代碼不懂開發(fā),不要緊,因為現(xiàn)在網(wǎng)站上有很多零基礎(chǔ)使用教程,良心教學,快速上手比起物理服務器,云服務器不用租場地,也省去了運維電力等基礎(chǔ)消耗,再加上價格便如果你有一臺云主機,你會用來做什么呢?擁有一臺云服務器,你可以做很多很多有趣的事情呀,不會代碼不懂開發(fā),不要緊,因為現(xiàn)在網(wǎng)站上有很多零基礎(chǔ)使用教程,良心教...

    30e8336b8229 評論0 收藏0
  • 2018-某熊的技術(shù)之路: 做些有趣的產(chǎn)品

    摘要:某熊的技術(shù)之路做些有趣的產(chǎn)品年初的時候,我就在想,今年的主題詞是什么上半年考慮的較多的是所謂研發(fā)效能的提升,下半年卻漸漸發(fā)現(xiàn)自己更多的會在想產(chǎn)品這兩個字。 showImg(https://segmentfault.com/img/remote/1460000016874425); 2018-某熊的技術(shù)之路: 做些有趣的產(chǎn)品 年初的時候,我就在想,今年的主題詞是什么;上半年考慮的較多的是...

    30e8336b8229 評論0 收藏0

發(fā)表評論

0條評論

魏明

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<