diff --git a/benscript.sh b/benscript.sh new file mode 100755 index 0000000..4ac2fca --- /dev/null +++ b/benscript.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +set -eux + +#./dependencies/mediamtx/mediamtx & +#./mediamtx.yml & + + +counter=1 +while [ $counter -le 10 ]; do + ffmpeg -re -stream_loop -1 -i ./data/file.wav -c copy -f rtsp rtsp://localhost:8554/ben/${counter} & + ((counter++)) + sleep 2.1 +done + + +#ffmpeg -re -stream_loop -1 -i file.ts -c copy -f rtsp rtsp://localhost:8554/mystreams diff --git a/calling.py b/calling.py new file mode 100644 index 0000000..bc0c52b --- /dev/null +++ b/calling.py @@ -0,0 +1,46 @@ +from pyVoIP.VoIP import VoIPPhone, InvalidStateError, CallState +import time +import wave + + +def answer(call): + print("Call answered!") + + try: + f = wave.open("data/sampleaudio/BabyElephantWalk60.wav", "rb") + frames = f.getnframes() + data = f.readframes(frames) + f.close() + + call.answer() + call.write_audio( + data + ) # This writes the audio data to the transmit buffer, this must be bytes. + + stop = time.time() + ( + frames / 8000 + ) # frames/8000 is the length of the audio in seconds. 8000 is the hertz of PCMU. + + while time.time() <= stop and call.state == CallState.ANSWERED: + time.sleep(0.1) + call.hangup() + except InvalidStateError: + pass + except: + call.hangup() + + +if __name__ == "__main__": + phone = VoIPPhone( + "localhost", + 5060, + "john.doe", + "password", + myIP="127.0.0.1", + sipPort=25060, + callCallback=answer, + ) + + phone.start() + input("Press enter to disable the phone") + phone.stop() diff --git a/centos.md b/centos.md new file mode 100644 index 0000000..7d6967d --- /dev/null +++ b/centos.md @@ -0,0 +1,18 @@ +Use the boot install: + - https://www.centos.org/download/ + - Select CentOS **Stream** + - Select CentOS 8 Stream + - Why is anyone using this? EOL is right around the corner. + +Install in VM environment of your choosing. You must have internet access inside the VM. +Set the installation source: + - http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os/ + - repository url type +software selection: + - minimal install + - guest agents + + +sudo dnf groupinstall "Development Tools" +sudo yum install pulseaudio-libs +sudo yum install python3-devel \ No newline at end of file diff --git a/containers/asterisk/docker-compose.yml b/containers/asterisk/docker-compose.yml new file mode 100644 index 0000000..3d274d9 --- /dev/null +++ b/containers/asterisk/docker-compose.yml @@ -0,0 +1,29 @@ +version: '3' + +services: + tele: + image: mlan/asterisk + network_mode: bridge # Only here to help testing + cap_add: + - sys_ptrace # Only here to help testing + - net_admin # Allow NFT, used by AutoBan + - net_raw # Allow NFT, used by AutoBan + ports: + - "${SMS_PORT-8081}:${WEBSMSD_PORT:-80}" # WEBSMSD port mapping + - "5060:5060/udp" # SIP UDP port + - "5060:5060" # SIP TCP port + - "5061:5061" # SIP TLS port + - "10000-10099:10000-10099/udp" # RTP ports + environment: + - SYSLOG_LEVEL=${SYSLOG_LEVEL-4} # Logging + - HOSTNAME=${TELE_SRV-tele}.${DOMAIN-docker.localhost} + - PULSE_SERVER=unix:/run/pulse/socket # Use host audio + - PULSE_COOKIE=/run/pulse/cookie # Use host audio + - WEBSMSD_PORT=${WEBSMSD_PORT-80} # WEBSMSD internal port + volumes: + - tele-conf:/srv # Persistent storage + - ./pulse:/run/pulse:rshared # Use host audio + - /etc/localtime:/etc/localtime:ro # Use host timezone + +volumes: + tele-conf: # Persistent storage \ No newline at end of file diff --git a/containers/kafka/docker-compose.yml b/containers/kafka/docker-compose.yml new file mode 100644 index 0000000..80af548 --- /dev/null +++ b/containers/kafka/docker-compose.yml @@ -0,0 +1,62 @@ +version: "3" + +services: + kafka: + container_name: kafka + hostname: kafka + image: bitnami/kafka:latest + ports: + # Flip the ports around, external gets default. + - "9092:9094" + - "9094:9092" + #network_mode: host + networks: + - kafka-internal + volumes: + - "kafka_data:/bitnami" + environment: + - ALLOW_PLAINTEXT_LISTENER=yes + - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 + - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://localhost:9094 + - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT + - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true + + kafka-ui: + container_name: kafka-ui + image: provectuslabs/kafka-ui:latest + ports: + - 8080:8080 + networks: + - kafka-internal + environment: + DYNAMIC_CONFIG_ENABLED: 'true' + KAFKA_CLUSTERS_0_NAME: test + KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092 + depends_on: + - kafka + volumes: + - ~/kui/config.yml:/etc/kafkaui/dynamic_config.yaml + + +# magic: +# image: "digitsy/kafka-magic" +# ports: +# - "8080:80" +# networks: +# - kafka-internal +# volumes: +# - .:/config +# environment: +# KMAGIC_ALLOW_TOPIC_DELETE: "true" +# KMAGIC_ALLOW_SCHEMA_DELETE: "true" +# KMAGIC_CONFIG_STORE_TYPE: "file" +# KMAGIC_CONFIG_STORE_CONNECTION: "Data Source=/config/KafkaMagicConfig.db;" +# KMAGIC_CONFIG_ENCRYPTION_KEY: "ENTER_YOUR_KEY_HERE" + + +volumes: + kafka_data: + driver: local + +networks: + kafka-internal: \ No newline at end of file diff --git a/data/kafkalogging.py b/data/kafkalogging.py new file mode 100644 index 0000000..1a02bf7 --- /dev/null +++ b/data/kafkalogging.py @@ -0,0 +1,88 @@ +from kafka import KafkaProducer +from kafka.admin import KafkaAdminClient, NewTopic +import subprocess +from select import select +import os + +kafka_servers = ['localhost:9092'] +producers = {} +client = KafkaAdminClient(bootstrap_servers=kafka_servers) + + +# Inspired by gist: +# https://gist.github.com/krzemienski/8c7e8e76c8652984cca0da3fea5b5368 +def check_io(process, process_name=None): + #if process.stdout is not None: os.set_blocking(process.stdout.fileno(), False) + #if process.stderr is not None: os.set_blocking(process.stderr.fileno(), False) + + #ready_to_read, _, _ = select( + # [x for x in [process.stdout, process.stderr] if x is not None], + # [], [], 0 + #) + + #return + + for pipe_name,pipe in {'stdout':process.stdout, 'stderr':process.stderr}.items(): + if pipe is None: + continue + + if os.get_blocking(pipe): + os.set_blocking(pipe, False) + + #if pipe not in ready_to_read: + # continue + + topic_name = f"{process.pid if process_name is None else process_name}-{pipe_name}" + + #if process.pid not in producers: + # producers[process.pid] = {} + #if pipe_name not in producers[process.pid]: + # #client.create_topics( + # # [ + # # NewTopic(name=topic_name, num_partitions=1, replication_factor=1) + # # ] + # #) + # producers[process.pid][pipe_name] = KafkaProducer(bootstrap_servers=kafka_servers, max_block_ms=10) + + while True: + #print(topic_name) + #break + # The python official docs recommend using Popen.communicate() + # instead of reading from the pipes directly. This isn't applicable + # here, as the python docs assume you're shelling out to a short-lived + # process and retrieving the results, not starting a service and + # pulling the log output. Popen.communicate() is a blocking call and + # would die here forever waiting for the service to exit. + output = pipe.readline() + if output: + print(output) + #producers[process.pid][pipe_name].send(topic_name, value=output) + else: + break + + #break anyway, i think we're dying in this loop + break + + #producers[process.pid][pipe_name].flush() + + + +if __name__ == '__main__': + import time + + ping = subprocess.Popen( + [ + '/usr/bin/ping', + '1.1.1.1', + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + while True: + check_io(ping, 'ping') + time.sleep(0.1) + + ping.kill() + ping.wait() + ping = None \ No newline at end of file diff --git a/radio.py b/radio.py index 5f08b10..7fbb9e4 100644 --- a/radio.py +++ b/radio.py @@ -25,6 +25,7 @@ class Radio(Streamer): raise RuntimeError("Failed to connect to radio device") self.capabilities = self._get_capabilities() + self.stream_errors = 0 def configure(self, frequency): if self.is_streaming(): @@ -59,8 +60,9 @@ class Radio(Streamer): "name": self.name, "device": self.device_info, "capabilities": self.capabilities, - "stream-path": self._stream_path(), + "stream-path": self.stream_path(), "streaming": self.is_streaming(), + "stream-errors": self.stream_errors, } def _get_capabilities(self): @@ -100,9 +102,14 @@ class Radio(Streamer): continue elif result.ret < 0: error = SoapyError(result.ret) - if error is not SoapyError.Timeout: + if error is SoapyError.Timeout or error is SoapyError.Overflow: + self.stream_errors += 1 + print("Non-fatal stream error:", error.name, file=sys.stderr) + else: print( - "Stream read failed, aborting stream:", error, file=sys.stderr + "Stream read failed, aborting stream:", + error.name, + file=sys.stderr, ) break continue @@ -159,6 +166,7 @@ class Radio(Streamer): soapy.SOAPY_SDR_RX, FORMATS[Radio.FORMAT].soapy ) result = self.device.activateStream(self.stream) + self.stream_errors = 0 if result != 0: raise RuntimeError(f"Error activating stream: {result}") diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..89fa3a1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +colorama==0.4.3 +flasgger==0.9.5 +Flask==2.2.3 +numpy==1.17.4 +prefixed==0.7.0 +pyVoIP +PyYAML==6.0 +requests==2.22.0