我正在为我的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/

10-10 21:39