show_prop.h

本文介绍了Windows应用程序中消息处理函数WndProc和特定资源ID的宏定义,包括退出、测试及关于对话框的处理。通过宏定义实现了资源标识符的指定,并展示了两个回调函数的声明。

  name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-5572165936844014&dt=1194442938015&lmt=1194190197&format=336x280_as&output=html&correlator=1194442937843&url=file%3A%2F%2F%2FC%3A%2FDocuments%2520and%2520Settings%2Flhh1%2F%E6%A1%8C%E9%9D%A2%2FCLanguage.htm&color_bg=FFFFFF&color_text=000000&color_link=000000&color_url=FFFFFF&color_border=FFFFFF&ad_type=text&ga_vid=583001034.1194442938&ga_sid=1194442938&ga_hid=1942779085&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=32&u_tz=480&u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency="allowtransparency"> #define IDM_EXIT           100
#define IDM_TEST           200
#define IDM_ABOUT          301

LRESULT CALLBACK WndProc  (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About    (HWND, UINT, WPARAM, LPARAM);

import cv2 import numpy as np import time import threading from queue import Queue from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * import sys class VideoPlayer(QMainWindow): def __init__(self): super().__init__() self.video_path = "" self.cap = None self.is_playing = False self.current_frame = 0 self.total_frames = 0 self.fps = 30 self.frame_queue = Queue(maxsize=30) self.video_thread = None self.timer = QTimer() self.was_playing = False # 新增:记录进度条拖动前的播放状态 self.init_ui() self.connect_signals() def init_ui(self): self.setWindowTitle("Premiere风格视频播放器") self.setGeometry(100, 100, 1000, 700) # 设置样式 self.setStyleSheet(""" QMainWindow { background-color: #2d2d2d; } QLabel { color: #ffffff; } QPushButton { background-color: #3d3d3d; color: white; border: 1px solid #555; padding: 8px; border-radius: 4px; min-width: 80px; } QPushButton:hover { background-color: #4d4d4d; border: 1px solid #666; } QPushButton:pressed { background-color: #2d2d2d; } QSlider::groove:horizontal { height: 6px; background: #444; border-radius: 3px; } QSlider::handle:horizontal { background: #666; width: 18px; height: 18px; margin: -6px 0; border-radius: 9px; } QSlider::handle:horizontal:hover { background: #888; } """) # 中心部件 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setContentsMargins(10, 10, 10, 10) main_layout.setSpacing(8) # 顶部菜单栏 menubar = self.menuBar() menubar.setStyleSheet(""" QMenuBar { background-color: #2d2d2d; color: white; border-bottom: 1px solid #3d3d3d; } QMenuBar::item:selected { background-color: #4d4d4d; } """) file_menu = menubar.addMenu("文件") open_action = QAction("打开视频", self) open_action.triggered.connect(self.open_video) file_menu.addAction(open_action) # 视频显示区域 self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setStyleSheet(""" background-color: #1a1a1a; border: 1px solid #333; border-radius: 4px; """) self.video_label.setMinimumHeight(400) main_layout.addWidget(self.video_label, stretch=1) # 信息显示区 info_layout = QHBoxLayout() info_layout.addWidget(QLabel("状态:")) self.status_label = QLabel("等待打开视频...") self.status_label.setStyleSheet("color: #888;") info_layout.addWidget(self.status_label) info_layout.addStretch() info_layout.addWidget(QLabel("帧率:")) self.fps_label = QLabel("-- fps") info_layout.addWidget(self.fps_label) info_layout.addWidget(QLabel(" | 分辨率:")) self.resolution_label = QLabel("-- x --") info_layout.addWidget(self.resolution_label) main_layout.addLayout(info_layout) # 控制按钮区 control_layout = QHBoxLayout() # 播放速度控制 speed_layout = QHBoxLayout() speed_layout.addWidget(QLabel("速度:")) self.speed_combo = QComboBox() self.speed_combo.addItems(["0.5x", "0.75x", "1.0x", "1.5x", "2.0x"]) self.speed_combo.setCurrentText("1.0x") self.speed_combo.setMaximumWidth(100) speed_layout.addWidget(self.speed_combo) control_layout.addLayout(speed_layout) control_layout.addStretch() self.btn_prev = QPushButton("← 上一帧") self.btn_play = QPushButton("▶ 播放") self.btn_next = QPushButton("下一帧 →") # 按钮样式 for btn in [self.btn_prev, self.btn_play, self.btn_next]: btn.setMinimumHeight(40) btn.setMinimumWidth(100) control_layout.addWidget(btn) control_layout.addStretch() main_layout.addLayout(control_layout) # 进度控制区 progress_layout = QHBoxLayout() progress_layout.addWidget(QLabel("进度:")) self.progress_slider = QSlider(Qt.Horizontal) self.progress_slider.setMinimum(0) self.progress_slider.singleStep = 1 self.progress_slider.setPageStep(10) self.progress_slider.valueChanged.connect(self._on_slider_changed) progress_layout.addWidget(self.progress_slider, stretch=1) self.time_label = QLabel("00:00.00 / 00:00.00") self.time_label.setMinimumWidth(150) progress_layout.addWidget(self.time_label) main_layout.addLayout(progress_layout) # 底部状态栏 self.setStatusBar(QStatusBar()) self.statusBar().setStyleSheet("background-color: #2d2d2d; color: #aaa;") def connect_signals(self): self.btn_prev.clicked.connect(self.prev_frame) self.btn_play.clicked.connect(self.toggle_play_pause) self.btn_next.clicked.connect(self.next_frame) self.progress_slider.sliderPressed.connect(self._on_slider_pressed) self.progress_slider.sliderReleased.connect(self._on_slider_released) self.timer.timeout.connect(self.update_frame) def open_video(self): """打开视频文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov *.mkv *.flv);;所有文件 (*.*)" ) if file_path: self.load_video(file_path) def load_video(self, video_path): """加载视频文件""" # 清理之前的资源 self.stop_playback() self.video_path = video_path # 打开视频文件 self.cap = cv2.VideoCapture(video_path) if not self.cap.isOpened(): QMessageBox.warning(self, "错误", "无法打开视频文件") return # 获取视频信息 self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) self.fps = self.cap.get(cv2.CAP_PROP_FPS) width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 更新UI信息 self.fps_label.setText(f"{self.fps:.1f} fps") self.resolution_label.setText(f"{width} x {height}") self.progress_slider.setMaximum(self.total_frames - 1) self.current_frame = 0 # 显示第一帧 self.show_frame() # 更新状态 video_name = video_path.split('/')[-1] if '/' in video_path else video_path.split('\\')[-1] self.status_label.setText(f"已加载: {video_name}") self.setWindowTitle(f"Premiere风格视频播放器 - {video_name}") def show_frame(self): """显示当前帧""" if self.cap is None: return self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame) ret, frame = self.cap.read() if ret: # 转换颜色空间 BGR -> RGB frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 调整图像大小以适应标签 h, w, ch = frame_rgb.shape bytes_per_line = ch * w qt_image = QImage(frame_rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) # 缩放图像以适应标签 scaled_image = qt_image.scaled( self.video_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.video_label.setPixmap(QPixmap.fromImage(scaled_image)) # 更新时间标签 current_time = self.current_frame / self.fps total_time = self.total_frames / self.fps self.time_label.setText( f"{self.format_time(current_time)} / {self.format_time(total_time)}" ) # 更新进度条 self.progress_slider.setValue(self.current_frame) def format_time(self, seconds): """格式化时间显示""" minutes = int(seconds // 60) seconds_remaining = seconds % 60 return f"{minutes:02d}:{seconds_remaining:06.2f}" def toggle_play_pause(self): """切换播放/暂停状态""" if self.cap is None: return if self.is_playing: self.pause_video() else: self.play_video() def play_video(self): """播放视频""" if self.cap is None or self.current_frame >= self.total_frames: return self.is_playing = True self.btn_play.setText("⏸ 暂停") # 启动视频更新定时器 speed_factor = float(self.speed_combo.currentText().replace('x', '')) interval = int(1000 / (self.fps * speed_factor)) self.timer.start(interval) def pause_video(self): """暂停视频""" self.is_playing = False self.btn_play.setText("▶ 播放") self.timer.stop() def stop_playback(self): """停止播放""" self.pause_video() if self.cap: self.cap.release() self.cap = None self.video_label.clear() self.video_label.setText("视频显示区域") self.status_label.setText("等待打开视频...") def update_frame(self): """更新视频帧""" if self.current_frame < self.total_frames - 1: self.current_frame += 1 self.show_frame() else: self.pause_video() self.current_frame = 0 def prev_frame(self): """跳转到上一帧""" if self.cap is None: return self.pause_video() self.current_frame = max(0, self.current_frame - 1) self.show_frame() def next_frame(self): """跳转到下一帧""" if self.cap is None: return self.pause_video() self.current_frame = min(self.total_frames - 1, self.current_frame + 1) self.show_frame() def _on_slider_pressed(self): """进度条被按下时暂停播放""" if self.is_playing: self.was_playing = True self.pause_video() else: self.was_playing = False def _on_slider_released(self): """进度条释放时跳转到指定位置""" if self.cap is None: return frame_pos = self.progress_slider.value() self.current_frame = frame_pos self.show_frame() if self.was_playing: self.play_video() def _on_slider_changed(self, value): """进度条值改变时更新当前帧""" if self.cap is None: return # 只有在鼠标拖动进度条时才更新帧 if self.progress_slider.isSliderDown(): self.current_frame = value self.show_frame() def resizeEvent(self, event): """窗口大小改变时重绘视频""" super().resizeEvent(event) if self.cap is not None: self.show_frame() def closeEvent(self, event): """窗口关闭时清理资源""" self.stop_playback() event.accept() def main(): app = QApplication(sys.argv) player = VideoPlayer() player.show() sys.exit(app.exec_()) if __name__ == "__main__": main() 多线程,加入音频逻辑,音频线程、音量滑块、音频播放 / 停止函数,并给我完整代码
最新发布
12-20
import sys import cv2 import numpy as np import wave import time import pyqtgraph as pg import subprocess import threading from queue import Queue from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap, QIcon, QFont from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QSlider, QComboBox, QGroupBox, QStatusBar, QTextEdit, QLineEdit, QMessageBox, QSplitter ) class VideoThread(QThread): change_pixmap = pyqtSignal(QImage) fps_signal = pyqtSignal(float) status_signal = pyqtSignal(str) def __init__(self, rtsp_url): super().__init__() self.rtsp_url = rtsp_url self._run_flag = True self.capture = None self.reconnect_attempts = 0 self.max_reconnect_attempts = 5 def run(self): while self._run_flag and self.reconnect_attempts < self.max_reconnect_attempts: try: self.status_signal.emit(f"尝试连接摄像头... (尝试 {self.reconnect_attempts + 1}/{self.max_reconnect_attempts})") # 使用FFmpeg后端以更好地支持RTSP流 self.capture = cv2.VideoCapture(self.rtsp_url, cv2.CAP_FFMPEG) if not self.capture.isOpened(): self.status_signal.emit("摄像头连接失败") self.reconnect_attempts += 1 time.sleep(2) continue self.status_signal.emit("摄像头连接成功") self.reconnect_attempts = 0 # 设置摄像头参数 self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) self.capture.set(cv2.CAP_PROP_FPS, 30) frame_count = 0 start_time = time.time() last_frame_time = start_time while self._run_flag: ret, frame = self.capture.read() current_time = time.time() if not ret: self.status_signal.emit("视频流中断,尝试重连...") break # 转换为RGB格式 rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w convert_to_qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format.Format_RGB888) p = convert_to_qt_format.scaled(1280, 720, Qt.AspectRatioMode.KeepAspectRatio) self.change_pixmap.emit(p) # 计算FPS frame_count += 1 elapsed_time = current_time - start_time if elapsed_time > 1.0: fps = frame_count / elapsed_time self.fps_signal.emit(fps) frame_count = 0 start_time = current_time # 检查帧间隔,避免卡顿 if current_time - last_frame_time > 2.0: # 超过2秒没有新帧 self.status_signal.emit("视频流卡顿,尝试重连...") break last_frame_time = current_time if self.capture: self.capture.release() except Exception as e: self.status_signal.emit(f"摄像头错误: {str(e)}") self.reconnect_attempts += 1 time.sleep(2) if self.reconnect_attempts >= self.max_reconnect_attempts: self.status_signal.emit("摄像头连接失败,请检查配置") else: self.status_signal.emit("视频流已停止") def stop(self): self._run_flag = False if self.capture and self.capture.isOpened(): self.capture.release() self.wait() class AudioCaptureThread(QThread): audio_data_signal = pyqtSignal(np.ndarray) status_signal = pyqtSignal(str) def __init__(self, rtsp_url): super().__init__() self.rtsp_url = rtsp_url self._run_flag = True self.audio_queue = Queue() self.ffmpeg_process = None def run(self): try: # 使用FFmpeg提取RTSP流中的音频 command = [ 'ffmpeg', '-i', self.rtsp_url, # 输入RTSP流 '-vn', # 不处理视频 '-ac', '1', # 单声道 '-ar', '44100', # 采样率 '-f', 's16le', # 输出格式为16位有符号小端PCM '-loglevel', 'quiet', # 不输出日志 'pipe:1' # 输出到标准输出 ] self.status_signal.emit("正在连接摄像头音频流...") self.ffmpeg_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) self.status_signal.emit("音频流已连接") chunk_size = 1024 * 2 # 1024个样本 * 2字节/样本 while self._run_flag: # 从FFmpeg输出读取原始音频数据 raw_audio = self.ffmpeg_process.stdout.read(chunk_size) if not raw_audio: break # 将字节数据转换为numpy数组 audio_data = np.frombuffer(raw_audio, dtype=np.int16) self.audio_data_signal.emit(audio_data) except Exception as e: self.status_signal.emit(f"音频错误: {str(e)}") finally: if self.ffmpeg_process: self.ffmpeg_process.terminate() self.ffmpeg_process.wait() self.status_signal.emit("音频流已停止") def stop(self): self._run_flag = False if self.ffmpeg_process: self.ffmpeg_process.terminate() self.wait() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("海康威视监控系统 - 视频与音频分析") self.setGeometry(100, 100, 1400, 900) # 设置应用图标 self.setWindowIcon(QIcon("monitor_icon.png")) # 替换为实际图标路径 # 创建主部件和布局 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 创建状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("就绪") # 创建RTSP配置区域 config_group = QGroupBox("海康威视摄像头配置") config_layout = QHBoxLayout() config_layout.addWidget(QLabel("RTSP URL:")) self.rtsp_url_edit = QLineEdit() self.rtsp_url_edit.setPlaceholderText("rtsp://username:password@ip:port/Streaming/Channels/101") self.rtsp_url_edit.setText("rtsp://admin:1q2w3e4r@192.168.1.64:554/Streaming/Channels/101") config_layout.addWidget(self.rtsp_url_edit, 70) self.test_btn = QPushButton("测试连接") self.test_btn.setIcon(QIcon("test.png")) config_layout.addWidget(self.test_btn) config_layout.addWidget(QLabel("分辨率:")) self.resolution_combo = QComboBox() self.resolution_combo.addItems(["1280x720", "1920x1080", "640x480"]) self.resolution_combo.setCurrentIndex(0) config_layout.addWidget(self.resolution_combo) config_layout.addWidget(QLabel("码率:")) self.bitrate_combo = QComboBox() self.bitrate_combo.addItems(["2048 Kbps", "4096 Kbps", "8192 Kbps"]) self.bitrate_combo.setCurrentIndex(1) config_layout.addWidget(self.bitrate_combo) config_group.setLayout(config_layout) main_layout.addWidget(config_group) # 创建视频和音频部分 - 修改为垂直布局 media_layout = QVBoxLayout() # 视频显示区域 video_group = QGroupBox("视频监控") video_layout = QVBoxLayout() self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.video_label.setMinimumSize(1280, 720) self.video_label.setStyleSheet("background-color: black;") video_controls_layout = QHBoxLayout() self.start_video_btn = QPushButton("开始视频") self.start_video_btn.setIcon(QIcon("video_start.png")) self.stop_video_btn = QPushButton("停止视频") self.stop_video_btn.setIcon(QIcon("video_stop.png")) self.stop_video_btn.setEnabled(False) self.brightness_slider = QSlider(Qt.Orientation.Horizontal) self.brightness_slider.setRange(0, 100) self.brightness_slider.setValue(50) self.brightness_slider.setTickPosition(QSlider.TickPosition.TicksBelow) self.brightness_slider.setTickInterval(10) self.contrast_slider = QSlider(Qt.Orientation.Horizontal) self.contrast_slider.setRange(0, 100) self.contrast_slider.setValue(50) self.contrast_slider.setTickPosition(QSlider.TickPosition.TicksBelow) self.contrast_slider.setTickInterval(10) video_controls_layout.addWidget(self.start_video_btn) video_controls_layout.addWidget(self.stop_video_btn) video_controls_layout.addWidget(QLabel("亮度:")) video_controls_layout.addWidget(self.brightness_slider) video_controls_layout.addWidget(QLabel("对比度:")) video_controls_layout.addWidget(self.contrast_slider) video_layout.addWidget(self.video_label) video_layout.addLayout(video_controls_layout) video_group.setLayout(video_layout) media_layout.addWidget(video_group, 70) # 视频占据70%空间 # 音频显示区域 - 移动到视频下方 audio_group = QGroupBox("音频波形") audio_layout = QVBoxLayout() # 音频波形图 - 修改高度限制和Y轴范围 self.audio_plot = pg.PlotWidget() self.audio_plot.setTitle("摄像头音频波形") self.audio_plot.setLabel('left', '振幅') self.audio_plot.setLabel('bottom', '样本') # 设置更大的Y轴范围并启用自动缩放 self.audio_plot.setYRange(-20000, 20000) self.audio_plot.enableAutoRange('y', 0.95) # 保留5%的边界 self.audio_plot.setXRange(0, 1024) self.audio_plot.setBackground('k') self.audio_plot.setMinimumHeight(300) # 增加最小高度 self.audio_curve = self.audio_plot.plot(pen='y') # 音频分析指标 audio_metrics_layout = QHBoxLayout() self.volume_label = QLabel("音量: -- dB") self.sample_rate_label = QLabel("采样率: -- Hz") self.peak_label = QLabel("峰值: --") for label in [self.volume_label, self.sample_rate_label, self.peak_label]: label.setFont(QFont("Arial", 10)) audio_metrics_layout.addWidget(label) audio_controls_layout = QHBoxLayout() self.start_audio_btn = QPushButton("开始音频") self.start_audio_btn.setIcon(QIcon("audio_start.png")) self.stop_audio_btn = QPushButton("停止音频") self.stop_audio_btn.setIcon(QIcon("audio_stop.png")) self.stop_audio_btn.setEnabled(False) self.volume_slider = QSlider(Qt.Orientation.Horizontal) self.volume_slider.setRange(0, 100) self.volume_slider.setValue(80) self.volume_slider.setTickPosition(QSlider.TickPosition.TicksBelow) self.volume_slider.setTickInterval(10) audio_controls_layout.addWidget(self.start_audio_btn) audio_controls_layout.addWidget(self.stop_audio_btn) audio_controls_layout.addWidget(QLabel("音量:")) audio_controls_layout.addWidget(self.volume_slider) audio_layout.addWidget(self.audio_plot) audio_layout.addLayout(audio_metrics_layout) audio_layout.addLayout(audio_controls_layout) audio_group.setLayout(audio_layout) media_layout.addWidget(audio_group, 30) # 音频占据30%空间 main_layout.addLayout(media_layout, 70) # 整个媒体区域占据70%空间 # 信息显示区域 info_layout = QHBoxLayout() # 系统状态 status_group = QGroupBox("系统状态") status_layout = QVBoxLayout() self.camera_status = QLabel("摄像头: 未连接") self.audio_status = QLabel("音频: 未连接") self.fps_label = QLabel("FPS: 0.0") self.resolution_label = QLabel("分辨率: --") self.bitrate_label = QLabel("码率: --") self.cpu_label = QLabel("CPU使用率: --%") self.mem_label = QLabel("内存使用: --MB") status_labels = [ self.camera_status, self.audio_status, self.fps_label, self.resolution_label, self.bitrate_label, self.cpu_label, self.mem_label ] for label in status_labels: label.setFont(QFont("Arial", 10)) status_layout.addWidget(label) status_group.setLayout(status_layout) # 操作日志 log_group = QGroupBox("操作日志") log_layout = QVBoxLayout() self.log_text = QTextEdit() self.log_text.setReadOnly(True) self.log_text.setFont(QFont("Consolas", 9)) log_layout.addWidget(self.log_text) log_group.setLayout(log_layout) info_layout.addWidget(status_group, 30) info_layout.addWidget(log_group, 70) main_layout.addLayout(info_layout, 30) # 信息区域占据30%空间 # 底部按钮 bottom_layout = QHBoxLayout() self.record_btn = QPushButton("开始录制") self.record_btn.setIcon(QIcon("record.png")) self.snapshot_btn = QPushButton("截图") self.snapshot_btn.setIcon(QIcon("camera.png")) self.settings_btn = QPushButton("PTZ控制") self.settings_btn.setIcon(QIcon("ptz.png")) self.exit_btn = QPushButton("退出") self.exit_btn.setIcon(QIcon("exit.png")) self.exit_btn.setStyleSheet("background-color: #ff6666; color: white;") bottom_layout.addWidget(self.record_btn) bottom_layout.addWidget(self.snapshot_btn) bottom_layout.addWidget(self.settings_btn) bottom_layout.addStretch() bottom_layout.addWidget(self.exit_btn) main_layout.addLayout(bottom_layout) # 初始化变量 self.video_thread = None self.audio_thread = None self.audio_data = np.zeros(1024, dtype=np.int16) self.is_recording = False self.video_writer = None self.audio_writer = None self.recording_start_time = 0 self.audio_frames = [] self.volume_history = [] # 连接信号和槽 self.start_video_btn.clicked.connect(self.start_video) self.stop_video_btn.clicked.connect(self.stop_video) self.start_audio_btn.clicked.connect(self.start_audio) self.stop_audio_btn.clicked.connect(self.stop_audio) self.snapshot_btn.clicked.connect(self.take_snapshot) self.record_btn.clicked.connect(self.toggle_recording) self.exit_btn.clicked.connect(self.close) self.test_btn.clicked.connect(self.test_connection) self.brightness_slider.valueChanged.connect(self.adjust_brightness) self.contrast_slider.valueChanged.connect(self.adjust_contrast) # 音频波形更新定时器 self.audio_timer = QTimer() self.audio_timer.timeout.connect(self.update_audio_plot) self.audio_timer.start(50) # 20Hz更新 # 系统状态更新定时器 self.status_timer = QTimer() self.status_timer.timeout.connect(self.update_system_status) self.status_timer.start(1000) # 1秒更新一次 # 录制定时器 self.record_timer = QTimer() self.record_timer.timeout.connect(self.update_recording_time) # 添加日志 self.log_message("应用程序已启动") self.log_message("使用FFmpeg从海康威视摄像头获取音频流") def start_video(self): if self.video_thread is None: rtsp_url = self.rtsp_url_edit.text().strip() if not rtsp_url: QMessageBox.warning(self, "配置错误", "请输入有效的RTSP URL") return self.video_thread = VideoThread(rtsp_url) self.video_thread.change_pixmap.connect(self.set_image) self.video_thread.fps_signal.connect(self.update_fps) self.video_thread.status_signal.connect(self.update_camera_status) self.video_thread.start() self.start_video_btn.setEnabled(False) self.stop_video_btn.setEnabled(True) self.log_message(f"视频流启动: {rtsp_url}") def stop_video(self): if self.video_thread: self.video_thread.stop() self.video_thread = None self.start_video_btn.setEnabled(True) self.stop_video_btn.setEnabled(False) self.camera_status.setText("摄像头: 未连接") self.fps_label.setText("FPS: 0.0") self.log_message("视频流已停止") def start_audio(self): if self.audio_thread is None: rtsp_url = self.rtsp_url_edit.text().strip() if not rtsp_url: QMessageBox.warning(self, "配置错误", "请输入有效的RTSP URL") return self.audio_thread = AudioCaptureThread(rtsp_url) self.audio_thread.audio_data_signal.connect(self.update_audio_data) self.audio_thread.status_signal.connect(self.update_audio_status) self.audio_thread.start() self.start_audio_btn.setEnabled(False) self.stop_audio_btn.setEnabled(True) self.log_message("音频流启动中...") def stop_audio(self): if self.audio_thread: self.audio_thread.stop() self.audio_thread = None self.start_audio_btn.setEnabled(True) self.stop_audio_btn.setEnabled(False) self.audio_status.setText("音频: 未连接") self.log_message("音频流已停止") def set_image(self, image): self.video_label.setPixmap(QPixmap.fromImage(image)) def update_audio_data(self, data): self.audio_data = data # 计算音量 (dB) if len(data) > 0: rms = np.sqrt(np.mean(np.square(data.astype(np.float32)))) if rms > 0: db = 20 * np.log10(rms / 32768.0) self.volume_label.setText(f"音量: {db:.1f} dB") self.volume_history.append(db) if len(self.volume_history) > 10: self.volume_history.pop(0) # 计算峰值 peak = np.max(np.abs(data)) self.peak_label.setText(f"峰值: {peak}") # 动态调整Y轴范围 max_peak = max(5000, peak * 1.2) # 确保最小值 self.audio_plot.setYRange(-max_peak, max_peak) # 更新采样率标签 self.sample_rate_label.setText("采样率: 44100 Hz") def update_audio_plot(self): if self.audio_thread: # 绘制音频波形 x = np.arange(len(self.audio_data)) self.audio_curve.setData(x, self.audio_data) def update_fps(self, fps): self.fps_label.setText(f"FPS: {fps:.1f}") def update_camera_status(self, message): self.camera_status.setText(f"摄像头: {message}") self.status_bar.showMessage(message, 3000) self.log_message(f"摄像头状态: {message}") def update_audio_status(self, message): self.audio_status.setText(f"音频: {message}") self.status_bar.showMessage(message, 3000) self.log_message(f"音频状态: {message}") def update_system_status(self): # 模拟系统状态更新 self.cpu_label.setText("CPU使用率: 32%") self.mem_label.setText("内存使用: 245MB") # 更新分辨率 resolution = self.resolution_combo.currentText() self.resolution_label.setText(f"分辨率: {resolution}") # 更新码率 bitrate = self.bitrate_combo.currentText() self.bitrate_label.setText(f"码率: {bitrate}") # 计算平均音量 if self.volume_history: avg_volume = np.mean(self.volume_history) self.status_bar.showMessage(f"平均音量: {avg_volume:.1f} dB", 1000) def take_snapshot(self): if self.video_thread and self.video_label.pixmap(): pixmap = self.video_label.pixmap() filename = f"snapshot_{time.strftime('%Y%m%d_%H%M%S')}.jpg" pixmap.save(filename) self.log_message(f"截图已保存: {filename}") self.status_bar.showMessage(f"截图已保存: {filename}", 3000) def toggle_recording(self): self.is_recording = not self.is_recording if self.is_recording: self.start_recording() else: self.stop_recording() def start_recording(self): try: # 视频录制 video_filename = f"recording_{time.strftime('%Y%m%d_%H%M%S')}.avi" fourcc = cv2.VideoWriter_fourcc(*'XVID') self.video_writer = cv2.VideoWriter(video_filename, fourcc, 20.0, (1280, 720)) # 音频录制 audio_filename = f"recording_{time.strftime('%Y%m%d_%H%M%S')}.wav" self.audio_frames = [] self.record_btn.setText("停止录制") self.record_btn.setIcon(QIcon("record_stop.png")) self.recording_start_time = time.time() self.record_timer.start(1000) self.log_message("开始录制视频和音频...") self.log_message(f"视频文件: {video_filename}") self.log_message(f"音频文件: {audio_filename}") except Exception as e: self.log_message(f"录制错误: {str(e)}") self.is_recording = False def stop_recording(self): if self.video_writer: self.video_writer.release() self.video_writer = None if self.audio_frames: audio_filename = f"recording_{time.strftime('%Y%m%d_%H%M%S')}.wav" wf = wave.open(audio_filename, 'wb') wf.setnchannels(1) wf.setsampwidth(2) # 16-bit = 2 bytes wf.setframerate(44100) wf.writeframes(b''.join(self.audio_frames)) wf.close() self.audio_frames = [] self.log_message(f"音频已保存: {audio_filename}") self.record_btn.setText("开始录制") self.record_btn.setIcon(QIcon("record.png")) self.record_timer.stop() self.log_message("录制已停止") def update_recording_time(self): elapsed = int(time.time() - self.recording_start_time) mins, secs = divmod(elapsed, 60) self.status_bar.showMessage(f"录制中: {mins:02d}:{secs:02d}") def test_connection(self): rtsp_url = self.rtsp_url_edit.text().strip() if not rtsp_url: QMessageBox.warning(self, "配置错误", "请输入有效的RTSP URL") return self.log_message(f"测试连接: {rtsp_url}") cap = cv2.VideoCapture(rtsp_url, cv2.CAP_FFMPEG) if cap.isOpened(): ret, frame = cap.read() if ret: self.log_message("连接测试成功: 摄像头响应正常") QMessageBox.information(self, "连接测试", "摄像头连接成功!") else: self.log_message("连接测试失败: 无法读取视频帧") QMessageBox.warning(self, "连接测试", "连接成功但无法读取视频帧") else: self.log_message("连接测试失败: 无法打开摄像头") QMessageBox.critical(self, "连接测试", "无法连接摄像头") cap.release() def adjust_brightness(self, value): # 在实际应用中,这里应该通过SDK或API调整摄像头亮度 self.log_message(f"调整亮度: {value}") self.status_bar.showMessage(f"亮度调整为: {value}", 2000) def adjust_contrast(self, value): # 在实际应用中,这里应该通过SDK或API调整摄像头对比度 self.log_message(f"调整对比度: {value}") self.status_bar.showMessage(f"对比度调整为: {value}", 2000) def log_message(self, message): timestamp = time.strftime("%H:%M:%S") self.log_text.append(f"[{timestamp}] {message}") self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum()) def closeEvent(self, event): self.stop_video() self.stop_audio() if self.is_recording: self.stop_recording() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) 在这个代码基础上增加声音播放
06-06
注释 class BatchVideoDetector(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("TPVideoDetector vision:1.0.0") self.setGeometry(0, 0, 1280, 720) self.width_spin = QSpinBox(self) self.height_spin = QSpinBox(self) self.detect_threshold_spin = QDoubleSpinBox(self) # 分辨率设置控件 self.width_spin.setRange(100, 1920) self.width_spin.setSingleStep(32) self.width_spin.setValue(512) self.height_spin.setRange(100, 1080) self.height_spin.setSingleStep(32) self.height_spin.setValue(288) self.size = (512, 288) self.detect_threshold_spin.setRange(0.0, 1.0) self.detect_threshold_spin.setSingleStep(0.01) self.detect_threshold_spin.setValue(0.3) self.detect_threshold = 0.3 self.is_save_video_spin = QCheckBox('是否保存到mp4文件', self) self.is_save_video_spin.stateChanged.connect(self.save_video) self.is_save_video_spin.setChecked(True) self.is_save_video = True self.is_save_txt_spin = QCheckBox('是否保存结果到txt文件', self) self.is_save_txt_spin.stateChanged.connect(self.save_txt) self.is_save_txt_spin.setChecked(True) self.is_save_txt = True # UI组件 self.video_list = QListWidget(self) self.video_list.addItem("待处理视频列表") self.video_label = QLabel("视频显示区域", self) self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setFixedSize(960, 540) self.video_label.setStyleSheet("QLabel { border: 1px solid black; }") self.btn_add = QPushButton("选择输入目录", self) self.btn_output = QPushButton("选择输出目录", self) self.btn_start = QPushButton("开始批量检测", self) self.progress_current = QProgressBar(self) self.progress_total = QProgressBar(self) self.result_label = QLabel("准备中...", self) self.result_label.setAlignment(Qt.AlignCenter) self.result_label.setFixedSize(960, 25) self.result_label.setStyleSheet("QLabel { border: 1px solid black; }") layout1 = QHBoxLayout() # 布局 main_layout = QVBoxLayout() video_layout = QVBoxLayout() video_layout.addWidget(self.result_label) video_layout.addWidget(self.video_label) layout1.addLayout(video_layout) layout1.addWidget(self.video_list) main_layout.addLayout(layout1) main_layout.addWidget(QLabel("当前视频进度:")) main_layout.addWidget(self.progress_current) main_layout.addWidget(QLabel("总任务进度:")) main_layout.addWidget(self.progress_total) control_layout = QHBoxLayout() control_layout.addWidget(self.btn_add) control_layout.addWidget(self.btn_output) control_layout.addWidget(self.btn_start) param_layout = QHBoxLayout() param_layout.addWidget(QLabel("输入宽度:")) param_layout.addWidget(self.width_spin) param_layout.addWidget(QLabel("输入高度:")) param_layout.addWidget(self.height_spin) param_layout.addWidget(QLabel("检测阈值:")) param_layout.addWidget(self.detect_threshold_spin) select_layout1 = QHBoxLayout() select_layout1.addWidget(self.is_save_video_spin) select_layout2 = QHBoxLayout() select_layout2.addWidget(self.is_save_txt_spin) main_layout.addLayout(control_layout) main_layout.addLayout(param_layout) main_layout.addLayout(select_layout1) main_layout.addLayout(select_layout2) container = QWidget() container.setLayout(main_layout) self.setCentralWidget(container) # 事件绑定 self.btn_add.clicked.connect(self.select_input_dir) self.btn_output.clicked.connect(self.select_output_dir) self.btn_start.clicked.connect(self.start_batch_detection) self.width_spin.valueChanged.connect(self.adjust_width) self.height_spin.valueChanged.connect(self.adjust_height) self.detect_threshold_spin.valueChanged.connect(self.adjust_detect_threshold) # 初始化变量 self.video_paths = [] self.total_frames = 0 self.output_dir = "" self.worker = None self.start_time = 0
11-14
import sys import cv2 import numpy as np import librosa import soundfile as sf from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QSlider, QSplitter, QFrame) from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap, QPainter, QPen, QBrush class AudioProcessingThread(QThread): 信号:处理完成的音频波形数据 waveform_ready = pyqtSignal(np.ndarray, np.ndarray, int) def init(self, audio_file): super().init() self.audio_file = audio_file def run(self): “”“加载音频文件并提取波形数据”“” try: # 使用librosa加载音频 y, sr = librosa.load(self.audio_file, sr=None, mono=True) # 使用soundfile获取音频信息 info = sf.info(self.audio_file) duration = info.duration self.waveform_ready.emit(y, np.arange(len(y)), sr) except Exception as e: print(f"音频处理错误: {e}") self.waveform_ready.emit(np.array([]), np.array([]), 0) class VideoPlayer(QMainWindow): def init(self): super().init() self.is_playing = False self.cap = None self.audio_file = “” self.current_frame = 0 self.total_frames = 0 self.fps = 30 self.audio_waveform = None self.audio_timestamps = None self.sample_rate = 0 self.init_ui() self.timer = QTimer(self) self.timer.timeout.connect(self.update_frame) def init_ui(self): self.setWindowTitle(“Premiere风格视频播放器”) self.setGeometry(100, 100, 1200, 800) # 中心部件 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) main_layout.setContentsMargins(10, 10, 10, 10) main_layout.setSpacing(8) # 创建分割器(视频在上,音频波形在下) splitter = QSplitter(Qt.Vertical) main_layout.addWidget(splitter, stretch=1) # 视频显示区域 video_frame = QFrame() video_frame.setStyleSheet(“background-color: #1a1a1a; border: 1px solid #333;”) video_layout = QVBoxLayout(video_frame) video_layout.setContentsMargins(0, 0, 0, 0) self.video_label = QLabel() self.video_label.setAlignment(Qt.AlignCenter) self.video_label.setMinimumSize(800, 450) video_layout.addWidget(self.video_label) splitter.addWidget(video_frame) # 音频波形显示区域 audio_frame = QFrame() audio_frame.setStyleSheet(“background-color: #0a0a0a; border: 1px solid #222;”) audio_layout = QVBoxLayout(audio_frame) audio_layout.setContentsMargins(0, 0, 0, 0) self.audio_wave_label = QLabel() self.audio_wave_label.setAlignment(Qt.AlignCenter) self.audio_wave_label.setMinimumHeight(150) audio_layout.addWidget(self.audio_wave_label) splitter.addWidget(audio_frame) # 控制按钮区 control_layout = QHBoxLayout() self.btn_open = QPushButton(“打开文件”) self.btn_open.clicked.connect(self.open_file) self.btn_prev = QPushButton(“← 上一帧”) self.btn_prev.clicked.connect(self.prev_frame) self.btn_play = QPushButton(“▶ 播放”) self.btn_play.clicked.connect(self.toggle_play_pause) self.btn_next = QPushButton(“下一帧 →”) self.btn_next.clicked.connect(self.next_frame) # 按钮样式 for btn in [self.btn_open, self.btn_prev, self.btn_play, self.btn_next]: btn.setMinimumHeight(40) btn.setStyleSheet(“”" QPushButton { font-size: 14px; padding: 5px 15px; background-color: #333; color: #eee; border: 1px solid #555; } QPushButton:hover { background-color: #444; } “”“) control_layout.addWidget(self.btn_open) control_layout.addWidget(self.btn_prev) control_layout.addWidget(self.btn_play) control_layout.addWidget(self.btn_next) control_layout.addStretch() main_layout.addLayout(control_layout) # 进度控制区 progress_layout = QHBoxLayout() progress_layout.addWidget(QLabel(“进度:”)) self.progress_slider = QSlider(Qt.Horizontal) self.progress_slider.setMinimum(0) self.progress_slider.setMaximum(100) self.progress_slider.sliderPressed.connect(self._on_slider_pressed) self.progress_slider.sliderReleased.connect(self._on_slider_released) self.progress_slider.valueChanged.connect(self._on_slider_changed) progress_layout.addWidget(self.progress_slider, stretch=1) self.time_label = QLabel(“00:00.00 / 00:00.00”) self.time_label.setMinimumWidth(150) self.time_label.setStyleSheet(“color: #aaa;”) progress_layout.addWidget(self.time_label) main_layout.addLayout(progress_layout) # 设置初始状态 self.update_controls() def open_file(self): “”“打开视频文件并初始化播放器””" from PyQt5.QtWidgets import QFileDialog file_path, _ = QFileDialog.getOpenFileName( self, “打开视频文件”, “”, “视频文件 (*.mp4 *.avi .mov .mkv);;所有文件 (.)” ) if file_path: # 释放之前的资源 if self.cap: self.cap.release() # 打开新视频 self.cap = cv2.VideoCapture(file_path) if not self.cap.isOpened(): print(“无法打开视频文件”) return # 获取视频信息 self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) self.fps = self.cap.get(cv2.CAP_PROP_FPS) self.current_frame = 0 # 设置进度条范围 self.progress_slider.setMaximum(self.total_frames - 1) # 处理音频 self.audio_file = file_path self.process_audio() # 显示第一帧 self.show_frame(self.current_frame) self.update_time_label() # 更新按钮状态 self.update_controls() def process_audio(self): “”“在后台线程中处理音频”“” self.audio_thread = AudioProcessingThread(self.audio_file) self.audio_thread.waveform_ready.connect(self.on_audio_processed) self.audio_thread.start() def on_audio_processed(self, waveform, timestamps, sample_rate): “”“接收处理完成的音频数据”“” self.audio_waveform = waveform self.audio_timestamps = timestamps self.sample_rate = sample_rate self.draw_audio_waveform() def draw_audio_waveform(self): “”“绘制音频波形图”“” if self.audio_waveform is None or len(self.audio_waveform) == 0: return # 创建空图像 width, height = self.audio_wave_label.width(), self.audio_wave_label.height() if width <= 0 or height <= 0: return image = QImage(width, height, QImage.Format_RGB32) image.fill(Qt.black) painter = QPainter(image) painter.setPen(QPen(Qt.green, 1)) # 绘制中心线 center_y = height // 2 painter.drawLine(0, center_y, width, center_y) # 绘制波形 if len(self.audio_waveform) > 0: step = max(1, len(self.audio_waveform) // width) points = [] for i in range(0, len(self.audio_waveform), step): x = int(i * width / len(self.audio_waveform)) y_val = self.audio_waveform[i] y = int(center_y - y_val * center_y * 0.8) points.append((x, y)) # 绘制波形线 for i in range(1, len(points)): painter.drawLine(points[i - 1][0], points[i - 1][1], points[i][0], points[i][1]) # 绘制当前播放位置指示器 if self.cap and self.current_frame > 0: frame_time = self.current_frame / self.fps audio_position = int(frame_time * self.sample_rate) x_pos = int(audio_position * width / len(self.audio_waveform)) painter.setPen(QPen(Qt.red, 2)) painter.drawLine(x_pos, 0, x_pos, height) painter.end() self.audio_wave_label.setPixmap(QPixmap.fromImage(image)) def resizeEvent(self, event): “”“窗口大小改变时重绘音频波形”“” super().resizeEvent(event) self.draw_audio_waveform() def show_frame(self, frame_num): “”“显示指定帧”“” if not self.cap: return self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num) ret, frame = self.cap.read() if ret: # 转换OpenCV BGR格式为RGB frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = frame.shape bytes_per_line = ch * w q_img = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(q_img).scaled( self.video_label.width(), self.video_label.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation )) # 更新音频波形中的位置指示器 self.draw_audio_waveform() def update_frame(self): “”“定时器回调,更新视频帧”“” if not self.is_playing or not self.cap: return self.current_frame += 1 if self.current_frame >= self.total_frames: self.current_frame = 0 self.is_playing = False self.timer.stop() self.update_controls() self.show_frame(self.current_frame) self.progress_slider.setValue(self.current_frame) self.update_time_label() def toggle_play_pause(self): “”“切换播放/暂停状态”“” self.is_playing = not self.is_playing if self.is_playing: self.timer.start(int(1000 / self.fps)) self.btn_play.setText(“⏸ 暂停”) else: self.timer.stop() self.btn_play.setText(“▶ 播放”) def prev_frame(self): “”“跳转到上一帧”“” self.current_frame = max(0, self.current_frame - 1) self.show_frame(self.current_frame) self.progress_slider.setValue(self.current_frame) self.update_time_label() def next_frame(self): “”“跳转到下一帧”“” self.current_frame = min(self.total_frames - 1, self.current_frame + 1) self.show_frame(self.current_frame) self.progress_slider.setValue(self.current_frame) self.update_time_label() def update_time_label(self): “”“更新时间标签”“” if not self.cap: return current_time = self.current_frame / self.fps total_time = self.total_frames / self.fps current_min, current_sec = divmod(current_time, 60) total_min, total_sec = divmod(total_time, 60) self.time_label.setText( f"{int(current_min):02d}:{current_sec:06.3f} / {int(total_min):02d}:{total_sec:06.3f}" ) def update_controls(self): “”“更新控件状态”“” has_video = self.cap is not None and self.cap.isOpened() self.btn_play.setEnabled(has_video) self.btn_prev.setEnabled(has_video) self.btn_next.setEnabled(has_video) self.progress_slider.setEnabled(has_video) if not has_video: self.btn_play.setText(“▶ 播放”) def _on_slider_pressed(self): “”“进度条被按下时暂停播放”“” self.was_playing = self.is_playing if self.is_playing: self.toggle_play_pause() def _on_slider_released(self): “”“进度条释放时跳转到指定位置”“” frame_num = self.progress_slider.value() self.current_frame = frame_num self.show_frame(frame_num) self.update_time_label() if self.was_playing: self.toggle_play_pause() def on_slider_changed(self, value): “”“进度条值改变时更新当前帧(但不跳转)”“” self.current_frame = value self.update_time_label() def closeEvent(self, event): “”“窗口关闭时释放资源”“” if self.cap: self.cap.release() if hasattr(self, ‘audio_thread’) and self.audio_thread.isRunning(): self.audio_thread.terminate() super().closeEvent(event) if name == “main”: app = QApplication(sys.argv) player = VideoPlayer() player.show() sys.exit(app.exec()) 报错:E:\Pygongcheng\waijieku\ZiDonghua.venv\Scripts\python.exe E:\Pygongcheng\waijieku\ZiDonghua\ShiPingJianJi\LianXi\Pyqt5Lianxi\P2lian.py E:\Pygongcheng\waijieku\ZiDonghua\ShiPingJianJi\LianXi\Pyqt5Lianxi\P2lian.py:24: UserWarning: PySoundFile failed. Trying audioread instead. y, sr = librosa.load(self.audio_file, sr=None, mono=True) E:\Pygongcheng\waijieku\ZiDonghua.venv\Lib\site-packages\librosa\core\audio.py:184: FutureWarning: librosa.core.audio.__audioread_load Deprecated as of librosa version 0.10.0. It will be removed in librosa version 1.0. y, sr_native = __audioread_load(path, offset, duration, dtype) 进程已结束,退出代码为 0 QMediaPlayer和QVideoWidget来替换OpenCV里关于声音部分,其他不用动,去掉所有音频波形的部分
12-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值