web audio living
总结网页音频直播的方案和遇到的问题。
代码:(github,待整理)
结果: 使用opus音频编码,web audio api 播放,可以达到100ms以内延时,高质量,低流量的音频直播。
背景: VDI(虚拟桌面) h264网页版预研,继h264视频直播方案解决之后的又一个对延时有高要求的音频直播方案(交互性,音视频同步)。
前提: flexVDI开源项目对音频的支持只实现了对未编码压缩的PCM音频数据。并且效果不好,要么卡顿,要么延时,流量在2~3Mbps(根据缓冲的大小)。
解决方案: 在spice server端对音频采用opus进行编码,flexVDI playback通道拿到opus packet数据后,调用opus js解码库解码成PCM数据,喂给audioContext进行播放。
流程简介:flexVDI palyback通道接收opus音频数据,调用libopus.js解码得到PCM数据,保存到buffer。创建scriptProcessorNode, 在onaudioprocess函数中从buffer里面拿到PCM数据,
按声道填充outputBuffer, 把scriptProcessorNode连接到audioContext.destination进行播放。具体代码见后文或者github。
opus编解码接口介绍:
参考:http://opus-codec.org/docs/opus_api-1.2/index.html
一、下面是我用opus c库解码opus音频,再用ffplay播放PCM数据的一个demo,可以看看opus解码接口是怎么使用的:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "opus.h" /*
static void int_to_char(opus_uint32 i, unsigned char ch[4])
{
ch[0] = i>>24;
ch[1] = (i>>16)&0xFF;
ch[2] = (i>>8)&0xFF;
ch[3] = i&0xFF;
}*/ static opus_uint32 char_to_int(unsigned char ch[])
{
return ((opus_uint32)ch[]<<) | ((opus_uint32)ch[]<<)
| ((opus_uint32)ch[]<< ) | (opus_uint32)ch[];
} int main(int argc, char** argv)
{
opus_int32 sampleRate = ;
int channels = , err = , len = ;
int max_payload_bytes = ;
int max_frame_size = *;
OpusDecoder* dec = NULL;
sampleRate = (opus_int32)atol(argv[]);
channels = atoi(argv[]);
FILE* fin = fopen(argv[], "rb");
FILE* fout = fopen(argv[], "wb+"); short *out;
unsigned char* fbytes, *data;
//in = (short*)malloc(max_frame_size*channels*sizeof(short));
out = (short*)malloc(max_frame_size*channels*sizeof(short));
/* We need to allocate for 16-bit PCM data, but we store it as unsigned char. */
fbytes = (unsigned char*)malloc(max_frame_size*channels*sizeof(short));
data = (unsigned char*)calloc(max_payload_bytes, sizeof(unsigned char));
dec = opus_decoder_create(sampleRate, channels, &err);
int nBytesRead = ;
opus_uint64 tot_out = ;
while(){
unsigned char ch[] = {};
nBytesRead = fread(ch, , , fin);
if(nBytesRead != )
break;
len = char_to_int(ch);
nBytesRead = fread(data, , len, fin);
if(nBytesRead != len)
break; opus_int32 output_samples = max_frame_size;
output_samples = opus_decode(dec, data, len, out, output_samples, );
int i;
for(i=; i < output_samples*channels; i++)
{
short s;
s=out[i];
fbytes[*i]=s&0xFF;
fbytes[*i+]=(s>>)&0xFF;
}
if (fwrite(fbytes, sizeof(short)*channels, output_samples, fout) != (unsigned)output_samples){
fprintf(stderr, "Error writing.\n");
return EXIT_FAILURE;
}
tot_out += output_samples;
} printf("tot_out: %llu \n", tot_out); return ;
}
这个程序对opus packets组成的文件(简单的length+packet格式)解码后得到PCM数据,再用ffplay播放PCM数据,看能否正常播放:
ffplay -f f32le -ac 1 -ar 48000 input_audio // 播放float32型PCM数据
ffplay -f s16le -ac 1 -ar 48000 input_audio //播放short16型PCM数据
ac表示声道数, ar表示采样率, input_audio是PCM音频文件。
二、要获取PCM数据文件,首先要得到opus packet二进制文件, 所以这里涉及到浏览器如何保存二进制文件到本地的问题:
参考代码:
var saveFile = (function(){
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display:none";
return function(data, name){
var blob = new Blob([data]);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = name;
a.click();
window.URL.revokeObjectURL(url);
};
}());
saveFile(data, 'test.pcm');
说明:首先把二进制数据写到typedArray中,然后用这个buffer构造Blob对象,生成URL, 再使用a标签把这个blob下载到本地。
三、利用audioContext播放PCM音频数据的两种方案:
(1)flexVDI的实现
参考:https://github.com/flexVDI/spice-web-client
function play(buffer, dataTimestamp) {
// Each data packet is 16 bits, the first being left channel data and the second being right channel data (LR-LR-LR-LR...)
//var audio = new Int16Array(buffer);
var audio = new Float32Array(buffer); // We split the audio buffer in two channels. Float32Array is the type required by Web Audio API
var left = new Float32Array(audio.length / 2);
var right = new Float32Array(audio.length / 2);
var channelCounter = 0;
var audioContext = this.audioContext;
var len = audio.length; for (var i = 0; i < len; ) {
//because the audio data spice gives us is 16 bits signed int (32768) and we wont to get a float out of it (between -1.0 and 1.0)
left[channelCounter] = audio[i++] / 32768;
right[channelCounter] = audio[i++] / 32768;
channelCounter++;
} var source = audioContext['createBufferSource'](); // creates a sound source
var audioBuffer = audioContext['createBuffer'](2, channelCounter, this.frequency);
audioBuffer['getChannelData'](0)['set'](left);
audioBuffer['getChannelData'](1)['set'](right);
source['buffer'] = audioBuffer;
source['connect'](this.audioContext['destination']);
source['start'](0);
}
注: buffer中保存的是short 型PCM数据,这里为了简单,去掉了对时间戳的处理,因为source.start(0)表示立即播放。如果是float型数据,不需要除以32768.
(2)ws-audio-api的实现
参考:https://github.com/Ivan-Feofanov/ws-audio-api
var bufL = new Float32Array(this.config.codec.bufferSize);
var bufR = new Float32Array(this.config.codec.bufferSize);
this.scriptNode = audioContext.createScriptProcessor(this.config.codec.bufferSize, 0, 2);
if (typeof AudioBuffer.prototype.copyToChannel === "function") {
this.scriptNode.onaudioprocess = function(e) {
var buf = e.outputBuffer;
_this.process(bufL, bufR); //获取PCM数据到bufL, bufR
buf.copyToChannel(bufL, 0);
buf.copyToChannel(bufR, 1);
};
} else {
this.scriptNode.onaudioprocess = function(e) {
var buf = e.outputBuffer;
_this.process(bufL, bufR);
buf.getChannelData(0).set(bufL);
buf.getChannelData(1).set(bufR);
};
}
this.scriptNode.connect(audioContext.destination);
延时卡顿的问题:audioContext有的浏览器默认是48000采样率,有的浏览器默认是44100的采样率,如果喂给audioContext的PCM数据的采样率不匹配,就会产生延时和卡顿的问题。
web audio living的更多相关文章
- 【HTML5】Web Audio API打造超炫的音乐可视化效果
HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我! 源码已经挂到github上了,有兴趣的同学也可以去st ...
- Web Audio介绍
Web Audio还是一个比较新的JavaScript API,它和HTML5中的<audio>是不同的,简单来说,<audio>标签是为了能在网页中嵌入音频文件,和播放器一样 ...
- 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况
对比audio标签 和 Web Audio API 各平台浏览器的支持情况: audio element Web Audio API desktop browsers Chrome 14 Yes ...
- Web Audio API_基本概念
Audio Context 音频的工作环境.类比做化学实验,Audio Context 就是为我们提供各种仪器和材料的实验室(严格地来说制造这些仪器材料的方法和工具也一并提供了).通常来说做实验一间实 ...
- [Javascript] Intro to the Web Audio API
An introduction to the Web Audio API. In this lesson, we cover creating an audio context and an osci ...
- 关于Web Audio API的入门
Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...
- 使用Web Audio API绘制音波图
摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...
- H5的Web Audio Api
概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...
- Web Audio初步介绍和实践
Web Audio还是一个比较新的JavaScript API,它和HTML5中的<audio>是不同的,简单来说,<audio>标签是为了能在网页中嵌入音频文件,和播放器一样 ...
随机推荐
- tomcat使用redis存储共享session
在tomcat集群环境下实现session共享有几种解决方式,这里介绍一种简单的方案. 使用redis对session进行存储,配置比較简单.webserver是tomcat6 1.下载jar包: c ...
- C# : 资源文件(多用于处理国际化)
1.建立Resource文件夹,添加资源文件 处理国际化的问题,我们可以添加多个资源文件,如下就是添加一个中文的,一个英文的. 2.向其中添加键值对. 3.取值 CultureInfo uiCultu ...
- zabbix web monitoring 监控网页
配置 Web 场景 配置 web 场景: 转到: 配置 (Configuration)–>主机 (或者 模板 ) 点击主机 (host)/ 模板 (template) 行中的 Web 点击右上角 ...
- handlebars.js基础学习笔记
最近在帮学校做个课程网站,就有人推荐用jquery+ajax+handlebars做网站前端,刚接触发现挺高大上的,于是就把一些基础学习笔记记录下来啦. 1.引用文件: jquery.js文件下载:h ...
- Mysql查询结果只有一条的情况下把值赋值给变量,再用if else 流程判断
BEGIN set @n=(SELECT count(day) from log where day=CURDATE()); THEN call m_LogInsert(); ELSE call m_ ...
- shell去掉后缀方法
#!/bin/bash olddir=/home/img/luimg newdir=/home/img/luimg/thumb while read line do if [ -f $olddir${ ...
- 【已解决】ckfinder_php_3.4.4 IIS 报错 无效请求
ckfinder_php_3.4.4 IIS 报错 无效请求 (Invalid request) Apache 正常,但是在IIS环境下报错,解决方法 设置 C:\Windows\Temp 目录 给 ...
- Linux下性能分析工具汇总
来自:http://os.51cto.com/art/201104/253114.htm 本文讲述的是:CPU性能分析工具.Memory性能分析工具.I/O性能分析工具.Network性能分析工具. ...
- Web性能测试工具:http_load安装&使用简介
除了siege,在Web性能测试工具中,http_load也是比较热门和常见的一款,有时因为种种原因,只能使用现成的工具,所以多了解和掌握一种Web性能测试工具是很有必要的. 1.下载安装包 略过 2 ...
- 一种关键字搜索---edu.cn
比如要搜索知识点最小二乘,可以这样: 曲线拟合的最小二乘法 edu.cn 然后就一大片关于edu的相关链接,很多知名学校链接 http://www.bb.ustc.edu.cn/jpkc/xiaoji ...