From 4d7ae06eb3dbaecf2de99baf91ece1263a062525 Mon Sep 17 00:00:00 2001 From: xiaji Date: Thu, 19 Mar 2026 09:41:43 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BE=8E=E5=8C=96push=5Fscreen.py=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E5=A2=9E=E5=8A=A0=E7=8A=B6=E6=80=81=E6=A0=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=85=8D=E7=BD=AE=E5=92=8C=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- push_screen.py | 209 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 174 insertions(+), 35 deletions(-) diff --git a/push_screen.py b/push_screen.py index beba95c..9f0cb61 100644 --- a/push_screen.py +++ b/push_screen.py @@ -2,10 +2,14 @@ import subprocess import psutil import os import sys +import socket import time from loguru import logger -from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QMessageBox, QVBoxLayout, QWidget -from PySide6.QtCore import QProcess, Qt +from PySide6.QtWidgets import (QApplication, QMainWindow, QPushButton, QMessageBox, + QVBoxLayout, QWidget, QLabel, QGroupBox, QGridLayout, + QStatusBar, QProgressBar, QFrame) +from PySide6.QtCore import QTimer, Qt, QThread, Signal +from PySide6.QtGui import QFont, QPalette, QColor logger.add(sys.stderr, format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", level="INFO") @@ -14,57 +18,192 @@ MEDIAMTX_PATH = r"D:\ScreenCast\mediamtx\mediamtx.exe" FFMPEG_PATH = r"D:\ScreenCast\ffmpeg\bin\ffmpeg.exe" STREAM_PATH = "screen" -def check_mediamtx_running(): - logger.info("检查MediaMTX进程是否运行") - for proc in psutil.process_iter(['name']): - if proc.info['name'] == 'mediamtx.exe': - logger.info("MediaMTX进程已运行") +class ConnectionChecker(QThread): + status_update = Signal(dict) + + def run(self): + while True: + status = { + "mediamtx": self.check_mediamtx(), + "ffmpeg": self.check_ffmpeg(), + "server": self.check_server(), + "server_port": self.check_port(SERVER_IP, 8554), + } + self.status_update.emit(status) + time.sleep(3) + + def check_mediamtx(self): + for proc in psutil.process_iter(['name']): + if proc.info['name'] == 'mediamtx.exe': + return True + return False + + def check_ffmpeg(self): + return os.path.exists(FFMPEG_PATH) + + def check_server(self): + try: + socket.create_connection((SERVER_IP, 8554), timeout=2) return True - logger.info("MediaMTX进程未运行") - return False + except: + return False -def start_mediamtx(): - if not check_mediamtx_running(): - logger.info("启动MediaMTX服务") - subprocess.Popen([MEDIAMTX_PATH], cwd=r"D:\ScreenCast\mediamtx", creationflags=subprocess.CREATE_NEW_CONSOLE) - time.sleep(3) - logger.info("MediaMTX服务已启动") - -def push_full_screen(window): - logger.info("开始全屏投屏") - start_mediamtx() - cmd = [ - FFMPEG_PATH, - "-f", "gdigrab", "-framerate", "30", "-i", "desktop", - "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", - "-f", "rtsp", f"rtsp://{SERVER_IP}:8554/{STREAM_PATH}" - ] - logger.info(f"执行FFmpeg命令: {' '.join(cmd)}") - process = QProcess() - process.startDetached(FFMPEG_PATH, cmd[1:]) - logger.info("FFmpeg推流已启动") - QMessageBox.information(window, "提示", f"全屏投屏已启动!接收端可打开浏览器访问:\nhttp://{SERVER_IP}:8889/webrtc.html?src=screen") + def check_port(self, ip, port): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(2) + result = sock.connect_ex((ip, port)) + sock.close() + return result == 0 + except: + return False class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("投屏源控制") - self.setFixedSize(400, 200) + self.setFixedSize(500, 350) + + self.status_bar = QStatusBar() + self.setStatusBar(self.status_bar) central_widget = QWidget() self.setCentralWidget(central_widget) - layout = QVBoxLayout(central_widget) + main_layout = QVBoxLayout(central_widget) - self.btn_push = QPushButton("一键全屏投屏") + title_label = QLabel("会议投屏系统 - 主播端") + title_font = QFont("Microsoft YaHei", 14, QFont.Bold) + title_label.setFont(title_font) + title_label.setAlignment(Qt.AlignCenter) + main_layout.addWidget(title_label) + + config_group = QGroupBox("本地配置") + config_layout = QGridLayout() + + self.mediamtx_label = QLabel("MediaMTX: 未检测") + self.ffmpeg_label = QLabel("FFmpeg: 未检测") + self.server_label = QLabel("服务器连接: 未检测") + self.port_label = QLabel("RTSP端口: 未检测") + + self.mediamtx_indicator = QLabel("●") + self.mediamtx_indicator.setFixedWidth(20) + self.ffmpeg_indicator = QLabel("●") + self.ffmpeg_indicator.setFixedWidth(20) + self.server_indicator = QLabel("●") + self.server_indicator.setFixedWidth(20) + self.port_indicator = QLabel("●") + self.port_indicator.setFixedWidth(20) + + config_layout.addWidget(self.mediamtx_indicator, 0, 0) + config_layout.addWidget(self.mediamtx_label, 0, 1) + config_layout.addWidget(self.ffmpeg_indicator, 1, 0) + config_layout.addWidget(self.ffmpeg_label, 1, 1) + config_layout.addWidget(self.server_indicator, 2, 0) + config_layout.addWidget(self.server_label, 2, 1) + config_layout.addWidget(self.port_indicator, 3, 0) + config_layout.addWidget(self.port_label, 3, 1) + + config_group.setLayout(config_layout) + main_layout.addWidget(config_group) + + info_group = QGroupBox("流信息") + info_layout = QGridLayout() + info_layout.addWidget(QLabel("服务器IP:"), 0, 0) + info_layout.addWidget(QLabel(SERVER_IP), 0, 1) + info_layout.addWidget(QLabel("推流地址:"), 1, 0) + info_layout.addWidget(QLabel(f"rtsp://{SERVER_IP}:8554/{STREAM_PATH}"), 1, 1) + info_layout.addWidget(QLabel("访问地址:"), 2, 0) + info_layout.addWidget(QLabel(f"http://{SERVER_IP}:8889/webrtc.html?src=screen"), 2, 1) + info_group.setLayout(info_layout) + main_layout.addWidget(info_group) + + self.btn_push = QPushButton("🎬 开始全屏投屏") self.btn_push.setFixedHeight(50) - self.btn_push.clicked.connect(lambda: push_full_screen(self)) - layout.addWidget(self.btn_push) + self.btn_push.setFont(QFont("Microsoft YaHei", 11)) + self.btn_push.clicked.connect(self.push_full_screen) + main_layout.addWidget(self.btn_push) + + self.log_label = QLabel("日志: 等待启动...") + self.log_label.setWordWrap(True) + self.log_label.setFrameShape(QFrame.Shape.StyledPanel) + self.log_label.setFixedHeight(40) + main_layout.addWidget(self.log_label) + + self.checker = ConnectionChecker() + self.checker.status_update.connect(self.update_status) + self.checker.start() logger.info("投屏源控制界面已初始化") + def update_status(self, status): + if status["mediamtx"]: + self.mediamtx_indicator.setStyleSheet("color: green; font-size: 16px;") + self.mediamtx_label.setText(f"MediaMTX: ✓ 运行中") + else: + self.mediamtx_indicator.setStyleSheet("color: red; font-size: 16px;") + self.mediamtx_label.setText(f"MediaMTX: ✗ 未运行") + + if status["ffmpeg"]: + self.ffmpeg_indicator.setStyleSheet("color: green; font-size: 16px;") + self.ffmpeg_label.setText(f"FFmpeg: ✓ 已安装") + else: + self.ffmpeg_indicator.setStyleSheet("color: red; font-size: 16px;") + self.ffmpeg_label.setText(f"FFmpeg: ✗ 未找到") + + if status["server"]: + self.server_indicator.setStyleSheet("color: green; font-size: 16px;") + self.server_label.setText(f"服务器连接: ✓ 可连接") + else: + self.server_indicator.setStyleSheet("color: orange; font-size: 16px;") + self.server_label.setText(f"服务器连接: ✗ 连接失败") + + if status["server_port"]: + self.port_indicator.setStyleSheet("color: green; font-size: 16px;") + self.port_label.setText(f"RTSP端口: ✓ 端口开放") + else: + self.port_indicator.setStyleSheet("color: orange; font-size: 16px;") + self.port_label.setText(f"RTSP端口: ✗ 端口关闭") + + def start_mediamtx(self): + if not self.checker.check_mediamtx(): + logger.info("启动MediaMTX服务") + subprocess.Popen([MEDIAMTX_PATH], cwd=r"D:\ScreenCast\mediamtx", creationflags=subprocess.CREATE_NEW_CONSOLE) + time.sleep(3) + logger.info("MediaMTX服务已启动") + self.log_label.setText("日志: MediaMTX服务已启动") + else: + logger.info("MediaMTX已在运行") + self.log_label.setText("日志: MediaMTX已在运行") + + def push_full_screen(self): + logger.info("开始全屏投屏") + self.start_mediamtx() + + if not os.path.exists(FFMPEG_PATH): + logger.error("FFmpeg未安装") + QMessageBox.warning(self, "错误", "FFmpeg未安装,请检查配置!") + return + + cmd = [ + FFMPEG_PATH, + "-f", "gdigrab", "-framerate", "30", "-i", "desktop", + "-c:v", "libx264", "-preset", "ultrafast", "-tune", "zerolatency", + "-f", "rtsp", f"rtsp://{SERVER_IP}:8554/{STREAM_PATH}" + ] + logger.info(f"执行FFmpeg命令: {' '.join(cmd)}") + subprocess.Popen(cmd, creationflags=subprocess.CREATE_NEW_CONSOLE) + logger.info("FFmpeg推流已启动") + self.log_label.setText("日志: FFmpeg推流已启动,正在向服务器推送...") + QMessageBox.information(self, "提示", f"全屏投屏已启动!\n\n接收端可打开浏览器访问:\nhttp://{SERVER_IP}:8889/webrtc.html?src=screen") + + def closeEvent(self, event): + self.checker.terminate() + event.accept() + if __name__ == "__main__": logger.info("应用程序启动") app = QApplication(sys.argv) + app.setStyle("Fusion") window = MainWindow() window.show() sys.exit(app.exec()) \ No newline at end of file