diff --git a/Client/__pycache__/appcomponent.cpython-310.pyc b/Client/__pycache__/appcomponent.cpython-310.pyc
index 89146bb..bfe064c 100644
Binary files a/Client/__pycache__/appcomponent.cpython-310.pyc and b/Client/__pycache__/appcomponent.cpython-310.pyc differ
diff --git a/Client/__pycache__/utils.cpython-310.pyc b/Client/__pycache__/utils.cpython-310.pyc
index 23aa5d9..a8ee467 100644
Binary files a/Client/__pycache__/utils.cpython-310.pyc and b/Client/__pycache__/utils.cpython-310.pyc differ
diff --git a/Client/appcomponent.py b/Client/appcomponent.py
index 2fd8dcb..f26a6b6 100644
--- a/Client/appcomponent.py
+++ b/Client/appcomponent.py
@@ -1,3 +1,20 @@
+"""
+This file is part of IDRB Project.
+
+IDRB Project is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+IDRB Project is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IDRB Project. If not, see .
+"""
+
import dearpygui.dearpygui as dpg
from utils import *
@@ -60,7 +77,7 @@ def window(self):
dpg.add_spacer()
dpg.add_text("IDRB (Internet Digital Radio Broadcasting System) Client")
dpg.add_spacer()
- dpg.add_text(f"IDRB Client v1.6.1 Beta")
+ dpg.add_text(f"IDRB Client v1.6.2 Beta")
dpg.add_spacer()
desc = "IDRB is a novel internet radio broadcasting alternative that uses HLS/DASH/HTTP streams, transferring over TCP/IP. This system supports images and RDS (Dynamic update) capabilities, enabling the transmission of station information. Additionally, it allows for setting station logos and images. IDRB offers multi-broadcasting functionalities and currently supports the Opus codec, with plans to incorporate PCM, MP2/3, AAC/AAC+, and more in the future, ensuring low delay. If you find this project intriguing, you can support it at damp11113.xyz/support."
@@ -148,8 +165,10 @@ def window(self):
dpg.add_text("Please restart software when configured")
with dpg.tab_bar():
with dpg.tab(label="Audio"):
- dpg.add_combo([], label="Output Device", tag="selectaudiooutputdevicecombo",
- callback=self.changeaudiodevice)
+ dpg.add_combo([], label="Output Device", tag="selectaudiooutputdevicecombo", callback=self.changeaudiodevice)
+ with dpg.tab(label="Network"):
+ dpg.add_input_int(label="Buffer Size", tag="buffersizeintinput", callback=self.changebuffersize)
+
def menubar(self):
with dpg.viewport_menu_bar():
diff --git a/Client/client.py b/Client/client.py
index eeff736..74a63e5 100644
--- a/Client/client.py
+++ b/Client/client.py
@@ -40,8 +40,7 @@ class App:
self.config = configparser.ConfigParser()
self.config.read("config.ini")
self.device_name_output = self.config["audio"]["device"]
- self.buffersize = 64 # can configable
-
+ self.buffersize = self.config["network"]["buffersize"]
self.working = False
self.readchannel = 1
@@ -60,8 +59,6 @@ class App:
self.lsitem = None
self.ccconwithpubselect = False
self.buffer = queue.Queue(maxsize=self.buffersize)
- self.okbuffer = False
- self.firstrunbuffer = True
def connecttoserverwithpubselect(self, sender, data):
self.ccconwithpubselect = True
@@ -125,8 +122,6 @@ class App:
dpg.configure_item("station_logo_config", show=False)
dpg.configure_item("RDSinfo", show=False)
dpg.configure_item("disconnectbutton", show=False)
- dpg.configure_item("connectservergroup", show=True)
- dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
dpg.configure_item("logostatus", show=False)
self.firstrun = True
self.firststart = True
@@ -136,9 +131,7 @@ class App:
self.ccisdecryptpassword = None
self.cciswaitlogoim = True
self.ccthreadlogorecisworking = False
- self.buffer = queue.Queue(maxsize=self.buffersize)
- self.okbuffer = False
- self.firstrunbuffer = True
+ # clear buffer
def RDSshow(self):
try:
@@ -231,6 +224,11 @@ class App:
self.config["audio"]["device"] = dpg.get_value(sender)
self.config.write(open('config.ini', 'w'))
+ def changebuffersize(self, sender, data):
+ self.buffersize = int(dpg.get_value(sender))
+ self.config["network"]["buffersize"] = str(dpg.get_value(sender))
+ self.config.write(open('config.ini', 'w'))
+
def pubserverselectone(self, sender, data):
if data == False:
dpg.configure_item("connectbuttonpubserverselect", show=False)
@@ -295,25 +293,33 @@ class App:
self.pubserverselectsearch()
def streambuffer(self, socket):
- consecutive_above_threshold = 0 # Counter to track consecutive iterations above threshold
- tolerance_iterations = 5 # Number of consecutive iterations required above threshold
while self.working:
- if self.cprotocol == "TCP":
- tempdata = b''
- # data = socket.recv(1580152)
- while True:
- part = socket.recv(1024)
- tempdata += part
- if len(part) < 1024:
- # either 0 or end of data
- break
- self.buffer.put(tempdata)
- elif self.cprotocol == "ZeroMQ":
- self.buffer.put(socket.recv())
- else:
- self.buffer.put(b"")
+ try:
+ if self.cprotocol == "TCP":
+ tempdata = b''
+ # data = socket.recv(1580152)
+ while True:
+ part = socket.recv(1024)
+ tempdata += part
+ if len(part) < 1024:
+ # either 0 or end of data
+ break
+ self.buffer.put(tempdata, timeout=0.1)
+ elif self.cprotocol == "ZeroMQ":
+ self.buffer.put(socket.recv(), timeout=0.1)
+ else:
+ self.buffer.put(b"")
- dpg.configure_item("bufferstatus", default_value=f'Buffer: {self.buffer.qsize()}/{self.buffersize}')
+ dpg.configure_item("bufferstatus", default_value=f'Buffer: {self.buffer.qsize()}/{self.buffersize}', color=(0, 255, 0))
+ except queue.Full:
+ dpg.configure_item("bufferstatus", default_value=f'Buffer: {self.buffer.qsize()}/{self.buffersize}', color=(255, 0, 0))
+
+ while not self.buffer.empty():
+ self.buffer.get()
+
+ dpg.configure_item("bufferstatus", default_value=f'Buffer: {self.buffer.qsize()}/{self.buffersize}', color=(255, 255, 0))
+ dpg.configure_item("serverstatus", default_value='disconnected', color=(255, 0, 0))
+ dpg.configure_item("connectservergroup", show=True)
def stream(self, socket):
opus_decoder = None
@@ -339,6 +345,7 @@ class App:
if self.buffer.not_empty:
data = self.buffer.get()
else:
+ dpg.configure_item("serverstatus", default_value='Buffering...', color=(255, 255, 0))
continue
bytesconunt += len(data)
@@ -589,7 +596,7 @@ class App:
ctypes.CDLL("opus.dll")
dpg.create_context()
- dpg.create_viewport(title=f'IDRB Client v1.6.1 Beta', width=1280, height=720, large_icon="IDRBfavicon.ico", clear_color=(43, 45, 48)) # set viewport window
+ dpg.create_viewport(title=f'IDRB Client v1.6.2 Beta', width=1280, height=720, large_icon="IDRBfavicon.ico", clear_color=(43, 45, 48)) # set viewport window
dpg.setup_dearpygui()
# -------------- add code here --------------
noimage_texture_data = []
@@ -621,6 +628,7 @@ class App:
output_devices.append(device_info['name'])
dpg.configure_item("selectaudiooutputdevicecombo", items=output_devices, default_value=self.config["audio"]["device"])
+ dpg.configure_item("buffersizeintinput", default_value=int(self.config["network"]["buffersize"]))
# -------------------------------------------
dpg.show_viewport()
diff --git a/Client/config.ini b/Client/config.ini
index 06777f3..28de1f7 100644
--- a/Client/config.ini
+++ b/Client/config.ini
@@ -1,6 +1,9 @@
[audio]
device = Speakers (2- USB Audio DAC )
+[network]
+buffersize = 128
+
[debug]
hideconsole = true
diff --git a/Client/utils.py b/Client/utils.py
index 6bf6f06..ec012aa 100644
--- a/Client/utils.py
+++ b/Client/utils.py
@@ -1,3 +1,20 @@
+"""
+This file is part of IDRB Project.
+
+IDRB Project is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+IDRB Project is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IDRB Project. If not, see .
+"""
+
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import scrypt
import numpy as np
diff --git a/Server/Settings.py b/Server/Settings.py
index c2bd6de..4d1dac3 100644
--- a/Server/Settings.py
+++ b/Server/Settings.py
@@ -15,10 +15,17 @@ You should have received a copy of the GNU General Public License
along with IDRB Project. If not, see .
"""
+# To config Muxer you need to goto server.py and find "Config Muxer"
+# To config RDS you need to goto RDS.py
+# To config Encoder you need to goto Encoder.py
+# Do not goto ThaiSDRDir.py and utils.py
+
# Server Settings
protocol = "ZMQ_WS" # TCP ZMQ ZMQ_WS
server_port = ('*', 6980) # if use other protocol ZMQ please use 0.0.0.0
compression_level = 9 # 0-9
+buffersize = 32 # must be int (on working it use buffersize + (buffersize/2) to standby)
+# low buffersize = low delay
# Server Info
ServerName = "DPCloudev"
@@ -40,4 +47,4 @@ public = True
ServerIP = "localhost"
#ServerPort = server_port[1]
ServerPort = 6980
-ThaiSDRkey = "1N5LURICLIN1U9QNYZ4MHJ6FNXISFXFELZAX135CFM0HSD17O2.63E60BE9EEA2339C113A15EB"
\ No newline at end of file
+ThaiSDRkey = ""
\ No newline at end of file
diff --git a/Server/__pycache__/Settings.cpython-310.pyc b/Server/__pycache__/Settings.cpython-310.pyc
index 31dc15f..4533904 100644
Binary files a/Server/__pycache__/Settings.cpython-310.pyc and b/Server/__pycache__/Settings.cpython-310.pyc differ
diff --git a/Server/server.py b/Server/server.py
index 48de425..703d29b 100644
--- a/Server/server.py
+++ b/Server/server.py
@@ -22,6 +22,8 @@ import threading
import zmq
import logging
import zlib
+import queue
+import math
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
ServerLog = logging.getLogger("IDRBServer")
@@ -61,7 +63,6 @@ _RDS.startRDSThread()
ServerLog.info('starting audio encoding')
Encoder.StartEncoder()
-
if protocol == "TCP":
connected_users = 0
elif protocol == "ZMQ":
@@ -74,78 +75,98 @@ timestart = time.time()
connectionlist = []
first = True
+Buffer = queue.Queue(maxsize=math.trunc(Settings.buffersize + (Settings.buffersize/2)))
+
+# ---------------------------------- Config Muxer ---------------------------------------
+
+def Muxer():
+ while True:
+ # Get the encoded audio from the buffer
+ ENchannel1 = Encoder.channel1.get()
+
+ # encrypt data
+ # ENC1encrypted, ENC1salt, ENC1iv = utils.encrypt_data(ENchannel1, "password")
+
+ # ENchannel1 = ENC1encrypted + b'|||||' + ENC1salt + b'|||||' + ENC1iv
+
+ ENchannel2 = Encoder.channel2.get()
+ content = {
+ "first": False,
+ "mainchannel": 1,
+ "channel": {
+ 1: {
+ "Station": "DPRadio+",
+ "StationDesc": "The best station in the world!",
+ "Encrypt": b'|||||' in ENchannel1, # check if encrypt
+ "ContentSize": len(ENchannel1),
+ "Content": ENchannel1,
+ "RDS": _RDS.RDS
+ },
+ 2: {
+ "Station": "DPTest",
+ "StationDesc": "",
+ "Encrypt": b'|||||' in ENchannel2,
+ "ContentSize": len(ENchannel2),
+ "Content": ENchannel2,
+ "RDS": _RDS.RDS2
+ }
+ },
+ "serverinfo": {
+ "Listener": connected_users,
+ "Startat": timestart,
+ "RDS": _RDS.ServerRDS
+ }
+ }
+ ThaiSDRDir.content = content
+
+ compressedcontent = zlib.compress(pickle.dumps(content), level=Settings.compression_level)
+
+ Buffer.put(compressedcontent)
+
+# -----------------------------------------------------------------------------------------------
+
def handle_client():
global connected_users, first
try:
while True:
- # Get the encoded audio from the buffer
- ENchannel1 = Encoder.channel1.get()
-
- # encrypt data
- #ENC1encrypted, ENC1salt, ENC1iv = utils.encrypt_data(ENchannel1, "password")
-
- #ENchannel1 = ENC1encrypted + b'|||||' + ENC1salt + b'|||||' + ENC1iv
-
- ENchannel2 = Encoder.channel2.get()
- content = {
- "first": False,
- "mainchannel": 1,
- "channel": {
- 1: {
- "Station": "DPRadio+",
- "StationDesc": "The best station in the world!",
- "Encrypt": b'|||||' in ENchannel1, # check if encrypt
- "ContentSize": len(ENchannel1),
- "Content": ENchannel1,
- "RDS": _RDS.RDS
- },
- 2: {
- "Station": "DPTest",
- "StationDesc": "",
- "Encrypt": b'|||||' in ENchannel2,
- "ContentSize": len(ENchannel2),
- "Content": ENchannel2,
- "RDS": _RDS.RDS2
- }
- },
- "serverinfo": {
- "Listener": connected_users,
- "Startat": timestart,
- "RDS": _RDS.ServerRDS
- }
- }
- ThaiSDRDir.content = content
-
- compressedcontent = zlib.compress(pickle.dumps(content), level=Settings.compression_level)
-
- #connection.sendall(pickle.dumps(content))
- if protocol == "TCP":
- for i in connectionlist:
- try:
- i.sendall(compressedcontent)
- except Exception as e:
- #print(f'Error sending data to {i.getpeername()}: {e}')
- # Remove disconnected client from the list
- if i in connectionlist:
- i.close()
- connectionlist.remove(i)
- connected_users -= 1
- # check if no user
- if not connectionlist:
- first = True
- ServerLog.info('server is standby now')
- break
- elif protocol == "ZMQ":
- s.send(compressedcontent)
+ # Check if the buffer queue has enough data to send
+ if Buffer.qsize() >= Settings.buffersize: # Adjust the threshold as needed
+ if protocol == "TCP":
+ for i in connectionlist:
+ try:
+ # Send data from the buffer queue to connected clients
+ for _ in range(Settings.buffersize):
+ i.sendall(Buffer.get())
+ except Exception as e:
+ if i in connectionlist:
+ i.close()
+ connectionlist.remove(i)
+ connected_users -= 1
+ if not connectionlist:
+ first = True
+ ServerLog.info('server is standby now')
+ break
+ elif protocol == "ZMQ":
+ # Send data from the buffer queue to ZMQ socket
+ for _ in range(Settings.buffersize):
+ s.send(Buffer.get())
except Exception as e:
print(f'Error: {e}')
+
# Your main server logic using threading for handling connections
if __name__ == "__main__":
if public:
ServerLog.info('starting ThaiSDR Directory')
ThaiSDRDir.run()
- ServerLog.info('server is running')
+
+ ServerLog.info('starting Muxer')
+
+ muxerthread = threading.Thread(target=Muxer)
+ muxerthread.start()
+
+ ServerLog.info('starting server')
+
if protocol == "TCP":
while True:
connection, client_address = s.accept()
@@ -162,4 +183,6 @@ if __name__ == "__main__":
elif protocol == "ZMQ":
client_thread = threading.Thread(target=handle_client)
# client_thread.daemon = True # Set the thread as a daemon so it exits when the main thread exits
- client_thread.start()
\ No newline at end of file
+ client_thread.start()
+
+ ServerLog.info('server is running')
\ No newline at end of file