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

代码:(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. tomcat使用redis存储共享session

    在tomcat集群环境下实现session共享有几种解决方式,这里介绍一种简单的方案. 使用redis对session进行存储,配置比較简单.webserver是tomcat6 1.下载jar包: c ...

  2. C# : 资源文件(多用于处理国际化)

    1.建立Resource文件夹,添加资源文件 处理国际化的问题,我们可以添加多个资源文件,如下就是添加一个中文的,一个英文的. 2.向其中添加键值对. 3.取值 CultureInfo uiCultu ...

  3. zabbix web monitoring 监控网页

    配置 Web 场景 配置 web 场景: 转到: 配置 (Configuration)–>主机 (或者 模板 ) 点击主机 (host)/ 模板 (template) 行中的 Web 点击右上角 ...

  4. handlebars.js基础学习笔记

    最近在帮学校做个课程网站,就有人推荐用jquery+ajax+handlebars做网站前端,刚接触发现挺高大上的,于是就把一些基础学习笔记记录下来啦. 1.引用文件: jquery.js文件下载:h ...

  5. Mysql查询结果只有一条的情况下把值赋值给变量,再用if else 流程判断

    BEGIN set @n=(SELECT count(day) from log where day=CURDATE()); THEN call m_LogInsert(); ELSE call m_ ...

  6. shell去掉后缀方法

    #!/bin/bash olddir=/home/img/luimg newdir=/home/img/luimg/thumb while read line do if [ -f $olddir${ ...

  7. 【已解决】ckfinder_php_3.4.4 IIS 报错 无效请求

    ckfinder_php_3.4.4 IIS 报错 无效请求 (Invalid request) Apache 正常,但是在IIS环境下报错,解决方法 设置 C:\Windows\Temp 目录 给 ...

  8. Linux下性能分析工具汇总

    来自:http://os.51cto.com/art/201104/253114.htm 本文讲述的是:CPU性能分析工具.Memory性能分析工具.I/O性能分析工具.Network性能分析工具. ...

  9. Web性能测试工具:http_load安装&使用简介

    除了siege,在Web性能测试工具中,http_load也是比较热门和常见的一款,有时因为种种原因,只能使用现成的工具,所以多了解和掌握一种Web性能测试工具是很有必要的. 1.下载安装包 略过 2 ...

  10. 一种关键字搜索---edu.cn

    比如要搜索知识点最小二乘,可以这样: 曲线拟合的最小二乘法 edu.cn 然后就一大片关于edu的相关链接,很多知名学校链接 http://www.bb.ustc.edu.cn/jpkc/xiaoji ...