基于udp的音频抖动缓冲的实现
来源:互联网 发布:门窗销售软件 编辑:程序博客网 时间:2024/05/16 18:35
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<head.data.seq,因此这一次没找到要播放的音频包,flushCnt==0,因此会比较此时播放链表中的音频包序列10,14,16,18 ...的播放时间总和与MAX_PLAY_TIME的大小,如果大于MAX_PLAY_TIME,表明NetStream中的播放缓冲已经满了,于是调用flushLater(),只要找到一帧音频包,就进行播放;如果小于MAX_PLAY_TIME,则表明NetStream中的播放缓冲还没满,还有一小段时间bufLen=bufTimeMax-netstream.bufferLength;所以需要在播放链表中找到几帧音频包,满足播放时间大于bufLen时,就进行播放
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
- 基于udp的音频抖动缓冲的实现
- 基于Udp的socket 实现
- 窗体抖动的实现
- 抖动窗口的实现
- [DirectSound] 基于DirectSound的音频均衡器实现
- 基于Apache Mina实现的UDP服务端
- 基于Apache Mina实现的UDP服务端
- 基于UDP的DayTime服务实现
- 基于UDP的局域网聊天实现
- 基于UDP的H5跨屏实现
- Java 实现基于UDP的用户登录
- 编程实现基于UDP的socket应用
- android view的抖动实现
- 窗口抖动效果的实现
- 手机屏幕窗口抖动的实现
- 基于屏幕像素抖动的PCF
- 基于Verilog的防抖动程序
- 基于屏幕像素抖动的PCF
- ListBox 添加水平滚动条
- HTML 网页变灰色
- android ubuntu jni so 报错 : java.lang.UnsatisfiedLinkError: stringFromJNI ____________ndk make: *** 没
- 全面认识Android手机(MIUI ROM适配之旅第四天——移植MIUI Framework)
- 服务器数据库系列 - apache ab 的使用
- 基于udp的音频抖动缓冲的实现
- IAR for AVR 学习笔记1--中断定义
- 适合我这种初学数据库菜鸟的存储过程的创建和调用详细过程
- Struts2自定义拦截器—Session超时的处理
- zoj_2388 Beat the Spread!
- 各种指针的定义
- Linux如何查找某种格式的文件或文件名中包括什么字符的文件
- TLD(Tracking-Learning-Detection)学习与源码理解之(一)
- 黑马程序员——银行调度系统