Skip to content

Commit

Permalink
move default audioframesender impl into separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
lost-illusi0n committed Sep 7, 2021
1 parent 8b290d9 commit c3537d5
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 65 deletions.
70 changes: 5 additions & 65 deletions voice/src/main/kotlin/udp/AudioFrameSender.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
@file:OptIn(KordVoice::class)
@file:Suppress("ArrayInDataClass")

package dev.kord.voice.udp

import dev.kord.common.annotation.KordVoice
import dev.kord.voice.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.transform
import mu.KotlinLogging
import kotlin.coroutines.CoroutineContext
import kotlin.random.Random

private val audioFrameSenderLogger = KotlinLogging.logger { }
import dev.kord.voice.AudioProvider
import dev.kord.voice.FrameInterceptor
import dev.kord.voice.FrameInterceptorContext
import dev.kord.voice.FrameInterceptorContextBuilder
import kotlinx.coroutines.CoroutineScope

@KordVoice
data class AudioFrameSenderConfiguration(
Expand All @@ -32,57 +25,4 @@ interface AudioFrameSender : CoroutineScope {
* send them to Discord.
*/
suspend fun start(configuration: AudioFrameSenderConfiguration)
}

@KordVoice
data class DefaultAudioFrameSenderData(
val udp: VoiceUdpConnection,
val dispatcher: CoroutineDispatcher
)

@KordVoice
class DefaultAudioFrameSender(
val data: DefaultAudioFrameSenderData
) : AudioFrameSender {
override val coroutineContext: CoroutineContext =
SupervisorJob() + data.dispatcher + CoroutineName("Default Audio Frame Poller")

private fun createFrameInterceptor(configuration: AudioFrameSenderConfiguration): FrameInterceptor =
with(configuration) {
val builder = baseFrameInterceptorContext
builder.ssrc = ssrc
return interceptorFactory(builder.build()) // we should assume that everything else is set before-hand in the base builder
}

override suspend fun start(configuration: AudioFrameSenderConfiguration) = with(configuration) {
val interceptor = createFrameInterceptor(configuration)
var sequence: UShort = Random.nextBits(UShort.SIZE_BITS).toUShort()

val frames = Channel<AudioFrame?>(Channel.RENDEZVOUS)
with(provider) { launch { provideFrames(frames) } }

audioFrameSenderLogger.trace { "audio poller starting." }

try {
frames.receiveAsFlow()
.transform { emit(interceptor.intercept(it)) }
.collect { frame ->
if (frame != null) {
val encryptedPacket = AudioPacket.DecryptedPacket(
sequence = sequence,
timestamp = sequence * 960u,
ssrc = ssrc,
decryptedData = frame.data
).encrypt(key)

data.udp.send(encryptedPacket.asByteReadPacket())
sequence++
}
}
} catch (e: Exception) {
/* we're done polling, nothing to worry about */
}

audioFrameSenderLogger.trace { "udp connection closed, stopped polling for audio frames." }
}
}
75 changes: 75 additions & 0 deletions voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package udp

import dev.kord.common.annotation.KordVoice
import dev.kord.voice.AudioFrame
import dev.kord.voice.FrameInterceptor
import dev.kord.voice.udp.AudioFrameSender
import dev.kord.voice.udp.AudioFrameSenderConfiguration
import dev.kord.voice.udp.AudioPacket
import dev.kord.voice.udp.VoiceUdpConnection
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch
import mu.KotlinLogging
import kotlin.coroutines.CoroutineContext
import kotlin.random.Random

private val audioFrameSenderLogger = KotlinLogging.logger { }

@KordVoice
data class DefaultAudioFrameSenderData(
val udp: VoiceUdpConnection,
val dispatcher: CoroutineDispatcher
)

@KordVoice
class DefaultAudioFrameSender(
val data: DefaultAudioFrameSenderData
) : AudioFrameSender {
override val coroutineContext: CoroutineContext =
SupervisorJob() + data.dispatcher + CoroutineName("Default Audio Frame Poller")

private fun createFrameInterceptor(configuration: AudioFrameSenderConfiguration): FrameInterceptor =
with(configuration) {
val builder = baseFrameInterceptorContext
builder.ssrc = ssrc
return interceptorFactory(builder.build()) // we should assume that everything else is set before-hand in the base builder
}

override suspend fun start(configuration: AudioFrameSenderConfiguration) = with(configuration) {
val interceptor = createFrameInterceptor(configuration)
var sequence: UShort = Random.nextBits(UShort.SIZE_BITS).toUShort()

val frames = Channel<AudioFrame?>(Channel.RENDEZVOUS)
with(provider) { launch { provideFrames(frames) } }

audioFrameSenderLogger.trace { "audio poller starting." }

try {
frames.receiveAsFlow()
.transform { emit(interceptor.intercept(it)) }
.collect { frame ->
if (frame != null) {
val encryptedPacket = AudioPacket.DecryptedPacket(
sequence = sequence,
timestamp = sequence * 960u,
ssrc = ssrc,
decryptedData = frame.data
).encrypt(key)

data.udp.send(encryptedPacket.asByteReadPacket())
sequence++
}
}
} catch (e: Exception) {
/* we're done polling, nothing to worry about */
}

audioFrameSenderLogger.trace { "udp connection closed, stopped polling for audio frames." }
}
}

0 comments on commit c3537d5

Please sign in to comment.