From e7e14b48caf0af558ed284e7b0c626a62df07b36 Mon Sep 17 00:00:00 2001 From: mrbesen Date: Fri, 7 Oct 2022 20:31:31 +0200 Subject: [PATCH] initial --- .gitignore | 4 + Makefile | 19 +++++ Readme.md | 23 ++++++ requirements.txt | 7 ++ screenstreamer/__main__.py | 18 +++++ screenstreamer/gui.py | 65 ++++++++++++++++ screenstreamer/streamer.py | 15 ++++ ui/mainwindow.ui | 154 +++++++++++++++++++++++++++++++++++++ 8 files changed, 305 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Readme.md create mode 100644 requirements.txt create mode 100644 screenstreamer/__main__.py create mode 100644 screenstreamer/gui.py create mode 100644 screenstreamer/streamer.py create mode 100644 ui/mainwindow.ui diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5528565 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +env/ +__pycache__ +*.pyc +screenstreamer/generated diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..460d907 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +UIC?=./env/lib/python3.8/site-packages/PySide6/Qt/libexec/uic + +%/: + mkdir -p $@ + +all: screenstreamer/generated/mainwindow.py + +# generate ui +screenstreamer/generated/mainwindow.py: screenstreamer/generated/ ui/mainwindow.ui + $(UIC) -g python ui/mainwindow.ui > screenstreamer/generated/mainwindow.py + +clean: + $(RM) -r screenstreamer/generated/ + +# generate a requirements.txt +requirements.txt: + pip freeze > $@ + +.PHONY: clean diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..a98f49e --- /dev/null +++ b/Readme.md @@ -0,0 +1,23 @@ +# ScreenStreamer +A simple Python ffmpeg wrapper for streaming and recieving a Desktop stream + +To run: +```bash +# create a venv +python3.8 -m venv venv + +# activate venv +source ./venv/bin/activate + +# install dependencys +pip3 install -r ./requirements.txt + +# build ui components +make all + +# run +python3 screenstreamer/ + +# leave env +deactivate +``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c403e6b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +ffmpeg==1.4 +ffmpeg-python==0.2.0 +future==0.18.2 +PySide6==6.2.4 +shiboken6==6.2.4 +six==1.16.0 +xlib==0.21 diff --git a/screenstreamer/__main__.py b/screenstreamer/__main__.py new file mode 100644 index 0000000..bc41337 --- /dev/null +++ b/screenstreamer/__main__.py @@ -0,0 +1,18 @@ +import gui +import sys +from PySide6 import QtWidgets + +mainwindow = None + +def main(): + global mainwindow + mainwindow = gui.ScreenStreamerGUI() + mainwindow.show() + + +if __name__ == '__main__': + app = QtWidgets.QApplication([]) + + main() + + sys.exit(app.exec()) diff --git a/screenstreamer/gui.py b/screenstreamer/gui.py new file mode 100644 index 0000000..76a57ed --- /dev/null +++ b/screenstreamer/gui.py @@ -0,0 +1,65 @@ +from PySide6 import QtCore, QtWidgets +from generated.mainwindow import Ui_ScreenStreamer +from streamer import Streamer +from Xlib import display +from Xlib.ext import randr + +class ScreenStreamerGUI(QtWidgets.QMainWindow): + + def __init__(self, parent = None) -> None: + QtWidgets.QMainWindow.__init__(self, parent) + + self.ui = Ui_ScreenStreamer() + self.ui.setupUi(self) + + displays = self.get_displays() + print(displays) + + for disp in displays: + self.ui.displaySelect.addItem("{} ({}x{})".format(disp['name'], disp['width'], disp['height']), userData=disp) + + def get_displays(self): + disp = display.Display() + screen = disp.screen() + window = screen.root + res = randr.get_screen_resources(window) + + result = list() + allwidth = 0 + allheight = 0 + for output in res.outputs: + params = disp.xrandr_get_output_info(output, res.config_timestamp) + + if not params.crtc: + continue # non connected display ports + + crtc = disp.xrandr_get_crtc_info(params.crtc, res.config_timestamp) + result.append({'name': params.name , 'width': crtc.width, 'height': crtc.height, 'x': crtc.x, 'y': crtc.y}) + + if crtc.width + crtc.x > allwidth: + allwidth = crtc.width + crtc.x + if crtc.height + crtc.y > allheight: + allheight = crtc.height + crtc.y + + result.append({'name': 'All', 'width': allwidth, 'height': allheight, 'x': 0, 'y': 0}) + return result + + def format_display(self, displayinfo): + return ":0.0+{},{}".format(displayinfo['x'], displayinfo['y']) + + def format_screensize(self, displayinfo): + return "{}:{}".format(displayinfo['width'], displayinfo['height']) + + @QtCore.Slot() + def stream_pressed(self): + displaydata = self.ui.displaySelect.currentData() + self.streamer = Streamer(self.format_display(displaydata), self.format_screensize(displaydata), self.ui.sendto.text()) + self.streamer.start() + # TODO: add button to stop and feedback on error + print('stream started') + self.ui.statusbar.showMessage('stream started') + + @QtCore.Slot() + def recieve_pressed(self): + # TODO + print('recieve') diff --git a/screenstreamer/streamer.py b/screenstreamer/streamer.py new file mode 100644 index 0000000..ad336d4 --- /dev/null +++ b/screenstreamer/streamer.py @@ -0,0 +1,15 @@ +from platform import python_branch +import ffmpeg + +class Streamer: + + def __init__(self, screendef: str, screensize: str, target: str) -> None: + self.screendef = screendef + self.screensize = screensize + self.target = target + + def start(self): + self.input = ffmpeg.input(self.screendef, f='x11grab', r=60, s=self.screensize) + self.output = ffmpeg.output(self.input, 'tcp://' + self.target, f='mpegts', vcodec='nvenc_hevc', tune='zerolatency') + self.proc = ffmpeg.run_async(self.output) + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui new file mode 100644 index 0000000..76923af --- /dev/null +++ b/ui/mainwindow.ui @@ -0,0 +1,154 @@ + + + ScreenStreamer + + + + 0 + 0 + 471 + 231 + + + + ScreenStreamer + + + + + + 0 + 0 + 456 + 153 + + + + + QLayout::SetMaximumSize + + + + + Stream + + + + + + Display + + + + + + + + + + Reciever + + + + + + + 127.0.0.1:1234 + + + + + + + Stream + + + + + + + + + + Recieve + + + + + + ListenAddress + + + + + + + 0.0.0.0:1234 + + + + + + + Recieve + + + + + + + + + + + + + 0 + 0 + 471 + 24 + + + + + + + + + streamBtn + clicked() + ScreenStreamer + stream_pressed() + + + 96 + 134 + + + 119 + 197 + + + + + recieveBtn + clicked() + ScreenStreamer + recieve_pressed() + + + 275 + 137 + + + 300 + 201 + + + + + + stream_pressed() + recieve_pressed() + +