我正在为我的VOIP应用程序寻找Java中的自适应 jitter buffer 实现。我为应用程序编写了一个固定的 jitter buffer ,但是由于网络质量差,我遇到了缓冲区不足或缓冲区溢出的问题。
是否有任何基于Java的自适应 jitter buffer 实现可直接与我的应用程序一起使用或用作引用。
任何帮助将不胜感激。
谢谢
最佳答案
我一直在(在C语言中)解决这个非常问题的问题,有一段时间了,就在我认为已经解决了这个问题时,互联网就变得很忙,或者其他地方发生了变化并蓬勃发展!再次有些断断续续的音频。出色地。我很确定我现在已经舔了一下。
使用下面的算法,我的声音质量确实非常好。我已经将其与在相同网络条件下运行的其他软件电话进行了比较,它的性能明显更好。
我要做的第一件事是尝试确定要注册的PBX或其他SIP代理是否在具有UA(软件电话)的本地网络上。
如果是,我将 jitter buffer 定义为100ms,否则,我将使用200ms。这样,如果可以的话,我可以限制我的等待时间。即使200毫秒也不会产生任何明显的对话麻烦或通话过度。
所以。然后我使用您可用的任何类型的系统计数器,例如Windows = GetTickCount64(),以第一个数据包进入播放的毫秒精度时间填充变量。我们将该变量称为“x”。
然后当(((GetTickCount64()-x)> jitterbuffer))为true时,我开始在该缓冲区上播放。
直接固定长度抖动缓冲器的实现。这是一个棘手的问题。
当我解码RTP帧(如从muLaw到PCM)以缓冲它以便回放时,我计算音频帧的AVERAGE ABSOLUTE振幅,并将其与该帧一起保存以进行回放。
我通过具有这样的结构来做到这一点:
typedef struct tagCCONNECTIONS {
char binuse;
struct sockaddr_in client;
SOCKET socket;
unsigned short media_index;
UINT32 media_ts;
long ssrc;
unsigned long long lasttimestamp;
int frames_buffered;
int buffer_building;
int starttime;
int ssctr;
struct {
short pcm[160];
} jb[AUDIO_BUFFER]; /* Buffered Audio frame array */
char jbstatus[AUDIO_BUFFER]; /* An array containing the status of the data in the CCONNETIONS::jb array */
char jbsilence[AUDIO_BUFFER];
int jbr,jbw; /* jbr = read position in CCONNECTIONS::jb array, jbw = write position */
short pcms[160];
char status;
/* These members are only used to buffer playback */
PCMS *outraw;
char *signal;
WAVEHDR *preparedheaders;
/**************************************************/
DIALOGITEM *primary;
int readptr;
int writeptr;
} CCONNECTIONS;
好的,请注意tagCCONNECTIONS::jbsilence [AUDIO_BUFFER]结构成员。这样,对于tagCCONNECTIONS::jb [x] .pcm []中的每个解码音频帧,都有关于该帧是否可听的相应数据。
这意味着对于将要播放的每个音频帧,我们都有关于该帧是否可听的信息。
还...
#define READY 1
#define EMPTY 0
tagCCONNECTIONS::jbstatus [AUDIO_BUFFER]字段让我们知道我们正在考虑播放的特定音频帧是READY还是EMPTY。在缓冲区下溢的理论情况下,它可能为空,在这种情况下,我们通常会等待它准备就绪,然后开始播放...
现在在我的播放音频的例程中,我有两个主要功能。一种叫做pushframe(),另一种叫做popframe()。
我打开网络连接并接收RTP的线程调用pushframe(),它将muLaw转换为PCM,计算帧的AVERAGE ABSOLUTE振幅并将其静音,并标记为无声,并标记:: jbstatus [x]准备就绪
然后,在播放音频的线程中,我们首先检查jitterbuffer时间是否已到期,
if ( ( GetTickCount64() - x ) > jitterbuffer ) {...}
然后,我们检查下一个要播放的帧是否为READY(就绪)(这意味着它确实已经被填满)。
然后,我们检查该框架之后的框架是否也准备就绪,以及是否听得见或无声!
*** 重要的
基本上,我们知道200ms的 jitter buffer 可以容纳10个20ms的音频帧。
如果在最初的200ms抖动缓冲延迟(节省音频)之后的任何时候,我们排队的音频帧数下降到10(或jitterbuffer/20)以下,我们将进入“缓冲构建”模式。如果我们计划播放的下一个音频帧是静默的,则告诉程序 jitter buffer 尚未满,距离满时还有20毫秒,但是我们继续播放我们要播放的帧现在,因为它是NEXT帧,所以我们再次看到它是“无声的”。我们只是不播放静默帧,而是使用静默期来等待入站帧以重新填充缓冲区。
tagCCONNECTIONS::lasttimestamp = GetTickCount64() - (jitterbuffer-20);
在所谓的“静默”期间,这将有一段完全静默的时间,但允许缓冲区自我补充。然后,当我再次充满10帧时,我退出“buffer_building”模式,只播放音频。
即使我们从一个完整的缓冲区短了一帧,我也会进入“buffer_building”模式,因为一个长发的人可能会说话,并且不会有太多的沉默。即使在“buffer_building”模式下,这也可能会迅速耗尽缓冲区。
现在……“什么是沉默?”我听到你问。在周围的困惑中,我对静音进行了硬编码,使其成为平均绝对16位PCM幅度小于200的任何帧。我对此进行了如下计算:
int total_pcm_val=0;
/* int jbn= whatever frame we're on */
for (i=0;i<160;i++) {
total_pcm_val+=ABS(cc->jb[jbn].pcm[i]);
}
total_pcm_val/=160;
if (total_pcm_val < 200) {
cc->jbsilence[jbn] = 1;
}
现在,我实际上正在计划在该连接上保持整体平均振幅,并尝试一下,如果我们刚收到的当前音频帧的振幅是整体平均振幅的5%或更小,那么我们认为该帧是无声的,或者也许2%...我不知道,但是那样的话,如果有很多风或背景噪音,“静音”的定义就会适应。我必须解决这个问题,但是我相信这是补充 jitter buffer 的关键。
在没有重要信息可听的情况下执行此操作,并使实际信息(其声音)保持清晰。
我希望这有帮助。在解释事情时,我有点头脑不清,但是我对VoIP应用程序的声音感到非常满意。
关于java - Java中的 jitter buffer 实现,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9497540/