WXInlinePlayer的音频播放部分。
入口很简单,判断是不是微信环境。
1
2
3
4
5
6
7
8
9
|
import Util from '../util/util';
import BrowserSound from './browser';
import WeChatSound from './wechat';
function Sound(opt) {
return Util.isWeChat() ? new WeChatSound(opt) : new BrowserSound(opt);
}
export default Sound;
|
由于微信部分和浏览器部分区别不大,属于细节性的区别,所以下面从浏览器角度看一下代码。先来看数据流入的地方:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
decode(data) {
if (data.length) {
data = Buffer.from(data);
this.data = Buffer.concat([this.data, data]); // 将数据放到this.data中保存起来
if (this.context) {
return new Promise(resolve => {
this.context.decodeAudioData( // 调用AudioContext.decodeAudioData()
this.data.buffer,
buffer => {
this._onDecodeSuccess(buffer);
resolve();
},
error => {
this._onDecodeError(error);
resolve();
}
);
});
}
}
return Promise.resolve();
}
|
这个函数是在processor
收到解码器decode
消息后调用的,这时候是解码器解封装了一段数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
_onDecodeSuccess(audioBuffer) {
const audioSrc = this.context.createBufferSource();
audioSrc.onended = this._onAudioBufferEnded.bind(this); // 播放完及时清理this.audioSrcNodes
if (!this.playStartedAt) {
const { duration } = audioBuffer; // 第一段音频数据的时长
const { currentTime, baseLatency, sampleRate } = this.context;
const startDelay = duration + (baseLatency || 128 / sampleRate); // 第一段数据延迟一定时间再播放
this.playStartedAt = currentTime + startDelay; // 记录开始播放的时间
}
audioSrc.buffer = audioBuffer;
if (this.state == 'running') {
try {
audioSrc.connect(this.gainNode);
audioSrc.start(this.totalTimeScheduled + this.playStartedAt); // 通过时间轴,指定每段音频的播放开始时间,避免卡顿
} catch (e) {}
}
this.audioSrcNodes.push({
source: audioSrc,
duration: audioBuffer.duration, // 这段音频的时长
timestamp: this.totalTimeScheduled // 这段音频在所有可播放的音频段中的时间位置
});
this.totalTimeScheduled += audioBuffer.duration;
this.duration += audioBuffer.duration;
this.data = Buffer.alloc(0);
this.emit('decode:success');
}
_onDecodeError(e) {
this.emit('decode:error', e); // 音频解码失败返回事件
}
|
除了上面这些部分,还有一个unblock
函数值得注意,这个函数在代码中几乎相当于音频播放的start,没有其它用处。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
unblock(offset) {
if (this.state != 'blocked') {
return;
}
this.state = 'running';
this.resume();
this.setBlockedCurrTime(offset);
this.playStartedAt = 0; // 初始化时间变量状态
this.totalTimeScheduled = 0;
for (let i = 0; i < this.audioSrcNodes.length; i++) {
const { source, timestamp, duration } = this.audioSrcNodes[i];
source.onended = null;
source.disconnect();
const audioSrc = this.context.createBufferSource();
audioSrc.onended = this._onAudioBufferEnded.bind(this); // 清理播放完的this.audioSrcNodes
if (!this.playStartedAt) {
const { currentTime, baseLatency, sampleRate } = this.context;
const startDelay = duration + (baseLatency || 128 / sampleRate);
this.playStartedAt = currentTime + startDelay;
}
audioSrc.buffer = source.buffer;
try {
audioSrc.connect(this.gainNode);
audioSrc.start(
this.totalTimeScheduled + this.playStartedAt, // 给音频安排schedule,让它在适当的时间播放
!i ? offset / 1000 - timestamp : 0
);
} catch (e) {}
this.audioSrcNodes[i].source = audioSrc;
this.audioSrcNodes[i].timestamp = this.totalTimeScheduled;
this.totalTimeScheduled += duration;
}
}
|
文章作者
shandowc
上次更新
2020-07-15
(7a1c413)