Skip to content

Commit 21bdd69

Browse files
authored
Allow usage of non-allocating frame buffers (#1095)
* Add option for enabling non-allocating framebuffers. * Accommodate frame buffer changes.
1 parent 4384612 commit 21bdd69

File tree

4 files changed

+17
-9
lines changed

4 files changed

+17
-9
lines changed

LavalinkServer/application.yml.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ lavalink:
3838
rotation: true
3939
channelMix: true
4040
lowPass: true
41+
nonAllocatingFrameBuffer: false # Setting to true reduces the number of allocations made by each player at the expense of frame rebuilding (e.g. non-instantaneous volume changes)
4142
bufferDurationMs: 400 # The duration of the NAS buffer. Higher values fare better against longer GC pauses. Duration <= 0 to disable JDA-NAS. Minimum of 40ms, lower values may introduce pauses.
4243
frameBufferDurationMs: 5000 # How many milliseconds of audio to keep buffered
4344
opusEncodingQuality: 10 # Opus encoder quality. Valid values range from 0 to 10, where 10 is best quality but is the most expensive on the CPU.

LavalinkServer/src/main/java/lavalink/server/config/AudioPlayerConfiguration.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.sedmelluq.discord.lavaplayer.source.soundcloud.*
1414
import com.sedmelluq.discord.lavaplayer.source.twitch.TwitchStreamAudioSourceManager
1515
import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager
1616
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager
17+
import com.sedmelluq.discord.lavaplayer.track.playback.NonAllocatingAudioFrameBuffer
1718
import com.sedmelluq.lava.extensions.youtuberotator.YoutubeIpRotatorSetup
1819
import com.sedmelluq.lava.extensions.youtuberotator.planner.*
1920
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.Ipv4Block
@@ -55,6 +56,11 @@ class AudioPlayerConfiguration {
5556
audioPlayerManager.enableGcMonitoring()
5657
}
5758

59+
if (serverConfig.isNonAllocatingFrameBuffer) {
60+
log.info("Using a non-allocating frame buffer")
61+
audioPlayerManager.configuration.setFrameBufferFactory(::NonAllocatingAudioFrameBuffer)
62+
}
63+
5864
val defaultFrameBufferDuration = audioPlayerManager.frameBufferDuration
5965
serverConfig.frameBufferDurationMs?.let {
6066
if (it < 200) { // At the time of writing, LP enforces a minimum of 200ms.

LavalinkServer/src/main/java/lavalink/server/config/ServerConfig.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.springframework.stereotype.Component
3030
@Component
3131
class ServerConfig {
3232
var password: String? = null
33+
var isNonAllocatingFrameBuffer = false
3334
var bufferDurationMs: Int? = null
3435
var frameBufferDurationMs: Int? = null
3536
var opusEncodingQuality: Int? = null

LavalinkServer/src/main/java/lavalink/server/player/LavalinkPlayer.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
*/
2222
package lavalink.server.player
2323

24+
import com.sedmelluq.discord.lavaplayer.format.StandardAudioDataFormats
2425
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
2526
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager
2627
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter
2728
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
2829
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason
2930
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame
31+
import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame
3032
import dev.arbjerg.lavalink.api.AudioPluginInfoModifier
3133
import dev.arbjerg.lavalink.api.IPlayer
3234
import io.netty.buffer.ByteBuf
@@ -36,6 +38,7 @@ import lavalink.server.io.SocketServer.Companion.sendPlayerUpdate
3638
import lavalink.server.player.filters.FilterChain
3739
import moe.kyokobot.koe.MediaConnection
3840
import moe.kyokobot.koe.media.OpusAudioFrameProvider
41+
import java.nio.ByteBuffer
3942
import java.util.concurrent.ScheduledFuture
4043
import java.util.concurrent.TimeUnit
4144

@@ -46,6 +49,9 @@ class LavalinkPlayer(
4649
audioPlayerManager: AudioPlayerManager,
4750
pluginInfoModifiers: List<AudioPluginInfoModifier>
4851
) : AudioEventAdapter(), IPlayer {
52+
private val buffer = ByteBuffer.allocate(StandardAudioDataFormats.DISCORD_OPUS.maximumChunkSize())
53+
private val mutableFrame = MutableAudioFrame().apply { setBuffer(buffer) }
54+
4955
val audioLossCounter = AudioLossCounter()
5056
var endMarkerHit = false
5157
var filters: FilterChain = FilterChain()
@@ -117,21 +123,15 @@ class LavalinkPlayer(
117123
}
118124

119125
private inner class Provider(connection: MediaConnection?) : OpusAudioFrameProvider(connection) {
120-
private var lastFrame: AudioFrame? = null
121-
122-
override fun canProvide(): Boolean {
123-
lastFrame = audioPlayer.provide()
124-
return if (lastFrame == null) {
126+
override fun canProvide() = audioPlayer.provide(mutableFrame).also { provided ->
127+
if (!provided) {
125128
audioLossCounter.onLoss()
126-
false
127-
} else {
128-
true
129129
}
130130
}
131131

132132
override fun retrieveOpusFrame(buf: ByteBuf) {
133133
audioLossCounter.onSuccess()
134-
buf.writeBytes(lastFrame!!.data)
134+
buf.writeBytes(buffer.flip())
135135
}
136136
}
137137
}

0 commit comments

Comments
 (0)