basic audio transmission

This commit is contained in:
mrbesen 2022-10-07 22:54:27 +02:00
parent ea733a2790
commit 259c46fa00
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
5 changed files with 104 additions and 9 deletions

View File

@ -1,7 +1,9 @@
from operator import indexOf
from PySide6 import QtCore, QtWidgets from PySide6 import QtCore, QtWidgets
from generated.mainwindow import Ui_ScreenStreamer from generated.mainwindow import Ui_ScreenStreamer
from streamer import Streamer from streamer import Streamer
from reciever import Reciever from reciever import Reciever
from pulse import Pulse
from Xlib import display from Xlib import display
from Xlib.ext import randr from Xlib.ext import randr
@ -19,6 +21,21 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
for disp in displays: for disp in displays:
self.ui.displaySelect.addItem("{} ({}x{})".format(disp['name'], disp['width'], disp['height']), userData=disp) self.ui.displaySelect.addItem("{} ({}x{})".format(disp['name'], disp['width'], disp['height']), userData=disp)
audiodevices = Pulse.get_audio_inputs()
print(audiodevices)
self.ui.audioSource.addItem('No Audio', userData=-1) # always add this option
for audio in audiodevices:
text = audio['description'] if audio['description'] else audio['name']
if audio.get('monitor', False):
text += ' (Monitor)'
self.ui.audioSource.addItem(text, userData=audio['id'])
if audio.get('isdefault', False):
self.ui.audioSource.setCurrentIndex(indexOf(audiodevices, audio))
self.streamer = None self.streamer = None
self.receiver = None self.receiver = None
@ -67,6 +84,7 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
self.streamer = Streamer( self.streamer = Streamer(
self.format_display(displaydata), self.format_display(displaydata),
self.format_screensize(displaydata), self.format_screensize(displaydata),
self.ui.audioSource.currentData(),
self.ui.protocolSelect.currentText().lower(), self.ui.protocolSelect.currentText().lower(),
self.ui.sendto.text() self.ui.sendto.text()
) )
@ -86,7 +104,8 @@ class ScreenStreamerGUI(QtWidgets.QMainWindow):
self.receiver = Reciever( self.receiver = Reciever(
self.ui.protocolSelect.currentText().lower(), self.ui.protocolSelect.currentText().lower(),
self.ui.listenAddr.text() self.ui.listenAddr.text(),
self.ui.disableAudio.checkState() == 'Checked'
) )
self.receiver.start() self.receiver.start()
self.ui.recieveBtn.setText('Stop Recieve') self.ui.recieveBtn.setText('Stop Recieve')

43
screenstreamer/pulse.py Normal file
View File

@ -0,0 +1,43 @@
import subprocess
from os import environ
class Pulse:
# this method uses the intern names of the sinks
# @staticmethod
# def get_audio_inputs():
# proc = subprocess.run(['pactl', 'list', 'short', 'sources'], capture_output=True)
# parts = [line.split('\t') for line in proc.stdout.decode('utf-8').split('\n')]
# deviceinfos = [{'id': device[0], 'name': device[1]} for device in parts if len(device) > 1]
# return deviceinfos
@staticmethod
def get_audio_inputs():
proc = subprocess.run(['pacmd', 'list-sources'], capture_output=True, env={**environ, 'LANG': 'C'})
lines = proc.stdout.decode('utf-8').split('\n')
currentSourceID = -1
currentName = ''
ismointor = False
isdefault = False
description = ''
devices = []
for line in lines:
if line[4:].startswith('index: '):
#submit old device
if currentSourceID != -1:
devices.append({'id': currentSourceID, 'name': currentName, 'monitor': ismointor, 'description': description, 'isdefault': isdefault})
ismointor = False
isdefault = line[2] == '*'
description = ''
currentSourceID = int(line[11:])
elif line.startswith('\tname: '):
currentName = line[8:-1]
elif line == '\t\tdevice.class = "monitor"':
ismointor = True
elif line.startswith('\t\tdevice.description = "'):
description = line[24:-1]
devices.append({'id': currentSourceID, 'name': currentName, 'monitor': ismointor, 'description': description, 'isdefault': isdefault})
return devices

View File

@ -2,9 +2,10 @@ import subprocess
class Reciever: class Reciever:
def __init__(self, protocol: str, target: str) -> None: def __init__(self, protocol: str, target: str, disable_audio: bool) -> None:
self.protocol = protocol # should be 'tcp' or 'udp' self.protocol = protocol # should be 'tcp' or 'udp'
self.target = target self.target = target
self.disable_audio = disable_audio
self.proc = None self.proc = None
def __del__(self) -> None: def __del__(self) -> None:
@ -13,8 +14,15 @@ class Reciever:
def start(self): def start(self):
# currently there is no direct ffplay support in the ffmpeg package # currently there is no direct ffplay support in the ffmpeg package
addr = self.protocol+ '://' + self.target + '?listen' args = ['ffplay', '-fflags', 'nobuffer', '-flags', 'low_delay', '-f', 'mpegts']
self.proc = subprocess.Popen(['ffplay', '-fflags', 'nobuffer', '-flags', 'low_delay', '-f', 'mpegts', addr])
if self.disable_audio:
args.append('-an')
# the address
args.append(self.protocol+ '://' + self.target + '?listen')
self.proc = subprocess.Popen(args)
def stop(self): def stop(self):
if self.proc: if self.proc:

View File

@ -2,9 +2,10 @@ import ffmpeg
class Streamer: class Streamer:
def __init__(self, screendef: str, screensize: str, protocol: str, target: str) -> None: def __init__(self, screendef: str, screensize: str, audioindex: int, protocol: str, target: str) -> None:
self.screendef = screendef self.screendef = screendef
self.screensize = screensize self.screensize = screensize
self.audioindex = audioindex
self.protocol = protocol # should be 'tcp' or 'udp' self.protocol = protocol # should be 'tcp' or 'udp'
self.target = target self.target = target
self.proc = None self.proc = None
@ -13,8 +14,15 @@ class Streamer:
self.stop() self.stop()
def start(self): 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)
self.output = ffmpeg.output(self.input, self.protocol + '://' + self.target, f='mpegts', vcodec='nvenc_hevc', tune='zerolatency') streams.append(self.input)
if self.audioindex != -1:
self.audio = ffmpeg.input(str(self.audioindex), f='pulse')
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) self.proc = ffmpeg.run_async(self.output)
def stop(self): def stop(self):

View File

@ -64,27 +64,37 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="QComboBox" name="displaySelect"/> <widget class="QComboBox" name="displaySelect"/>
</item> </item>
<item row="2" column="0"> <item row="3" column="0">
<widget class="QLabel" name="RecieverLabel"> <widget class="QLabel" name="RecieverLabel">
<property name="text"> <property name="text">
<string>Reciever</string> <string>Reciever</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="3" column="1">
<widget class="QLineEdit" name="sendto"> <widget class="QLineEdit" name="sendto">
<property name="placeholderText"> <property name="placeholderText">
<string>127.0.0.1:1234</string> <string>127.0.0.1:1234</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="QPushButton" name="streamBtn"> <widget class="QPushButton" name="streamBtn">
<property name="text"> <property name="text">
<string>Stream</string> <string>Stream</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="audioSourceLabel">
<property name="text">
<string>Audio Source</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="audioSource"/>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -115,6 +125,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableAudio">
<property name="text">
<string>Disable Audio</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>