error handling

This commit is contained in:
mrbesen 2022-10-08 17:01:38 +02:00
parent 7339f52bcf
commit 2399277971
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
4 changed files with 112 additions and 27 deletions

View File

@ -75,9 +75,8 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
def stream_pressed(self):
if self.streamer:
# stream already running -> stopping
self.streamer = None
self.ui.statusbar.showMessage('stream stopped')
self.ui.streamBtn.setText('Stream')
self.streamer.stop()
self.ui.streamBtn.setEnabled(False)
return
displaydata = self.ui.displaySelect.currentData()
@ -88,18 +87,17 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
self.ui.protocolSelect.currentText().lower(),
"{}:{}".format(self.ui.sendto.text(), self.ui.streamPort.value())
)
self.streamer.process_manager.process_terminated.connect(self.stream_terminated)
self.streamer.start()
self.ui.streamBtn.setText('Stop Stream')
# TODO: add feedback on error
self.set_stream_status(True)
self.ui.statusbar.showMessage('stream started')
@QtCore.Slot()
def recieve_pressed(self):
if self.receiver:
# stream already running -> stopping
self.receiver = None
self.ui.statusbar.showMessage('recieve stopped')
self.ui.recieveBtn.setText('Recieve')
# recieve already running -> stopping
self.receiver.stop()
self.ui.recieveBtn.setEnabled(False)
return
self.receiver = Reciever(
@ -107,7 +105,44 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
"{}:{}".format(self.ui.listenAddr.text(), self.ui.listenPort.value()),
self.ui.disableAudio.checkState() == 'Checked'
)
self.receiver.process_manager.process_terminated.connect(self.recieve_terminated)
self.receiver.start()
self.ui.recieveBtn.setText('Stop Recieve')
# TODO: add feedback on error
self.set_recieve_status(True)
self.ui.statusbar.showMessage('recieve started')
def set_recieve_status(self, status: bool):
"""status = True - stream is running"""
self.ui.recieveBtn.setEnabled(True)
if status:
self.ui.recieveBtn.setText('Stop Recieve')
else:
self.ui.recieveBtn.setText('Recieve')
def set_stream_status(self, status: bool):
"""status = True - stream is running"""
self.ui.streamBtn.setEnabled(True)
if status:
self.ui.streamBtn.setText('Stop Stream')
else:
self.ui.streamBtn.setText('Stream')
def show_error(self, tag: str, returncode: int, errormsg: str):
if errormsg:
error = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Critical, tag + ' Error', tag + ' Error: ' + errormsg, QtWidgets.QMessageBox.Ok, self)
error.show()
error.exec()
@QtCore.Slot(int, str)
def recieve_terminated(self, returncode, errormsg):
self.receiver = None
self.ui.statusbar.showMessage('recieve stopped')
self.show_error('Recieve', returncode, errormsg)
self.set_recieve_status(False)
@QtCore.Slot(int, str)
def stream_terminated(self, returncode, errormsg):
self.streamer = None
self.ui.statusbar.showMessage('stream stopped')
self.show_error('Stream', returncode, errormsg)
self.set_stream_status(False)

View File

@ -0,0 +1,54 @@
from PySide6.QtCore import QObject, Signal
from threading import Thread
import subprocess
class ProcManager(QObject):
process_terminated = Signal(int, str)
def __init__(self) -> None:
super(ProcManager, self).__init__()
self.thre = None
self.proc = None
def start(self, proc):
# this method might override the old process and thread
self.proc = proc
self.thre = Thread(target=self.detect_end)
self.thre.start()
def stop(self):
if self.proc:
self.proc.terminate()
try:
self.proc.wait(1)
except subprocess.TimeoutExpired as e:
self.proc.kill()
if self.thre:
self.thre.join()
self.thre = None
def detect_end(self):
# wait for the process to terminate
while True:
try:
self.proc.wait(1)
break
except subprocess.TimeoutExpired as e:
continue
returncode = self.proc.returncode
errorstream = self.proc.stderr.read()
if isinstance(errorstream, bytes):
errorstream = errorstream.decode('utf-8')
self.proc = None
print('Process Terminated:', returncode, errorstream)
self.process_terminated.emit(returncode, errorstream)

View File

@ -1,4 +1,5 @@
import subprocess
from procmanager import ProcManager
class Reciever:
@ -6,7 +7,7 @@ class Reciever:
self.protocol = protocol # should be 'tcp' or 'udp'
self.target = target
self.disable_audio = disable_audio
self.proc = None
self.process_manager = ProcManager()
def __del__(self) -> None:
self.stop()
@ -14,7 +15,7 @@ class Reciever:
def start(self):
# currently there is no direct ffplay support in the ffmpeg package
args = ['ffplay', '-fflags', 'nobuffer', '-flags', 'low_delay', '-f', 'mpegts']
args = ['ffplay', '-hide_banner', '-loglevel', 'error', '-fflags', 'nobuffer', '-flags', 'low_delay', '-f', 'mpegts']
if self.disable_audio:
args.append('-an')
@ -22,12 +23,9 @@ class Reciever:
# the address
args.append(self.protocol+ '://' + self.target + '?listen')
self.proc = subprocess.Popen(args)
proc = subprocess.Popen(args, encoding='utf-8', shell=False, text=True, stderr=subprocess.PIPE)
self.process_manager.start(proc)
def stop(self):
if self.proc:
self.proc.terminate()
self.proc.wait(1)
self.proc.kill()
self.proc = None
self.process_manager.stop()

View File

@ -1,4 +1,5 @@
import ffmpeg
from procmanager import ProcManager
class Streamer:
@ -8,14 +9,14 @@ class Streamer:
self.audioindex = audioindex
self.protocol = protocol # should be 'tcp' or 'udp'
self.target = target
self.proc = None
self.process_manager = ProcManager()
def __del__(self) -> None:
self.stop()
def start(self):
streams = []
self.input = ffmpeg.input(self.screendef, f='x11grab', r=60, s=self.screensize)
self.input = ffmpeg.input(self.screendef, f='x11grab', r=60, s=self.screensize, hide_banner='-y', loglevel='error')
streams.append(self.input)
if self.audioindex != -1:
@ -23,13 +24,10 @@ class Streamer:
streams.append(self.audio)
self.output = ffmpeg.output(*streams, self.protocol + '://' + self.target, f='mpegts', vcodec='nvenc_hevc', tune='zerolatency')
self.proc = ffmpeg.run_async(self.output)
proc = ffmpeg.run_async(self.output, pipe_stderr=True)
self.process_manager.start(proc)
def stop(self):
if self.proc:
self.proc.terminate()
self.proc.wait(1)
self.proc.kill()
self.proc = None
self.process_manager.stop()