简体   繁体   English

如何确定Android上的音频延迟(AudioTrack)?

[英]How do you determine the audio latency (AudioTrack) on Android?

I have an app in which I use an AudioTrack in streaming mode to play dynamically generated audio. 我有一个应用程序,我在流模式下使用AudioTrack播放动态生成的音频。 The app doesn't have to respond instantaneously to inputs, so the latency issues don't bother me for that side of the program. 该应用程序不必立即响应输入,因此延迟问题不会打扰我的程序的那一面。

The problem is that I have an animation that needs to be as precisely 'in-sync' as possible with the audio and it seems that different devices have different amounts of time between when the AudioTrack stops blocking the write() call and asks for more data, and when that audio is played from the speaker. 问题是我有一个动画需要与音频尽可能精确地“同步”,似乎不同的设备在AudioTrack停止阻止write()调用和要求更多之间有不同的时间量数据,以及从扬声器播放音频的时间。

My current solution gets me most of the way there -- I count the number of frames I've passed in to the AudioTrack so far, and compare it to getPlaybackHeadPosition() . 我当前的解决方案让我大部分都在那里 - 我计算到目前为止我传递给AudioTrack ,并将它与getPlaybackHeadPosition()进行比较。 It looks basically like: 它看起来基本上像:

long currentTimeInFrames = 0;
while(playingAudio) {
  currentTimeInFrames += numberOfFramesToWrite;
  long delayInFrames = (currentTimeInFrames - audioTrack.getPlaybackHeadPosition());
  audioTrack.write(frameBuffer,0,sampleSize);
  doAnimationAfterDelay(delayInFrames);
}

However, there's still some latency that getPlaybackHeadPosition() doesn't seem to account for that varies by device. 但是,仍有一些延迟, getPlaybackHeadPosition()似乎不会因设备而异。

Is there a way to poll the system for the latency of the AudioTrack? 有没有办法在系统中轮询AudioTrack的延迟?

API level 19 adds a method in AudioTrack called getTimeStamp() . API级别19在AudioTrack添加了一个名为getTimeStamp()的方法 From the documentation: 从文档:

Poll for a timestamp on demand. 根据需要轮询时间戳。

If you need to track timestamps during initial warmup or after a routing or mode change, you should request a new timestamp periodically until the reported timestamps show that the frame position is advancing, or until it becomes clear that timestamps are unavailable for this route. 如果您需要在初始预热期间或路由或模式更改后跟踪时间戳,则应定期请求新的时间戳,直到报告的时间戳显示帧位置正在前进,或者直到明确表示此路由的时间戳不可用为止。

You specify an AudioTimestamp object as the function's parameter and it will fill in the most recently "presented" frame position along with its "estimated" timestamp in nanoseconds. 您将AudioTimestamp对象指定为函数的参数,它将填充最近“呈现”的帧位置及其“估计”时间戳(以纳秒为单位)。 The nanosecond value corresponds to the millisecond value returned by SystemClock.uptimeMillis() . 纳秒值对应于SystemClock.uptimeMillis()返回的毫秒值。

You can then determine the latency by figuring out when you wrote that particular frame to AudioTrack vs. when getTimestamp() thinks it actually presented. 然后,您可以通过确定何时将该特定帧写入AudioTrackgetTimestamp()认为它实际呈现的时间来确定延迟。 I have found this method to be more accurate than the other methods mentioned above. 我发现这种方法比上面提到的其他方法更准确。

You have to be careful though. 你必须要小心。 The documentation says getTimeStamp() is not supported on all platforms or all routes. 文档说所有平台或所有路由都不支持getTimeStamp() You can determine if the call was successful by checking the boolean return value. 您可以通过检查boolean返回值来确定调用是否成功。 I have found with the devices I have tested that the function returns false until audio begins presenting, and then subsequent calls return true. 我发现我测试的设备函数返回false,直到音频开始呈现,然后后续调用返回true。 I have only tested with AudioTrack in STREAM_MUSIC mode. 我只在STREAM_MUSIC模式下使用AudioTrack进行了测试。 Your mileage may vary. 你的旅费可能会改变。

Consider driver's latency. 考虑驱动程序的延迟。 There's hidden function AudioManager.getOutputLatency(int) to get this. 有隐藏的函数AudioManager.getOutputLatency(int)来获取它。

Call it like this: 像这样称呼它:

AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
try{
   Method m = am.getClass().getMethod("getOutputLatency", int.class);
   latency = (Integer)m.invoke(am, AudioManager.STREAM_MUSIC);
}catch(Exception e){
}

I get about 45 - 50 ms on different devices. 我在不同的设备上大约需要45到50毫秒。 Use the result in your calculations. 在计算中使用结果。

You should take into account the buffersize you passed along into the AudioTrack creation. 您应该考虑传递给AudioTrack创建的缓冲区大小。

final int minBufSize = AudioTrack.getMinBufferSize(Application.PLAYRATE,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

out=new AudioTrack(AudioManager.STREAM_MUSIC, Application.PLAYRATE, 
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufSize, 
AudioTrack.MODE_STREAM);

extraLatencyFrames = minBufSize/4;

Okay, this is the key. 好的,这是关键。 First you need to extend the Audiotrack class, and then use the getNativeFrameCount to have an approximation on the latency involved in the native side of things. 首先,您需要扩展Audiotrack类,然后使用getNativeFrameCount来近似于本机方面所涉及的延迟。

class MyAudioTrack extends AudioTrack
{
    public MyAudioTrack(int streamType, int sampleRateInHz, int channelConfig,
            int audioFormat, int bufferSizeInBytes, int mode)
            throws IllegalArgumentException {
        super(streamType, sampleRateInHz, channelConfig, audioFormat,
                bufferSizeInBytes, mode);
        System.out.println("Native framecount "+getNativeFrameCount());
    }   
    public int getFrameCount()
    {
        return getNativeFrameCount();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM