如何在PyQt4中播放摄像头采集的实时视频?(树莓派3场景)
在PyQt4的Phonon中播放树莓派摄像头实时视频流
我来帮你搞定这个问题!在树莓派上用PyQt4的Phonon播放摄像头实时流,核心是利用Phonon的GStreamer后端(Linux下默认的后端)来直接读取摄像头的视频流,下面是具体的实现方案和修改后的代码:
前置准备
首先确保树莓派上安装了必要的依赖包:
sudo apt-get update sudo apt-get install python-pyqt4.phonon gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-tools
实现思路
Phonon本身不直接支持从摄像头捕获帧,但它可以通过GStreamer管道来播放实时流。树莓派的摄像头设备通常是/dev/video0,我们可以构造一个GStreamer管道,让Phonon直接播放这个管道的输出,这样就能实现实时视频流的播放。
修改后的代码
重点修改fourth_view函数,添加摄像头流的播放逻辑,以及启动/停止按钮的槽函数:
import sys from PyQt4 import QtGui, QtCore from PyQt4.phonon import Phonon class Example(QtGui.QWidget): def __init__(self): super(Example, self).__init__() self.setGeometry(10,10,640,480) # 扩大窗口以适配视频 self.first_box = QtGui.QVBoxLayout() self.second_box = QtGui.QVBoxLayout() self.third_box = QtGui.QVBoxLayout() vbox = QtGui.QVBoxLayout() vbox.addLayout(self.first_box) vbox.addLayout(self.second_box) vbox.addLayout(self.third_box) self.setLayout(vbox) self.first_view() self.setWindowTitle('Pupilometer') # 保存player实例,避免函数结束后被回收 self.video_player = None def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.close() def first_view(self): powerButton = QtGui.QPushButton("Power") settingButton = QtGui.QPushButton("Settings") videoButton = QtGui.QPushButton("Video") savedfileButton = QtGui.QPushButton("Saved Records") newButton = QtGui.QPushButton("New Record") powerButton.clicked.connect(self.first_second_view) settingButton.clicked.connect(self.first_third_view) videoButton.clicked.connect(self.first_fourth_view) savedfileButton.clicked.connect(self.first_fifth_view) newButton.clicked.connect(self.first_sixth_view) hbox = QtGui.QHBoxLayout() hbox.addWidget(newButton) hbox.addWidget(savedfileButton) hbox.addWidget(videoButton) hbox.addWidget(settingButton) hbox.addWidget(powerButton) font = QtGui.QFont() font.setBold(True) font.setPixelSize(12) lbl1 = QtGui.QLabel('Select an option', self) lbl1.setFont(font) lbl2 = QtGui.QLabel('Welcome to the Pupilometer', self) lbl2.setFont(font) self.first_box.addWidget(lbl1) self.second_box.addWidget(lbl2) self.third_box.addLayout(hbox) def first_second_view(self): print("first_second_view") self.clearLayout(self.second_box) self.second_view() def second_view(self): font = QtGui.QFont() font.setBold(True) font.setPixelSize(12) lbl1 = QtGui.QLabel('Power Off?', self) lbl1.setFont(font) yesButton = QtGui.QPushButton("Yes") noButton = QtGui.QPushButton("No") yesButton.clicked.connect(self.close_fun) noButton.clicked.connect(self.any_first_view) self.second_box.addWidget(lbl1) self.second_box.addWidget(yesButton) self.second_box.addWidget(noButton) def any_first_view(self): self.clearLayout(self.second_box) self.first_from_view() def first_from_view(self): font = QtGui.QFont() font.setBold(True) font.setPixelSize(12) lbl2 = QtGui.QLabel('Welcome to the Pupilometer', self) lbl2.setFont(font) self.second_box.addWidget(lbl2) def first_third_view(self): print("first_third_view") def first_fourth_view(self): print("first_fourth_view") self.clearLayout(self.second_box) self.fourth_view() def fourth_view(self): # 初始化Phonon视频播放器 self.video_player = Phonon.VideoPlayer(Phonon.VideoCategory, self) # 构造GStreamer管道:从摄像头读取视频,转换格式后输出 # 树莓派摄像头设备通常是/dev/video0,根据实际情况调整 gstreamer_pipeline = "v4l2src device=/dev/video0 ! videoconvert ! autovideosink" # 创建MediaSource,指定GStreamer管道 media_source = Phonon.MediaSource(gstreamer_pipeline) startButton = QtGui.QPushButton("Start") stopButton = QtGui.QPushButton("Stop") startButton.clicked.connect(lambda: self.start_video_stream(media_source)) stopButton.clicked.connect(self.stop_video_stream) hbox = QtGui.QHBoxLayout() hbox.addWidget(startButton) hbox.addWidget(stopButton) self.second_box.addWidget(self.video_player) self.second_box.addLayout(hbox) def start_video_stream(self, media_source): if self.video_player: self.video_player.load(media_source) self.video_player.play() def stop_video_stream(self): if self.video_player: self.video_player.stop() def first_fifth_view(self): print("first_fifth_view") def first_sixth_view(self): print("first_sixth_view") self.clearLayout(self.second_box) self.sixth_view() def sixth_view(self): print("hello") name = QtGui.QLabel('Name') age = QtGui.QLabel('Age') gender = QtGui.QLabel('Gender') patentID = QtGui.QLabel('Patient ID') nameEdit = QtGui.QLineEdit() ageEdit = QtGui.QLineEdit() patientIDEdit = QtGui.QLineEdit() self.b1 = QtGui.QRadioButton("Male") self.b2 = QtGui.QRadioButton("Female") self.b1.toggled.connect(lambda:self.btnstate(self.b1)) self.b2.toggled.connect(lambda:self.btnstate(self.b2)) grid = QtGui.QGridLayout() grid.setSpacing(10) grid.addWidget(name, 1, 0) grid.addWidget(nameEdit, 1, 1) grid.addWidget(age, 2, 0) grid.addWidget(ageEdit, 2, 1) grid.addWidget(gender,3,0) grid.addWidget(self.b1,3,1) grid.addWidget(self.b2,3,2) grid.addWidget(patentID, 4, 0) grid.addWidget(patientIDEdit, 4, 1) self.second_box.addLayout(grid) def btnstate(self,b): if b.text() == "Male": if b.isChecked(): print(f"{b.text()} is selected") else: print(f"{b.text()} is deselected") if b.text() == "Female": if b.isChecked(): print(f"{b.text()} is selected") else: print(f"{b.text()} is deselected") def clearLayout(self, layout): if layout is not None: while layout.count(): item = layout.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() else: self.clearLayout(item.layout()) def close_fun(self): print("Closing the Window") sys.exit() def main(): app = QtGui.QApplication(sys.argv) ex = Example() ex.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
关键修改点说明
- 将
video_player作为类的成员变量,避免在fourth_view函数结束后被垃圾回收,导致播放异常。 - 使用GStreamer管道
v4l2src device=/dev/video0 ! videoconvert ! autovideosink来读取摄像头流,这个管道会从摄像头设备读取视频,转换为Phonon能处理的格式,然后输出到视频窗口。 - 添加了
start_video_stream和stop_video_stream槽函数,分别处理播放和停止逻辑。 - 扩大了窗口尺寸,让视频显示更合适。
注意事项
- 如果你的摄像头设备不是
/dev/video0,可以通过ls /dev/video*命令查看实际设备路径,修改管道中的device参数。 - 确保树莓派的摄像头已经启用:可以通过
raspi-config的Interfacing Options->Camera来开启。
内容的提问来源于stack exchange,提问作者Anuj Rohilla
相关产品推荐
相关产品推荐

