我有一个基本的 pyqt 视频播放器,但我希望视频时间线背景是视频当时发生的情况的图片,我必须使用什么才能有效地做到这一点?这是基本播放器:
import sys
from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtMultimediaWidgets import QVideoWidget
from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
class PyVideoPlayer(QWidget):
def __init__(self, parent=None):
super(PyVideoPlayer, self).__init__(parent)
self.mediaPlayer = QMediaPlayer()
self.audioOutput = QAudioOutput()
videoWidget = QVideoWidget()
self.playButton = QPushButton()
self.playButton.setIcon(QIcon("gui/images/svg_icons/icon_play.svg"))
self.playButton.setEnabled(False)
self.playButton.clicked.connect(self.play)
self.volumeButton = QPushButton()
self.volumeButton.setIcon(QIcon("gui/images/svg_icons/icon_volume.svg"))
self.volumeButton.clicked.connect(self.showVolumeSlider)
self.volumeSlider = QSlider(orientation=Qt.Horizontal)
self.volumeSlider.setRange(0, 100)
self.volumeSlider.setValue(self.audioOutput.volume() * 100)
self.volumeSlider.setMaximumWidth(100)
self.volumeSlider.sliderMoved.connect(self.setVolume)
self.volumeSlider.hide()
self.positionSlider = QSlider(orientation=Qt.Horizontal)
self.positionSlider.setRange(0, 0)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.timeLabel = QLabel()
self.timeLabel.setStyleSheet("color: #4f5b6e; font-family: 'Roboto'; font-size: 9pt; font-weight: bold;")
# Set up the layout
videoLayout = QVBoxLayout()
videoLayout.addWidget(videoWidget, stretch=1)
controlLayout = QHBoxLayout()
controlLayout.addWidget(self.playButton)
controlLayout.addWidget(self.volumeButton)
controlLayout.addWidget(self.volumeSlider)
controlLayout.addWidget(self.timeLabel)
layout = QVBoxLayout()
layout.addLayout(videoLayout)
layout.addWidget(self.positionSlider)
layout.addLayout(controlLayout)
layout.setContentsMargins(10, 0, 10, 0)
self.setLayout(layout)
self.mediaPlayer.setVideoOutput(videoWidget)
self.mediaPlayer.playbackStateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.errorOccurred.connect(self.handleError)
def setMedia(self, fileName):
self.mediaPlayer.setSource(QUrl.fromLocalFile(fileName))
self.mediaPlayer.setAudioOutput(self.audioOutput)
self.playButton.setEnabled(True)
self.play()
def play(self):
if self.mediaPlayer.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediaStateChanged(self, state):
if state == QMediaPlayer.PlaybackState.PlayingState:
self.playButton.setIcon(QIcon("gui/images/svg_icons/icon_pause.svg"))
else:
self.playButton.setIcon(QIcon("gui/images/svg_icons/icon_play.svg"))
def positionChanged(self, position):
self.positionSlider.setValue(position)
self.timeLabel.setText(f"{position // 60000:02}:{(position // 1000) % 60:02} / {self.mediaPlayer.duration() // 60000:02}:{(self.mediaPlayer.duration() // 1000) % 60:02}")
def durationChanged(self, duration):
self.positionSlider.setRange(0, duration)
def setPosition(self, position):
self.mediaPlayer.setPosition(position)
def setVolume(self, volume):
self.audioOutput.setVolume(volume / 100)
def showVolumeSlider(self):
if self.volumeSlider.isHidden():
self.volumeSlider.show()
else:
self.volumeSlider.hide()
def handleError(self):
self.playButton.setEnabled(False)
print("Error: " + self.mediaPlayer.errorString())
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = PyVideoPlayer()
ex.setMedia("your_video.mp4") # Replace with your video file path
ex.show()
sys.exit(app.exec_())
我尝试过不同的方法,例如使用 QVideoFrame,然后使用 QImage 转换为图像,但不确定它是否应该像这样使用。附上我希望时间线看起来如何的示例,任何帮助将不胜感激,谢谢。
您可以使用
QVideoSink
https://doc.qt.io/qtforpython-6/PySide6/QtMultimedia/QVideoSink.html 和 videoFrame()
函数从 QtMultimedia
获取帧数据
示例:
class TimeineWidget(QWidget):
def __init__(self):
super(TimeineWidget, self).__init__()
self.image = QLabel()
self.vlayout = QVBoxLayout()
self.resize(200, 150)
self.setStyleSheet("background: white;")
self.vlayout.addWidget(self.image)
self.setLayout(self.vlayout)
self.show()
def setImage(self, img):
pixmap = QPixmap(img)
self.image.setPixmap(pixmap.scaled(200, 150, Qt.AspectRatioMode.KeepAspectRatio))
class PyVideoPlayer():
# ...
def __init__(self, parent=None):
# ...
self.timeineWidget = TimeineWidget()
# ...
layout = QVBoxLayout()
layout.addLayout(videoLayout)
layout.addWidget(self.timeineWidget) # add timeline widget to layout
layout.addWidget(self.positionSlider)
layout.addLayout(controlLayout)
# Triggered in realtime while the video is playing
def positionChanged(self, position):
# ...
# update image timeline
if self.mediaPlayer.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
sink = self.mediaPlayer.videoSink()
self.timeineWidget.setImage(sink.videoFrame().toImage())
# Triggered by the position
def setPosition(self, position):
# ...
# update image timeline
if self.mediaPlayer.playbackState() != QMediaPlayer.PlaybackState.PlayingState:
sink = self.mediaPlayer.videoSink()
self.timeineWidget.setImage(sink.videoFrame().toImage())