From bb9a4123b0b819ab27a09a25cad95d80d18395bb Mon Sep 17 00:00:00 2001 From: rdkartono Date: Thu, 31 Jul 2025 12:06:02 +0700 Subject: [PATCH] Commit 31/07/2025 --- src/Main.kt | 1 + src/somecodes/Codes.kt | 25 ++++++++++++++++++ src/zello/Event_OnStreamStart.kt | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/Main.kt b/src/Main.kt index cf2673f..02f618a 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -5,6 +5,7 @@ import zello.ZelloEvent //TIP To Run code, press or // click the icon in the gutter. fun main() { + val z = ZelloClient.fromConsumerZello() z.Start(object : ZelloEvent { override fun onChannelStatus( diff --git a/src/somecodes/Codes.kt b/src/somecodes/Codes.kt index 2040bc2..715a027 100644 --- a/src/somecodes/Codes.kt +++ b/src/somecodes/Codes.kt @@ -1,10 +1,35 @@ package somecodes +@Suppress("unused") class Codes { companion object{ fun ValidString(s : String?) : Boolean { return s != null && s.isNotEmpty() && s.isNotBlank() } + + /** + * Codec header for Zello audio packet is base64 encoded string from {samplingrate (16 Little Endian), frames_per_packet, frame_size_ms} + * @param samplingrate Sampling rate in Hz (e.g., 8000, 16000, 32000, etc.) + * @param frames_per_packet Number of frames per packet (usually 1 or 2) + * @param frame_size_ms Size of each frame in milliseconds (e.g., 20, 30, 40, etc.) + */ + fun toCodecHeader(samplingrate: Int, frames_per_packet: Byte, frame_size_ms: Byte) : String{ + val xx = ByteArray(4) + xx[0] = (samplingrate and 0xFF).toByte() // Little Endian + xx[1] = ((samplingrate shr 8) and 0xFF).toByte() + xx[2] = frames_per_packet + xx[3] = frame_size_ms + return java.util.Base64.getEncoder().encodeToString(xx) + } + + fun fromCodecHeader(header: String) : Triple? { + val decoded = java.util.Base64.getDecoder().decode(header) + if (decoded.size != 4) return null + val samplingrate = (decoded[0].toInt() and 0xFF) or ((decoded[1].toInt() and 0xFF) shl 8) + val frames_per_packet = decoded[2] + val frame_size_ms = decoded[3] + return Triple(samplingrate, frames_per_packet, frame_size_ms) + } } } \ No newline at end of file diff --git a/src/zello/Event_OnStreamStart.kt b/src/zello/Event_OnStreamStart.kt index 6455729..39c9a1f 100644 --- a/src/zello/Event_OnStreamStart.kt +++ b/src/zello/Event_OnStreamStart.kt @@ -1,6 +1,8 @@ package zello import com.fasterxml.jackson.annotation.JsonPropertyOrder +import somecodes.Codes.Companion.ValidString +import somecodes.Codes.Companion.fromCodecHeader @Suppress("unused") @JsonPropertyOrder(value = ["command", "type", "codec", "codec_header", "packet_duration", "stream_id", "channel", "from", "for"]) @@ -14,4 +16,46 @@ class Event_OnStreamStart { var channel: String="" var from: String = "" // the user who started the stream var For: String = "" // the user who is receiving the stream, should be "for" not "For" + + /** + * Get Sampling Rate from codec_header. + * @return sampling rate in Hz, or 0 if codec_header is not valid. + */ + fun getSamplingRate() : Int{ + if (ValidString(codec_header)){ + val triple = fromCodecHeader(codec_header) + if (triple!=null){ + return triple.first + } + } + return 0 + } + + /** + * Get number of frames per packet, either 1 or 2 + * @return number of frames per packet, or 0 if codec_header is not valid. + */ + fun getFramesPerSecond() : Byte{ + if (ValidString(codec_header)){ + val triple = fromCodecHeader(codec_header) + if (triple!=null){ + return triple.second + } + } + return 0 + } + + /** + * Get audio frame size in miliseconds + * @return audio frame size in milliseconds, or 0 if codec_header is not valid. + */ + fun getFrameSizePerMs() : Byte{ + if (ValidString(codec_header)){ + val triple = fromCodecHeader(codec_header) + if (triple!=null){ + return triple.third + } + } + return 0 + } } \ No newline at end of file