总结网页音频直播的方案和遇到的问题。

代码:(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的更多相关文章

  1. 【HTML5】Web Audio API打造超炫的音乐可视化效果

    HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我!  源码已经挂到github上了,有兴趣的同学也可以去st ...

  2. Web Audio介绍

    Web Audio还是一个比较新的JavaScript API,它和HTML5中的<audio>是不同的,简单来说,<audio>标签是为了能在网页中嵌入音频文件,和播放器一样 ...

  3. 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况

    对比audio标签 和 Web Audio API 各平台浏览器的支持情况:   audio element Web Audio API desktop browsers Chrome 14 Yes  ...

  4. Web Audio API_基本概念

    Audio Context 音频的工作环境.类比做化学实验,Audio Context 就是为我们提供各种仪器和材料的实验室(严格地来说制造这些仪器材料的方法和工具也一并提供了).通常来说做实验一间实 ...

  5. [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 ...

  6. 关于Web Audio API的入门

    Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...

  7. 使用Web Audio API绘制音波图

    摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...

  8. H5的Web Audio Api

    概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...

  9. Web Audio初步介绍和实践

    Web Audio还是一个比较新的JavaScript API,它和HTML5中的<audio>是不同的,简单来说,<audio>标签是为了能在网页中嵌入音频文件,和播放器一样 ...

随机推荐

  1. SQL SERVER 使用订阅发布同步数据库(转)

    一.数据库复制涉及  1.发布服务器:  数据的来源服务器,维护源数据,决定哪些数据将被分发,检测哪些数据发生了修改,并将这些信息提交给分发服务器.  2.分发服务器: 分发服务器负责把从发布服务器拿 ...

  2. Laravel之视图和Blade模板引擎

    一.视图 1.视图文件存放在resources/views目录2.视图载入及传参 return view('greeting', ['name' => 'James']); 还可以通过with ...

  3. 51nod 125乘法逆元 (扩展欧几里得)

    给出2个数M和N(M < N),且M与N互质.找出一个数K满足0 < K < N且K * M % N = 1,假设有多个满足条件的.输出最小的. Input 输入2个数M, N中间用 ...

  4. jQuery 技术揭秘

    1.平时jquery调用页: index.html : <!DOCTYPE html> <html lang="en"> <head> < ...

  5. kvo&kvc

    Key Value Coding Key Value Coding是cocoa的一个标准组成部分,它能让我们可以通过name(key)的方式访问property, 不必调用明确的property ac ...

  6. scikit-learn---PCA(Principle Component Analysis)---KNN(image classifier)

    摘要:PCA为非监督分类方法,常用于数据降维.为监督分类数据预处理,本例采用PCA对人脸特征提取先做降维处理,然后使用KNN算法对图片进行分类 ##1.PCA简介 设法将原来变量重新组合成一组新的互相 ...

  7. 使用Gitolite搭建Gitserver

    Gitolite是一款Perl语言开发的Git服务管理工具.通过公钥对用户进行认证.并可以通过配置文件对些操作进行基于分支和路径的精细控制. Gitolite採用的是SSH协议而且使用SSH公钥认证. ...

  8. 用VS2010创建三层架构开发模式及三层架构的研究

    三层架构的研究 三层体系结构的概念 用户界面表示层(USL) 业务逻辑层(BLL) 数据访问层(DAL) BLL将USL与DAL隔开了,并且加入了业务规则   各层的作用 1:数据数据访问层:主要是对 ...

  9. 利用Bootstrap简单实现一个文件上传进度条

    © 版权声明:本文为博主原创文章,转载请注明出处 说明: 1. 使用commons-fileupload.jar实现文件上传及进度监听 2. 使用bootstrap的进度条进行页面显示 3. 因为进度 ...

  10. Struts2学习之拦截器栈

    © 版权声明:本文为博主原创文章,转载请注明出处 拦截器栈: - 从结构上看:拦截器栈相当于多个拦截器的组合 - 从功能上看:拦截器栈也是拦截器 默认拦截器栈: - 在struts-core.jar中 ...