mirror of
https://github.com/damp11113/IDRB.git
synced 2025-04-27 22:48:09 +00:00
update 1.6.1 r2
change Zlib to LZ4 better settings encoder in server side
This commit is contained in:
parent
221fc180c3
commit
93bfe055db
Binary file not shown.
@ -28,7 +28,7 @@ import zmq
|
|||||||
from pyogg import OpusDecoder
|
from pyogg import OpusDecoder
|
||||||
import configparser
|
import configparser
|
||||||
import ctypes
|
import ctypes
|
||||||
import zlib
|
import lz4.frame
|
||||||
|
|
||||||
from utils import *
|
from utils import *
|
||||||
import appcomponent
|
import appcomponent
|
||||||
@ -40,7 +40,7 @@ class App:
|
|||||||
self.config = configparser.ConfigParser()
|
self.config = configparser.ConfigParser()
|
||||||
self.config.read("config.ini")
|
self.config.read("config.ini")
|
||||||
self.device_name_output = self.config["audio"]["device"]
|
self.device_name_output = self.config["audio"]["device"]
|
||||||
self.buffersize = self.config["network"]["buffersize"]
|
self.buffersize = int(self.config["network"]["buffersize"])
|
||||||
|
|
||||||
self.working = False
|
self.working = False
|
||||||
self.readchannel = 1
|
self.readchannel = 1
|
||||||
@ -396,7 +396,7 @@ class App:
|
|||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
decompressed_data = zlib.decompress(data)
|
decompressed_data = lz4.frame.decompress(data)
|
||||||
|
|
||||||
datadecoded = pickle.loads(decompressed_data)
|
datadecoded = pickle.loads(decompressed_data)
|
||||||
except:
|
except:
|
||||||
|
@ -14,14 +14,12 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with IDRB Project. If not, see <https://www.gnu.org/licenses/>.
|
along with IDRB Project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from pyogg import OpusBufferedEncoder
|
|
||||||
import numpy as np
|
|
||||||
import pyaudio
|
import pyaudio
|
||||||
import RDS as _RDS
|
import RDS as _RDS
|
||||||
import logging
|
import logging
|
||||||
|
import tools
|
||||||
|
|
||||||
EncoderLog = logging.getLogger("Encoder")
|
EncoderLog = logging.getLogger("Encoder")
|
||||||
|
|
||||||
@ -37,64 +35,28 @@ for i in range(p.get_device_count()):
|
|||||||
device_index_input = dev['index']
|
device_index_input = dev['index']
|
||||||
break
|
break
|
||||||
|
|
||||||
device_name_input = "Line 4 (Virtual Audio Cable)"
|
|
||||||
device_index_input2 = 0
|
|
||||||
for i in range(p.get_device_count()):
|
|
||||||
dev = p.get_device_info_by_index(i)
|
|
||||||
if dev['name'] == device_name_input:
|
|
||||||
device_index_input2 = dev['index']
|
|
||||||
break
|
|
||||||
|
|
||||||
streaminput = p.open(format=pyaudio.paInt16, channels=2, rate=48000, input=True, input_device_index=device_index_input)
|
|
||||||
streaminput2 = p.open(format=pyaudio.paInt16, channels=2, rate=48000, input=True, input_device_index=device_index_input2)
|
|
||||||
|
|
||||||
# Create a shared queue for encoded audio packets
|
# Create a shared queue for encoded audio packets
|
||||||
channel1 = Queue()
|
channel1 = Queue()
|
||||||
|
|
||||||
channel2 = Queue()
|
channel2 = Queue()
|
||||||
|
|
||||||
# Function to continuously encode audio and put it into the queue
|
channel1option = {
|
||||||
def encode_audio():
|
"Bitrates": 64000,
|
||||||
encoder = OpusBufferedEncoder()
|
"DeviceInputIndex": device_index_input
|
||||||
encoder.set_application("audio")
|
}
|
||||||
encoder.set_sampling_frequency(_RDS.RDS["ContentInfo"]["samplerates"])
|
|
||||||
encoder.set_channels(_RDS.RDS["ContentInfo"]["channel"])
|
|
||||||
encoder.set_bitrates(_RDS.RDS["ContentInfo"]["bitrate"])
|
|
||||||
encoder.set_frame_size(60)
|
|
||||||
encoder.set_bitrate_mode("VBR")
|
|
||||||
encoder.set_compresion_complex(10)
|
|
||||||
|
|
||||||
while True:
|
channel2option = {
|
||||||
pcm = np.frombuffer(streaminput.read(1024, exception_on_overflow=False), dtype=np.int16)
|
"Bitrates": 18000,
|
||||||
|
"InputWAVFile": "./Samples/audiotest.wav"
|
||||||
|
}
|
||||||
|
|
||||||
encoded_packets = encoder.buffered_encode(memoryview(bytearray(pcm)))
|
|
||||||
for encoded_packet, _, _ in encoded_packets:
|
|
||||||
# Put the encoded audio into the buffer
|
|
||||||
|
|
||||||
channel1.put(encoded_packet.tobytes())
|
EncoderChannel1 = tools.AudioEncoder(_RDS.RDS, channel1option, channel1)
|
||||||
|
|
||||||
def encode_audio2():
|
EncoderChannel2 = tools.AudioEncoder(_RDS.RDS2, channel2option, channel2, "wav")
|
||||||
encoder2 = OpusBufferedEncoder()
|
|
||||||
encoder2.set_application("audio")
|
|
||||||
encoder2.set_sampling_frequency(_RDS.RDS2["ContentInfo"]["samplerates"])
|
|
||||||
encoder2.set_channels(_RDS.RDS2["ContentInfo"]["channel"])
|
|
||||||
encoder2.set_bitrates(_RDS.RDS2["ContentInfo"]["bitrate"])
|
|
||||||
encoder2.set_frame_size(60)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
pcm2 = np.frombuffer(streaminput2.read(1024, exception_on_overflow=False), dtype=np.int16)
|
|
||||||
|
|
||||||
encoded_packets = encoder2.buffered_encode(memoryview(bytearray(pcm2)))
|
|
||||||
for encoded_packet, _, _ in encoded_packets:
|
|
||||||
# Put the encoded audio into the buffer
|
|
||||||
channel2.put(encoded_packet.tobytes())
|
|
||||||
|
|
||||||
#channel2.put(pcm2.tobytes()) # if you use pcm
|
|
||||||
|
|
||||||
def StartEncoder():
|
def StartEncoder():
|
||||||
EncoderLog.info("Starting encoder")
|
EncoderLog.info("Starting encoder")
|
||||||
audio_thread = threading.Thread(target=encode_audio)
|
|
||||||
audio_thread2 = threading.Thread(target=encode_audio2)
|
|
||||||
|
|
||||||
audio_thread.start()
|
EncoderChannel1.startencoder()
|
||||||
audio_thread2.start()
|
EncoderChannel2.startencoder()
|
@ -19,7 +19,7 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from damp11113 import scrollTextBySteps
|
from damp11113.utils import scrollTextBySteps
|
||||||
import threading
|
import threading
|
||||||
import Settings
|
import Settings
|
||||||
import logging
|
import logging
|
||||||
@ -96,12 +96,6 @@ RDS = {
|
|||||||
"AS": [ # AS = Alternative Server
|
"AS": [ # AS = Alternative Server
|
||||||
# can add more server here
|
# can add more server here
|
||||||
],
|
],
|
||||||
"ContentInfo": {
|
|
||||||
"Codec": "opus",
|
|
||||||
"bitrate": 64000,
|
|
||||||
"channel": 2,
|
|
||||||
"samplerates": 48000
|
|
||||||
},
|
|
||||||
"images": {
|
"images": {
|
||||||
"logo": {
|
"logo": {
|
||||||
"lazy": False,
|
"lazy": False,
|
||||||
@ -145,12 +139,6 @@ RDS2 = {
|
|||||||
"AS": [ # AS = Alternative Server
|
"AS": [ # AS = Alternative Server
|
||||||
# can add more server here
|
# can add more server here
|
||||||
],
|
],
|
||||||
"ContentInfo": {
|
|
||||||
"Codec": "Opus",
|
|
||||||
"bitrate": 8000,
|
|
||||||
"channel": 2,
|
|
||||||
"samplerates": 48000
|
|
||||||
},
|
|
||||||
"images": {
|
"images": {
|
||||||
"logo": None
|
"logo": None
|
||||||
}
|
}
|
||||||
@ -194,5 +182,5 @@ def startRDSThread():
|
|||||||
|
|
||||||
thread.start()
|
thread.start()
|
||||||
thread2.start()
|
thread2.start()
|
||||||
thread3.start()
|
#thread3.start()
|
||||||
|
|
||||||
|
BIN
Server/Samples/audiotest.wav
Normal file
BIN
Server/Samples/audiotest.wav
Normal file
Binary file not shown.
@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with IDRB Project. If not, see <https://www.gnu.org/licenses/>.
|
along with IDRB Project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import lz4.frame
|
||||||
|
|
||||||
# To config Muxer you need to goto server.py and find "Config Muxer"
|
# To config Muxer you need to goto server.py and find "Config Muxer"
|
||||||
# To config RDS you need to goto RDS.py
|
# To config RDS you need to goto RDS.py
|
||||||
# To config Encoder you need to goto Encoder.py
|
# To config Encoder you need to goto Encoder.py
|
||||||
@ -23,7 +25,7 @@ along with IDRB Project. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
# Server Settings
|
# Server Settings
|
||||||
protocol = "ZMQ_WS" # TCP ZMQ ZMQ_WS
|
protocol = "ZMQ_WS" # TCP ZMQ ZMQ_WS
|
||||||
server_port = ('*', 6980) # if use other protocol ZMQ please use 0.0.0.0
|
server_port = ('*', 6980) # if use other protocol ZMQ please use 0.0.0.0
|
||||||
compression_level = 9 # 0-9
|
compression_level = lz4.frame.COMPRESSIONLEVEL_MAX
|
||||||
buffersize = 32 # must be int (on working it use buffersize + (buffersize/2) to standby)
|
buffersize = 32 # must be int (on working it use buffersize + (buffersize/2) to standby)
|
||||||
# low buffersize = low delay
|
# low buffersize = low delay
|
||||||
|
|
||||||
@ -42,13 +44,9 @@ If you want your server to be listed publicly on ThaiSDR Directory, following th
|
|||||||
6. copy api key
|
6. copy api key
|
||||||
|
|
||||||
"""
|
"""
|
||||||
public = True
|
public = False
|
||||||
#ServerIP = "IDRB.damp11113.xyz" # do not add protocol before ip
|
#ServerIP = "IDRB.damp11113.xyz" # do not add protocol before ip
|
||||||
ServerIP = "localhost"
|
ServerIP = "localhost"
|
||||||
#ServerPort = server_port[1]
|
#ServerPort = server_port[1]
|
||||||
ServerPort = 6980
|
ServerPort = 6980
|
||||||
<<<<<<< HEAD
|
ThaiSDRkey = "1N5LURICLIN1U9QNYZ4MHJ6FNXISFXFELZAX135CFM0HSD17O2.63E60BE9EEA2339C113A15EB"
|
||||||
ThaiSDRkey = ""
|
|
||||||
=======
|
|
||||||
ThaiSDRkey = ""
|
|
||||||
>>>>>>> 53163be3dc12010715271f6020a0c07d2af720ec
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Server/__pycache__/tools.cpython-310.pyc
Normal file
BIN
Server/__pycache__/tools.cpython-310.pyc
Normal file
Binary file not shown.
@ -21,9 +21,13 @@ import pickle
|
|||||||
import threading
|
import threading
|
||||||
import zmq
|
import zmq
|
||||||
import logging
|
import logging
|
||||||
import zlib
|
import lz4.frame
|
||||||
import queue
|
import queue
|
||||||
import math
|
import math
|
||||||
|
import os
|
||||||
|
|
||||||
|
os.environ["damp11113_load_all_module"] = "NO"
|
||||||
|
os.environ["damp11113_check_update"] = "NO"
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
|
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
|
||||||
ServerLog = logging.getLogger("IDRBServer")
|
ServerLog = logging.getLogger("IDRBServer")
|
||||||
@ -88,8 +92,10 @@ def Muxer():
|
|||||||
# ENC1encrypted, ENC1salt, ENC1iv = utils.encrypt_data(ENchannel1, "password")
|
# ENC1encrypted, ENC1salt, ENC1iv = utils.encrypt_data(ENchannel1, "password")
|
||||||
|
|
||||||
# ENchannel1 = ENC1encrypted + b'|||||' + ENC1salt + b'|||||' + ENC1iv
|
# ENchannel1 = ENC1encrypted + b'|||||' + ENC1salt + b'|||||' + ENC1iv
|
||||||
|
try:
|
||||||
ENchannel2 = Encoder.channel2.get()
|
ENchannel2 = Encoder.channel2.get()
|
||||||
|
except:
|
||||||
|
ENchannel2 = b""
|
||||||
content = {
|
content = {
|
||||||
"first": False,
|
"first": False,
|
||||||
"mainchannel": 1,
|
"mainchannel": 1,
|
||||||
@ -119,7 +125,7 @@ def Muxer():
|
|||||||
}
|
}
|
||||||
ThaiSDRDir.content = content
|
ThaiSDRDir.content = content
|
||||||
|
|
||||||
compressedcontent = zlib.compress(pickle.dumps(content), level=Settings.compression_level)
|
compressedcontent = lz4.frame.compress(pickle.dumps(content), compression_level=Settings.compression_level)
|
||||||
|
|
||||||
Buffer.put(compressedcontent)
|
Buffer.put(compressedcontent)
|
||||||
|
|
||||||
|
141
Server/tools.py
Normal file
141
Server/tools.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
"""
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
from pyogg import OpusBufferedEncoder
|
||||||
|
import numpy as np
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import threading
|
||||||
|
from damp11113.randoms import rannum
|
||||||
|
|
||||||
|
class AudioEncoder:
|
||||||
|
def __init__(self, RDS: dict, option: dict, queuebuffer, audioinputtype="device", audiocodec="opus", standalone=False):
|
||||||
|
self.audiocodec = audiocodec.upper()
|
||||||
|
self.audioinput = audioinputtype.upper()
|
||||||
|
self.RDS = RDS
|
||||||
|
self.option = option
|
||||||
|
self.buffer = queuebuffer
|
||||||
|
|
||||||
|
self.running = False
|
||||||
|
self.encoder = None
|
||||||
|
self.standalone = standalone
|
||||||
|
|
||||||
|
self.runningthread = threading.Thread(target=self._running)
|
||||||
|
self.sourceinput = None
|
||||||
|
|
||||||
|
self._temp_ip = None
|
||||||
|
self._temp_port = None
|
||||||
|
|
||||||
|
self._temp_socket = None
|
||||||
|
|
||||||
|
def _createencoder(self):
|
||||||
|
self.RDS.update({
|
||||||
|
"ContentInfo": {
|
||||||
|
"Codec": self.audiocodec,
|
||||||
|
"bitrate": self.option.get("Bitrates", 64000),
|
||||||
|
"channel": self.option.get("Channel", 2),
|
||||||
|
"samplerates": self.option.get("SamplesRates", 48000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.audiocodec == "PCM":
|
||||||
|
pass
|
||||||
|
elif self.audiocodec == "OPUS":
|
||||||
|
self.encoder = OpusBufferedEncoder()
|
||||||
|
self.encoder.set_application("audio")
|
||||||
|
self.encoder.set_sampling_frequency(self.option.get("SamplesRates", 48000))
|
||||||
|
self.encoder.set_channels(self.option.get("Channel", 2))
|
||||||
|
self.encoder.set_bitrates(self.option.get("Bitrates", 64000))
|
||||||
|
self.encoder.set_frame_size(self.option.get("opusFrameSize", 60))
|
||||||
|
self.encoder.set_bitrate_mode(self.option.get("BitrateMode", "VBR").upper())
|
||||||
|
self.encoder.set_compresion_complex(self.option.get("opusCompressionLevel", 10))
|
||||||
|
|
||||||
|
def _createaudiosource(self):
|
||||||
|
if self.audioinput == "DEVICE":
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
self.sourceinput = p.open(format=pyaudio.paInt16, channels=self.option.get("Channel", 2), rate=self.option.get("SamplesRates", 48000), input=True, input_device_index=self.option.get("DeviceInputIndex", 0))
|
||||||
|
elif self.audioinput == "WAV":
|
||||||
|
file = self.option.get("InputWAVFile", "")
|
||||||
|
if file == "":
|
||||||
|
raise "No Audio wav Input Please use option 'InputWAVFile' to set wav file path"
|
||||||
|
self.sourceinput = wave.open(file, "rb")
|
||||||
|
elif self.audioinput == "SOCKET":
|
||||||
|
IP = self.option.get("InputSocketIP", "0.0.0.0")
|
||||||
|
Port = self.option.get("InputSocketPort", rannum(12000, 12300))
|
||||||
|
|
||||||
|
self._temp_ip = IP
|
||||||
|
self._temp_port = Port
|
||||||
|
|
||||||
|
self._temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self._temp_socket.bind((IP, Port))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise "No Audio Device Supported"
|
||||||
|
|
||||||
|
def _running(self):
|
||||||
|
framesize = self.option.get("InputFrameSize", 1024)
|
||||||
|
samplesrates = self.option.get("SamplesRates", 48000)
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
# get audio
|
||||||
|
if self.audioinput == "DEVICE":
|
||||||
|
pcm = np.frombuffer(self.sourceinput.read(framesize, exception_on_overflow=False), dtype=np.int16)
|
||||||
|
elif self.audioinput == "WAV":
|
||||||
|
pcm = np.frombuffer(self.sourceinput.readframes(framesize * 2), dtype=np.int16)
|
||||||
|
if len(pcm) == 0:
|
||||||
|
self.sourceinput.rewind()
|
||||||
|
|
||||||
|
if self.standalone:
|
||||||
|
time.sleep(framesize / samplesrates)
|
||||||
|
|
||||||
|
elif self.audioinput == "SOCKET":
|
||||||
|
pcm = np.frombuffer(self.sourceinput.recv(framesize), dtype=np.int16)
|
||||||
|
else:
|
||||||
|
raise "no input data support"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# encode audio
|
||||||
|
if self.audiocodec == "PCM":
|
||||||
|
self.buffer.put(pcm.tobytes())
|
||||||
|
elif self.audiocodec == "OPUS":
|
||||||
|
encoded_packets = self.encoder.buffered_encode(memoryview(bytearray(pcm)))
|
||||||
|
for encoded_packet, _, _ in encoded_packets:
|
||||||
|
self.buffer.put(encoded_packet.tobytes())
|
||||||
|
|
||||||
|
|
||||||
|
def startencoder(self):
|
||||||
|
self._createaudiosource()
|
||||||
|
self._createencoder()
|
||||||
|
|
||||||
|
if self.audioinput == "SOCKET":
|
||||||
|
self._temp_socket.listen(2)
|
||||||
|
print(f"[Socket Input Beta] {self.__class__.__name__} audio incoming server is running on {self._temp_ip}:{self._temp_port}. (PCM Data only)")
|
||||||
|
print(f"[Socket Input Beta] {self.__class__.__name__} waiting for data for start encoder")
|
||||||
|
self.sourceinput, addr = self._temp_socket.accept()
|
||||||
|
print(f"[Socket Input Beta] {self.__class__.__name__} {addr} is connected. encoder is starting")
|
||||||
|
|
||||||
|
self._temp_socket.settimeout(self.option.get("InputSocketTimeout", 0.1))
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
self.runningthread.start()
|
||||||
|
|
||||||
|
|
||||||
|
def stopencoder(self):
|
||||||
|
self.running = False
|
||||||
|
self.runningthread.join()
|
Loading…
x
Reference in New Issue
Block a user