谈起音视频,前端能做些什么

栏目: Html5 · 发布时间: 6年前

内容简介:@(音视频)[Audio|Video|MSE]音视频随着互联网的发展,对音视频的需求越来越多,然而音视频无乱是播放还是编解码,封装对性能要求都比较高,那现阶段的前端再音视频领域都能做些什么呢。[TOC]

@(音视频)[Audio|Video|MSE]

音视频随着互联网的发展,对音视频的需求越来越多,然而音视频无乱是播放还是编解码,封装对性能要求都比较高,那现阶段的前端再音视频领域都能做些什么呢。

[TOC]

音频或视频的播放

html5 audio

提起音视频的播放,我萌首先想到的是HTMLMediaElement, video 播放视频, audio 播放音频。举个栗子:

<audio controls autoplay loop="true" preload="auto" src="audio.mp3"></audio>
复制代码
  • controls 指定浏览器渲染成html5 audio .
  • autoplay 属性告诉浏览器,当加载完的时候,自动播放.
  • loop 属性循环播放.
  • preload 当渲染到audio元素时,便加载音频文件.
  • 移动端的浏览器并不支持 autoplaypreload 属性,即不会自动加载音频文件,只有通过一些事件触发,比如 touchclick 事件等触发加载然后播放.
  • 媒体元素还有一些改变音量,某段音频播放完成事件等,请阅读HTMLMediaElement.
  • 当然如果你的网页是跑在WebView中,可以让客户端设置一些属性实现预加载和自动播放。

AudioContext

虽然使用html5的 audio 可以播放音频,但是正如你看到存在很多问题,同时我萌不能对音频的播放进行很好的控制,比如说从网络中获取到音频二进制数据,有的时候我萌想顺序播放多段音频,对于使用audio元素也是力不从心,处理起来并不优雅。 举个栗子:

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);
	}
}
复制代码

监听 audio 元素的 onended 事件,顺序播放。

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

AudioContext接口表示由音频模块连接而成的音频处理图,每个模块对应一个AudioNode。AudioContext可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建AudioContext对象,因为一切都发生在这个环境之中。

可能理解起来比较晦涩,简单的来说,AudioContext 像是一个工厂,对于一个音频的播放,从音源到声音控制,到链接播放硬件的实现播放,都是由各个模块负责处理,通过connect 实现流程的控制。

谈起音视频,前端能做些什么

现在我萌便能实现音频的播放控制,比如从网络中获取。利用AJAX中获取 arraybuffer类型数据,通过解码,然后把音频的二进制数据传给AudioContext创建的BufferSourceNode,最后通过链接 destination 模块实现音频的播放。

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调用

function xhr() {
    var XHR = new XMLHttpRequest();
   XHR.open('GET', '//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播放对于小段的音频文件还行,但是一大段音频文件来说,等到下载完成才播放,不太现实,能否一边下载一边播放呢。这里就要利用fetch 实现加载 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 实现播放,测试发现移动端不能顺利实现播放,pc端浏览器可以。

PCM audio

实现audioContext播放时,我萌需要解码,利用 decodeAudioData api实现解码,我萌都知道,一般音频都要压缩成mp3,aac这样的编码格式,我萌需要先解码成PCM数据才能播放,那PCM 又是什么呢?我萌都知道,声音都是由物体振动产生,但是这样的声波无法被计算机存储计算,我萌需要使用某种方式去刻画声音,于是乎便有了PCM格式的数据,表示麦克风采集声音的频率,采集的位数以及声道数,立体声还是单声道。

Media Source Extensions

Media Source Extensions 可以动态的给 AudioVideo 创建stream流,实现播放,简单的来说,可以很好的播放进行控制,比如再播放的时候实现 seek 功能什么的,也可以在前端对某种格式进行转换进行播放,并不是支持所有的格式的。

谈起音视频,前端能做些什么

通过将数据append进 SourceBuffer 中,MSE把这些数据存进缓冲区,解码实现播放。这里简单的举个使用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编码格式的视频转包装成mp4格式,然后实现播放。

谈起音视频,前端能做些什么

从流程图中可以看到, IOController 实现对视频流的加载,这里支持 fetch 的 stream能力, WebSocket 等,将得到的视频流,这里指的是flv格式的视频流,将其转封装成MP4格式,最后将MP4格式的数据通过appendBuffer将数据喂给MSE,实现播放。

未来

上面谈到的都是视频的播放,你也看到,即使播放都存在很多限制,MSE的浏览器支持还不多,那在视频的编码解码这些要求性能很高的领域,前端能否做一些事情呢? 前端性能不高有很多原因,在浏览器这样的沙盒环境下,同时js这种动态语言,性能不高,所以有大佬提出把c++编译成js ,然后提高性能,或许你已经知道我要说的是什么了,它就是ASM.js,它是js的一种严格子集。我萌可以考虑将一些视频编码库编译成js去运行提高性能,其中就不得不提到的FFmpeg,可以考虑到将其编译成asm,然后对视频进行编解码。

谈起音视频,前端能做些什么

写在最后

我萌可以看到,前端对音视频的处理上由于诸多原因,可谓如履薄冰,但是在视频播放上,随着浏览器的支持,还是可以有所作为的。

招纳贤士

今日头条长期大量招聘前端工程师,可选北京、深圳、上海、厦门等城市。欢迎投递简历到tcscyl@gmail.com / yanglei.yl@bytedance.com


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python for Data Analysis

Python for Data Analysis

Wes McKinney / O'Reilly Media / 2012-11-1 / USD 39.99

Finding great data analysts is difficult. Despite the explosive growth of data in industries ranging from manufacturing and retail to high technology, finance, and healthcare, learning and accessing d......一起来看看 《Python for Data Analysis》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具