1. 音频包的结构:
关键成员变量如下,其字段意思自明,无需进一步叙述:
public var lastseq:uint;
public var from:uint;
public var sid:uint;
public var seq:uint;
public var time_stamp:uint;
public var fec_id:uint;
public var type:uint;
public var voice_type:uint;
public var payload:ByteArray;
public var wavBytes:ByteArray;
public var isBroadcast:Boolean;
public var playTime:uint;
public var flvBytes:ByteArray;
public var reSend:Boolean;
2. 音频包的存储结构:
使用双向链表来存储,每个节点对应一个音频包,该双向链表有链表大小size属性,有头节点head.每次来一个音频包,就把该音频包插入到双向链表中的适当位置(在考虑丢包,乱序,延迟之后,确定该音频包的位置),链表按音频包的序列号依次排序,head的序列号最小
3. 把音频包添加到双向链表中:
返回值说明:
Res == 0;说明是正常的按顺序从小到大接收到的音频包
Res == 1;说明是重复接收到的音频包
Res == 2;说明是发生丢包
Res == 3;说明是接收到的是晚到的音频包
根据返回值的不同进行相对应的处理:
Res == 0 或者Res == 3,直接flush(),确定要播放的音频包的数目
Res == 1,重复的包,直接丢弃
Res == 2,发生丢包,对所有丢包序列进行重传
4. flush函数:
(1)上一次播放的音频包与当前链表的head节点的序列号是连续的,
即 lastSeq+2 = head.data.seq:
则从当前head节点开始,找到一段连续的音频包,最多为链表的长度个包,
设此时找到的音频包的个数为flushCnt
(2)上一次播放的音频包与当前的head节点的序列号是不连续的,
即 lastSeq+2 < head.data.seq:
如果此时,链表中的所有的音频包的总共的播放时间已经大于最大的播放时间,
即MAX_PLAY_TIME,则调用flushLater(0,bufTime);函数,见下面对该函数的叙述
5. playAudio函数:
对4步骤中确定的flushCnt个音频包进行播放
(1)如果上次播放的音频包与当前链表的head节点的序列号是不连续的,则要进行丢包
补偿,如果丢包的个数为1,即lastSeq+4 == head.data.seq,则再播放一次上次播放的音频包:具体来说,如果有序列号依次为:2,4,6,10,12,...
上次播放到序列号6为止,而这次要从序列号10开始播放,丢失了序列号为8的音频包,因此,在播放序列号10之前,还要再播放一次序列号6;即播放序列为:
2,4,6,6,10,12,...
如果丢包的个数大于等于2,即lastSeq+4 < head.data.seq,则再播放一次上次播放的音频包,同时,当前音频包要播放两次,具体来说:如果有序列号依次为:
2,4,6,12,14,...上次播放到序列号6为止,而这次要从序列号12开始播放,丢失了序列号为8,10的两个音频包,因此要先播放序列号6,播放序列号12,再接着播放序列号12,再接着播放后面的序列号,即播放序列为:2,4,6,6,12,12,14,...
如丢包补偿的实现如下:
if (_lastAudioPlayRes) {
var seqLen:uint = voiceRes.seq - _lastAudioPlayRes.seq;
if (seqLen == 4) {
_ns.appendBytes(_lastAudioPlayRes.flvBytes);
} else if (seqLen > 4) {
_ns.appendBytes(_lastAudioPlayRes.flvBytes);
_ns.appendBytes(voiceRes.flvBytes);
appendBytes(voiceRes.flvBytes);
}
}
6. flushLater函数:
当flush函数的返回值为0时,表示此次flush函数没有找到要播放的一小段音频包,此时,如果链表中的音频包的播放时间累积总和大于MAX_PLAY_TIME,则传递给flushLater()的参数为minPlayTime==0,_audioBufTimeMax,表示此时只要找到一个音频包,就可以播放,避免每次flush都没找到音频包
否则,若链表中的音频包在netstream中的缓冲时间小于audio_flush_time,且netStream的播放缓冲还没满,则计算最小的播放时间minPlayTime = uint((AUDIO_BUF_TIME_MAX - bufLen)*1000);,然后在链表中找出几帧音频包,使得他们的播放时间总和(包括丢失的包的播放时间)大于minPlayTime即可播放
具体来说:如果有音频包序列为:2,4,6,10,14,16,18...
上次播放到序列号为6为止,然后这一次在调用flush的时候,由于lastSeq+2
7. 平滑Rtt的计算,参考公式如下:
var smoothRtt:uint = (n * _smoothRttSum / _rttCnt + curRtt) / m;
var deltaRtt:int = curRtt - smoothRtt;
var smoothDeltaRtt:int = (n * _smoothRttDeltaSum / _rttCnt + deltaRtt) / m;
var rtt:int = smoothRtt + smoothDeltaRtt;
说明:n,m的取值得根据具体情况,取经验值,上述的MAX_PLAY_TIME,AUDIO_BUF_TIME_MAX也要取相应的经验值
转载请注明出处:山水间博客,http://blog.csdn.net/linyanwen99/article/details/8985656