2012-02-29 61 views
4

我正在寻找适用于我的VOIP应用程序的Java自适应抖动缓冲区实现。我为我的应用程序编写了一个固定的抖动缓冲区,但由于网络质量差,我会遇到缓冲区不足或缓冲区溢出问题。Java中的抖动缓冲区实现

是否有任何基于Java的自适应抖动缓冲区实现可用于直接与我的应用程序一起使用或用作参考。

任何帮助将不胜感激。

感谢

+0

到底是什么问题了吗?估计需要的缓冲区大小(排队理论可能有所帮助。)还是动态内存分配?或者是其他东西? – Matthias 2012-02-29 14:13:23

+0

我希望能够根据当前的抖动来改变缓冲区大小。 – 2012-03-01 03:32:39

+0

你在哪里遇到困难? – Matthias 2012-03-01 06:22:44

回答

8

我一直对这个问题非常(在C虽然)进行了一段时间,当我以为我找到了,互联网变得繁忙的地方或以其他方式改变和繁荣!一些断断续续的音频。好。我很确定我现在已经舔了它。

使用下面的算法,我有非常好听的音质。我将它与我在相同网络条件下运行的其他软电话进行了比较,它的性能明显更好。

我做的第一件事是尝试确定我们正在注册的PBX或其他SIP代理是否与UA(软电话)在本地网络上。

如果是这样,我将我的jitterbuffer定义为100ms,如果没有,我使用200ms。如果可以的话,这样我就可以限制我的延迟即使是200毫秒也不会产生明显的会话故障或过度说话。

所以。然后我使用任何类型的系统计数器,例如Windows = GetTickCount64(),用我的第一个数据包进入播放的毫秒精度时间填充一个变量。我们称这个变量为“x”。

然后,当((GetTickCount64() - x)> jitterbuffer)为真时,我开始在该缓冲区上播放。

直向前的固定长度抖动缓冲器实现。这是棘手的一点。当我正在解码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]字段让我们知道,如果我们正在考虑播放特定音频帧准备好或空。在缓冲区下溢的理论情况下,它可能是空的,在这种情况下,我们通常会等待它准备好,然后开始播放...

现在在我的例程中播放音频...我有两个主要功能。一个叫做pushframe(),另一个叫做popframe()。

我的线程打开网络连接并接收RTP调用pushframe(),它将muLaw转换为PCM,计算框架的AVERAGE ABSOLUTE振幅,并将其标记为静音,并将其标记为:: jbstatus [X]为准备

然后在我的线程播放音频,我们首先检查防抖动时间已过,再由

if ((GetTickCount64() - x) > jitterbuffer) {...} 

然后,我们检查,如果要播放的下一帧是READY(意味着它确实已经填满了)。

然后我们检查帧帧后准备还,如果是声或无声!

***重要

基本上,我们知道,一个200ms的抖动缓冲器可以坐一个20ms的音频帧。

如果在初始200ms抖动缓冲延迟(保存音频)之后的任意时刻,我们排队的音频帧数将降至10以下(或jitterbuffer/20),我们进入我称之为“buffer_building”模式。如果我们预定播放的下一个音频帧没有声音,我们会告诉程序抖动缓冲区还没有满,但它仍然离满20毫秒,但我们继续播放我们的帧现在因为这是下一个框架,我们再次看到“沉默”...。我们只是不播放静音帧,并使用静默期等待入站帧来重新填充我们的缓冲区。

tagCCONNECTIONS::lasttimestamp = GetTickCount64() - (jitterbuffer-20); 

这将在什么将是一段完全沉默的“假设”的沉默,但允许缓冲,以补充自身。然后,当我再次满满10个帧时,我退出“buffer_building”模式,然后播放音频。

我输入“buffer_building”模式,即使我们从全缓冲短的一帧,因为长篇大论的人可以说话,有可能不会有太大的沉默。即使在“buffer_building”模式下,这可能会快速耗尽缓冲区。

现在... “什么是沉默?”我听到你问。在我瞎搞,我硬编码沉默小于200的平均绝对16位PCM幅度任何帧我想这个如下:

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%......我不知道,但如果存在很多风或背景噪音,那么“沉默”的定义可以适应。我必须玩这个,但我相信这是补充抖动缓冲区的关键。

做时,有没有听,并保持实际信息(自己的声音)水晶般清澈的重要信息。

我希望这会有所帮助。当谈到解释事情时,我有些分散,但我对我的VoIP应用程序听起来非常非常满意。

+0

知道处理耗尽连续声源的任何合理方案(如流媒体音乐?) – 2016-01-29 00:52:26

+0

真正有用的文章!你的意思是说,即使播放一帧(第一帧),节目将从播放模式切换到“缓冲区建立”模式?因为在这种情况下,每次播放第一帧时,程序都会继续从播放切换到缓冲区构建模式。你能澄清一下吗? – 2017-05-24 07:38:14