gpt4 book ai didi

java - 在JVM中长时间记录音频时出现突然的延迟

转载 作者:IT老高 更新时间:2023-10-28 13:31:20 27 4
gpt4 key购买 nike

我正在实现一个使用JDK Version 8 Update 201实时(或至少尽可能接近实时)记录和分析音频的应用程序。在执行模拟该应用程序典型用例的测试时,我注意到在连续录制了几个小时的音频后,突然延迟了一到两秒。到目前为止,还没有明显的延迟。仅在此关键记录点之后的几个小时才开始出现此延迟。

到目前为止我尝试过的

为了检查用于计时音频样本的代码是否错误,我注释掉了与计时有关的所有内容。这基本上使我离开了这个更新循环,该循环会在准备好音频样本后立即获取它们(请注意:Kotlin代码):

while (!isInterrupted) {
val audioData = read(sampleSize, false)
listener.audioFrameCaptured(audioData)
}

这是我的读取方法:
fun read(samples: Int, buffered: Boolean = true): AudioData {
//Allocate a byte array in which the read audio samples will be stored.
val bytesToRead = samples * format.frameSize
val data = ByteArray(bytesToRead)

//Calculate the maximum amount of bytes to read during each iteration.
val bufferSize = (line.bufferSize / BUFFER_SIZE_DIVIDEND / format.frameSize).roundToInt() * format.frameSize
val maxBytesPerCycle = if (buffered) bufferSize else bytesToRead

//Read the audio data in one or multiple iterations.
var bytesRead = 0
while (bytesRead < bytesToRead) {
bytesRead += (line as TargetDataLine).read(data, bytesRead, min(maxBytesPerCycle, bytesToRead - bytesRead))
}

return AudioData(data, format)
}

但是,即使没有我的帮助,问题也没有得到解决。因此,我继续进行一些实验,并让应用程序使用不同的音频格式运行,这导致了非常困惑的结果(我将使用PCM签名的16位立体声音频格式,其字节序少,采样率44100.0 Hz默认情况下,除非另有说明):
  • 根据所使用的机器,延迟出现之前必须经过的关键时间似乎有所不同。在我的Windows 10台式电脑上,它大约需要6.5到7个小时。但是,在我的笔记本电脑(也使用Windows 10)上,相同的音频格式大约需要4到5个小时。
  • 使用的音频 channel 数量似乎有影响。如果我将声道数量从立体声更改为单声道,则延迟出现之前的时间会在台式机上加倍,介于13到13.5小时之间。
  • 将样本大小从16位减小到8位也会导致延迟开始出现之前的时间加倍。在我的桌面上的13到13.5小时之间的某个时间。
  • 将字节顺序从小字节序更改为大字节序无效。
  • 从立体声混音切换到物理麦克风也不起作用。
  • 我尝试使用不同的缓冲区大小(1024、2048和3072样本帧)及其默认缓冲区大小打开该行。这也没有改变任何东西。
  • 在延迟开始发生后刷新TargetDataLine会导致所有字节为零,持续大约一到两秒钟。此后,我再次获得非零值。但是,延迟仍然存在。如果在临界点之前刷新该行,则不会得到那些零字节。
  • 在出现延迟后停止并重新启动TargetDataLine也不会更改任何内容。
  • 关闭并重新打开TargetDataLine确实可以避免延迟,直到它在几个小时后重新出现为止。
  • 每十分钟自动刷新一次TargetDataLines内部缓冲区无助于解决问题。因此,内部缓冲区中的缓冲区溢出似乎不是原因。
  • 使用并行垃圾收集器来避免应用程序冻结也无济于事。
  • 使用的采样率似乎很重要。如果我将采样率提高一倍至88200 Hertz,则延迟将在3到3.5个小时的运行时间之间开始。
  • 如果我让它使用我的“默认”音频格式在Linux下运行,则在经过大约9个小时的运行时间后,它仍然可以正常运行。

  • 我得出的结论:

    这些结果使我得出的结论是,在此问题开始发生之前,我可以记录音频的时间取决于运行应用程序的计算机,并且取决于音频的字节率(即帧大小和采样率)。音频格式。这似乎是正确的(尽管到目前为止我还不能完全确认这一点),因为如果我结合2和3中所做的更改,我会假设我可以录制四倍的音频采样时间(介于26和27小时),就像在延迟开始出现之前使用“默认”音频格式时一样。由于我还没有足够的时间让应用程序运行这么长时间,我只能说由于时间限制,在不得不停止它之前,它可以正常运行约15个小时。因此,该假设仍有待确认或否定。

    根据项目符号13的结果,似乎整个问题仅在使用Windows时出现。因此,我认为这可能是javax.sound.sampled API的特定于平台的部分中的错误。

    即使我认为当此问题开始发生时我可能已经找到了改变的方法,但我对结果并不满意。我可以定期关闭并重新打开该行,以免该问题开始出现。但是,这样做会导致一些任意的少量时间,而我将无法捕获音频样本。此外,Javadoc指出,某些行在关闭后根本无法重新打开。因此,对于我而言,这不是一个好的解决方案。

    理想情况下,整个问题都不应该发生。我是否完全缺少某些东西,或者我在使用javax.sound.sampled API可能遇到的限制?我该如何摆脱这个问题?

    编辑:通过Xtreme Biker和gidds的建议,我创建了一个小示例应用程序。您可以在 Github repository中找到它。

    最佳答案

    我在Java音频接口(interface)方面有(相当)丰富的经验。
    以下几点可能有助于指导您找到正确的解决方案:

  • 这与JVM版本无关-自Java 1.3或1.5以来,Java音频系统几乎没有升级
  • Java音频系统是操作系统必须提供的任何音频接口(interface)API的穷人包装。在linux中,它是Pulseaudio库,对于Windows,则是直接显示音频API(如果我没记错的话)。
  • 同样,音频系统API属于传统API-有些功能无法使用或未实现,其他行为则很奇怪,因为它们取决于过时的设计(如果需要,我可以提供示例)。
  • 这不是垃圾收集的问题-如果您对“延迟”的定义符合我的理解(音频数据会延迟1-2秒,这意味着您会在1-2秒后开始听东西),那么,垃圾收集器无法使空白数据神奇地被目标数据行捕获,然后像往常一样在2秒钟的字节偏移后附加数据。
  • 这里最有可能发生的事情是硬件或驱动程序在某个时候为您提供了2秒的乱码数据,然后像往常一样流送其余数据,导致您遇到“延迟”。
  • 它可以在linux上完美运行的事实意味着这不是硬件问题,而是与驱动程序相关的问题。
  • 要确认这一怀疑,您可以尝试通过FFmpeg捕获音频达相同的持续时间,然后查看问题是否重现。
  • 如果您使用的是专用音频捕获硬件,则最好与硬件制造商联系,并向他询问您在Windows上面临的问题。
  • 无论如何,当从头开始编写音频捕获应用程序时,如果可能的话,我强烈建议您远离Java音频系统。对POC很好,但它是未维护的旧版API。 JNA始终是一个可行的选择(我在Linux中将其与ALSA/Pulse-audio结合使用来控制Java音频系统无法更改的音频硬件属性),因此您可以在Windows中使用C++查找音频捕获示例并将其转换为Java。它可以为您提供对音频捕获设备的精细控制,远远超过JVM提供的OOTB。如果您想看看一个生活/呼吸可用的JNA示例,请查看我的JNA AAC encoder项目。
  • 同样,如果您使用特殊的捕获技巧,那么制造商很有可能已经提供了自己的低级C api来与硬件接口(interface),并且您也应该考虑一下。
  • 如果不是这种情况,也许您和您的公司/客户应该
    考虑使用专门的捕获硬件(不必
    这么贵)。
  • 关于java - 在JVM中长时间记录音频时出现突然的延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55482552/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com