Skip to content

Commit

Permalink
Allow usage of non-allocating frame buffers (#1095)
Browse files Browse the repository at this point in the history
* Add option for enabling non-allocating framebuffers.

* Accommodate frame buffer changes.
  • Loading branch information
devoxin committed Aug 20, 2024
1 parent 4384612 commit 21bdd69
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 9 deletions.
1 change: 1 addition & 0 deletions LavalinkServer/application.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ lavalink:
rotation: true
channelMix: true
lowPass: true
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)
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.
frameBufferDurationMs: 5000 # How many milliseconds of audio to keep buffered
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.sedmelluq.discord.lavaplayer.source.soundcloud.*
import com.sedmelluq.discord.lavaplayer.source.twitch.TwitchStreamAudioSourceManager
import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager
import com.sedmelluq.discord.lavaplayer.track.playback.NonAllocatingAudioFrameBuffer
import com.sedmelluq.lava.extensions.youtuberotator.YoutubeIpRotatorSetup
import com.sedmelluq.lava.extensions.youtuberotator.planner.*
import com.sedmelluq.lava.extensions.youtuberotator.tools.ip.Ipv4Block
Expand Down Expand Up @@ -55,6 +56,11 @@ class AudioPlayerConfiguration {
audioPlayerManager.enableGcMonitoring()
}

if (serverConfig.isNonAllocatingFrameBuffer) {
log.info("Using a non-allocating frame buffer")
audioPlayerManager.configuration.setFrameBufferFactory(::NonAllocatingAudioFrameBuffer)
}

val defaultFrameBufferDuration = audioPlayerManager.frameBufferDuration
serverConfig.frameBufferDurationMs?.let {
if (it < 200) { // At the time of writing, LP enforces a minimum of 200ms.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import org.springframework.stereotype.Component
@Component
class ServerConfig {
var password: String? = null
var isNonAllocatingFrameBuffer = false
var bufferDurationMs: Int? = null
var frameBufferDurationMs: Int? = null
var opusEncodingQuality: Int? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
*/
package lavalink.server.player

import com.sedmelluq.discord.lavaplayer.format.StandardAudioDataFormats
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame
import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame
import dev.arbjerg.lavalink.api.AudioPluginInfoModifier
import dev.arbjerg.lavalink.api.IPlayer
import io.netty.buffer.ByteBuf
Expand All @@ -36,6 +38,7 @@ import lavalink.server.io.SocketServer.Companion.sendPlayerUpdate
import lavalink.server.player.filters.FilterChain
import moe.kyokobot.koe.MediaConnection
import moe.kyokobot.koe.media.OpusAudioFrameProvider
import java.nio.ByteBuffer
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

Expand All @@ -46,6 +49,9 @@ class LavalinkPlayer(
audioPlayerManager: AudioPlayerManager,
pluginInfoModifiers: List<AudioPluginInfoModifier>
) : AudioEventAdapter(), IPlayer {
private val buffer = ByteBuffer.allocate(StandardAudioDataFormats.DISCORD_OPUS.maximumChunkSize())
private val mutableFrame = MutableAudioFrame().apply { setBuffer(buffer) }

val audioLossCounter = AudioLossCounter()
var endMarkerHit = false
var filters: FilterChain = FilterChain()
Expand Down Expand Up @@ -117,21 +123,15 @@ class LavalinkPlayer(
}

private inner class Provider(connection: MediaConnection?) : OpusAudioFrameProvider(connection) {
private var lastFrame: AudioFrame? = null

override fun canProvide(): Boolean {
lastFrame = audioPlayer.provide()
return if (lastFrame == null) {
override fun canProvide() = audioPlayer.provide(mutableFrame).also { provided ->
if (!provided) {
audioLossCounter.onLoss()
false
} else {
true
}
}

override fun retrieveOpusFrame(buf: ByteBuf) {
audioLossCounter.onSuccess()
buf.writeBytes(lastFrame!!.data)
buf.writeBytes(buffer.flip())
}
}
}

0 comments on commit 21bdd69

Please sign in to comment.