Use PUB/Sub for voice sending (auto socket recovery_

This commit is contained in:
Ruben van de Ven 2019-01-17 21:23:05 +01:00
parent 8f68505ff4
commit e8bfa8a6da
2 changed files with 47 additions and 25 deletions

View file

@ -14,7 +14,7 @@ logger = logging.getLogger("client")
class VoiceServer(object): class VoiceServer(object):
"""A UDP server, providing mic data at 16 kHz""" """A UDP server, providing mic data at 16 kHz"""
def __init__(self, voice_port: int, input_rate: int, input_name: str = None, target_rate: int = 16000): def __init__(self, loop, voice_port: int, input_rate: int, input_name: str = None, target_rate: int = 16000):
self.voice_port = voice_port self.voice_port = voice_port
self.input_rate = input_rate self.input_rate = input_rate
self.target_rate = target_rate self.target_rate = target_rate
@ -22,6 +22,8 @@ class VoiceServer(object):
self.clients = [] self.clients = []
self.laststate = None self.laststate = None
self.input_name = input_name self.input_name = input_name
self.ctx = Context.instance()
self.loop = loop
def get_input_idx(self): def get_input_idx(self):
input_device_idx = None input_device_idx = None
@ -47,16 +49,19 @@ class VoiceServer(object):
# rate converted 44k1 -> 16k gives len(f) == 2972 (16/44.1 * 8192) # rate converted 44k1 -> 16k gives len(f) == 2972 (16/44.1 * 8192)
f, self.laststate = audioop.ratecv(in_data, 2, 1, self.input_rate, self.target_rate, self.laststate) f, self.laststate = audioop.ratecv(in_data, 2, 1, self.input_rate, self.target_rate, self.laststate)
for s in self.clients:
try: # for s in self.clients:
s.send(f) try:
except Exception as e: # self.loop.call_soon_threadsafe()
self.clients.remove(s) self.loop.call_soon_threadsafe( self.voice_socket.send, f )
logger.warn("Error sending to {}, {}".format(s.getsockname(), e)) # s.send(f)
pass except Exception as e:
# self.clients.remove(s)
logger.warn("Error sending to {}".format(e))
pass
return (None, pyaudio.paContinue) return (None, pyaudio.paContinue)
def start(self): async def start(self):
FORMAT = pyaudio.paInt16 FORMAT = pyaudio.paInt16
CHANNELS = 1 CHANNELS = 1
CHUNK = 4096 CHUNK = 4096
@ -76,18 +81,24 @@ class VoiceServer(object):
while not self.stopped: while not self.stopped:
try: try:
self.voice_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) address = "tcp://*:{}".format(self.voice_port)
self.voice_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.voice_socket = self.ctx.socket(zmq.PUB)
address = ('', self.voice_port) # self.voice_socket.setsockopt(zmq.CONFLATE, 1)
self.voice_socket.bind(address) self.voice_socket.bind(address)
self.voice_socket.listen(5)
read_list = [self.voice_socket] # self.voice_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.voice_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# address = ('', self.voice_port)
# self.voice_socket.bind(address)
# self.voice_socket.listen(5)
# read_list = [self.voice_socket]
logger.info( "Waiting for voice connections on {}".format(address) ) logger.info( "Waiting for voice connections on {}".format(address) )
while not self.stopped: while not self.stopped:
(clientsocket, address) = self.voice_socket.accept() await asyncio.sleep(1)
logger.info( "Got voice connection from {}".format(address)) # (clientsocket, address) = self.voice_socket.accept()
self.clients.append(clientsocket) # logger.info( "Got voice connection from {}".format(address))
# self.clients.append(clientsocket)
logger.info( "Stop recording & streaming") logger.info( "Stop recording & streaming")
self.voice_socket.close() self.voice_socket.close()
@ -214,7 +225,10 @@ class Hugvey(object):
return await self.cmd_server.command_listener() return await self.cmd_server.command_listener()
def start(self): def start(self):
loop = asyncio.get_event_loop()
self.voice_server = VoiceServer( self.voice_server = VoiceServer(
loop = loop,
voice_port = int(self.config['voice']['port']), voice_port = int(self.config['voice']['port']),
input_rate = int(self.config['voice']['input_rate']), input_rate = int(self.config['voice']['input_rate']),
input_name = self.config['voice']['input_name'], input_name = self.config['voice']['input_name'],
@ -225,11 +239,10 @@ class Hugvey(object):
cmd_address = self.config['events']['cmd_address'], cmd_address = self.config['events']['cmd_address'],
publish_address = self.config['events']['publish_address'], publish_address = self.config['events']['publish_address'],
) )
loop = asyncio.get_event_loop()
logger.info('start') logger.info('start')
# self.voice_server.asyncStart(loop) # self.voice_server.asyncStart(loop)
# loop.run_until_complete(self.voice_server.start()) # loop.run_until_complete(self.voice_server.start())
asyncio.ensure_future(self.voice_server.asyncStart(loop)) asyncio.ensure_future(self.voice_server.start())
asyncio.ensure_future(self.cmd_server.command_listener()) asyncio.ensure_future(self.cmd_server.command_listener())
asyncio.ensure_future(self.cmd_server.event_sender()) asyncio.ensure_future(self.cmd_server.event_sender())
self.cmd_server.showMyself() self.cmd_server.showMyself()

View file

@ -3,6 +3,8 @@ Consume a given Hugvey audio socket, and stream into the given services to emit
""" """
import socket import socket
import logging import logging
from zmq.asyncio import Context
import zmq
logger = logging.getLogger("streamer") logger = logging.getLogger("streamer")
@ -22,17 +24,24 @@ class AudioStreamer(object):
async def run(self): async def run(self):
self.isRunning = True self.isRunning = True
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) address = "tcp://{}:{}".format(self.address, self.port)
logger.info("Attempt connection on {}:{}".format(self.address, self.port)) self.ctx = Context.instance()
s.connect((self.address, self.port)) self.socket = self.ctx.socket(zmq.SUB)
self.socket.subscribe('')
# self.socket.setsockopt(zmq.CONFLATE, 1)
self.socket.connect(address)
# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
logger.info("Attempt connection on {}:{}".format(self.address, self.port))
# s.connect((self.address, self.port))
#
while self.isRunning: while self.isRunning:
data = s.recv(self.chunk) data = await self.socket.recv()
# logger.debug('chunk received') # logger.debug('chunk received')
self.process(data) self.process(data)
logger.info("Close socket on {}:{}".format(self.address, self.port)) logger.info("Close socket on {}:{}".format(self.address, self.port))
s.close() self.socket.close()
def stop(self): def stop(self):
self.isRunning = False self.isRunning = False