initial
This commit is contained in:
commit
e7e14b48ca
|
@ -0,0 +1,4 @@
|
|||
env/
|
||||
__pycache__
|
||||
*.pyc
|
||||
screenstreamer/generated
|
|
@ -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
|
|
@ -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
|
||||
```
|
|
@ -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
|
|
@ -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())
|
|
@ -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')
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScreenStreamer</class>
|
||||
<widget class="QMainWindow" name="ScreenStreamer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>471</width>
|
||||
<height>231</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ScreenStreamer</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>456</width>
|
||||
<height>153</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="streamBox">
|
||||
<property name="title">
|
||||
<string>Stream</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="displayLabel">
|
||||
<property name="text">
|
||||
<string>Display</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="displaySelect"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="RecieverLabel">
|
||||
<property name="text">
|
||||
<string>Reciever</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="sendto">
|
||||
<property name="placeholderText">
|
||||
<string>127.0.0.1:1234</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="streamBtn">
|
||||
<property name="text">
|
||||
<string>Stream</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="recieveBox">
|
||||
<property name="title">
|
||||
<string>Recieve</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>ListenAddress</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="listenAddr">
|
||||
<property name="placeholderText">
|
||||
<string>0.0.0.0:1234</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="recieveBtn">
|
||||
<property name="text">
|
||||
<string>Recieve</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>471</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>streamBtn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScreenStreamer</receiver>
|
||||
<slot>stream_pressed()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>96</x>
|
||||
<y>134</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>119</x>
|
||||
<y>197</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>recieveBtn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScreenStreamer</receiver>
|
||||
<slot>recieve_pressed()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>275</x>
|
||||
<y>137</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>300</x>
|
||||
<y>201</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<slots>
|
||||
<slot>stream_pressed()</slot>
|
||||
<slot>recieve_pressed()</slot>
|
||||
</slots>
|
||||
</ui>
|
Loading…
Reference in New Issue