mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Use low latency audio pathway on Lollipop and later
This commit is contained in:
parent
42f18cb4ac
commit
6f82f82abb
@ -1,8 +1,10 @@
|
||||
package com.limelight.binding.audio;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.os.Build;
|
||||
|
||||
import com.limelight.LimeLog;
|
||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||
@ -12,10 +14,50 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
||||
|
||||
private AudioTrack track;
|
||||
|
||||
private AudioTrack createAudioTrack(int channelConfig, int bufferSize, boolean lowLatency) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||
48000,
|
||||
channelConfig,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM);
|
||||
}
|
||||
else {
|
||||
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_GAME);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// Use FLAG_LOW_LATENCY on L through N
|
||||
if (lowLatency) {
|
||||
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
|
||||
}
|
||||
}
|
||||
|
||||
AudioTrack.Builder trackBuilder = new AudioTrack.Builder()
|
||||
.setAudioFormat(new AudioFormat.Builder()
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setSampleRate(48000)
|
||||
.setChannelMask(channelConfig)
|
||||
.build())
|
||||
.setAudioAttributes(attributesBuilder.build())
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.setBufferSizeInBytes(bufferSize);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Use PERFORMANCE_MODE_LOW_LATENCY on O and later
|
||||
if (lowLatency) {
|
||||
trackBuilder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
|
||||
}
|
||||
}
|
||||
|
||||
return trackBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setup(int audioConfiguration) {
|
||||
int channelConfig;
|
||||
int bufferSize;
|
||||
int bytesPerFrame;
|
||||
|
||||
switch (audioConfiguration)
|
||||
@ -38,44 +80,76 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
||||
// do this on many devices and it lowers audio latency.
|
||||
// We'll try the small buffer size first and if it fails,
|
||||
// use the recommended larger buffer size.
|
||||
try {
|
||||
// Buffer two frames of audio if possible
|
||||
bufferSize = bytesPerFrame * 2;
|
||||
|
||||
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||
48000,
|
||||
channelConfig,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM);
|
||||
track.play();
|
||||
} catch (Exception e) {
|
||||
// Try to release the AudioTrack if we got far enough
|
||||
try {
|
||||
if (track != null) {
|
||||
track.release();
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
boolean lowLatency;
|
||||
int bufferSize;
|
||||
|
||||
// Now try the larger buffer size
|
||||
bufferSize = Math.max(AudioTrack.getMinBufferSize(48000,
|
||||
// We will try:
|
||||
// 1) Small buffer, low latency mode
|
||||
// 2) Large buffer, low latency mode
|
||||
// 3) Small buffer, standard mode
|
||||
// 4) Large buffer, standard mode
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
case 1:
|
||||
lowLatency = true;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
lowLatency = false;
|
||||
break;
|
||||
default:
|
||||
// Unreachable
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
case 2:
|
||||
bufferSize = bytesPerFrame * 2;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
// Try the larger buffer size
|
||||
bufferSize = Math.max(AudioTrack.getMinBufferSize(48000,
|
||||
channelConfig,
|
||||
AudioFormat.ENCODING_PCM_16BIT),
|
||||
bytesPerFrame * 2);
|
||||
bytesPerFrame * 2);
|
||||
|
||||
// Round to next frame
|
||||
bufferSize = (((bufferSize + (bytesPerFrame - 1)) / bytesPerFrame) * bytesPerFrame);
|
||||
// Round to next frame
|
||||
bufferSize = (((bufferSize + (bytesPerFrame - 1)) / bytesPerFrame) * bytesPerFrame);
|
||||
break;
|
||||
default:
|
||||
// Unreachable
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||
48000,
|
||||
channelConfig,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM);
|
||||
track.play();
|
||||
// Skip low latency options if hardware sample rate isn't 48000Hz
|
||||
if (AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC) != 48000 && lowLatency) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
track = createAudioTrack(channelConfig, bufferSize, lowLatency);
|
||||
track.play();
|
||||
|
||||
// Successfully created working AudioTrack. We're done here.
|
||||
LimeLog.info("Audio track configuration: "+bufferSize+" "+lowLatency);
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
// Try to release the AudioTrack if we got far enough
|
||||
try {
|
||||
if (track != null) {
|
||||
track.release();
|
||||
track = null;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
LimeLog.info("Audio track buffer size: "+bufferSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user