问题
I have an app that's designed to help synchronise an experiment video and data signals. The App has a video widget and a slider that can set the temporal position of the video. However, QMediaPlayer will only set the position at 500mSec/1000mSec intervals (in my app 500mSec/in the contrived app 1000mSec) while the video is shot at 50fps implying 20mSec intervals. This makes synchronisation pretty useless. I've added an output of the position before and after the position change.
Any help would be appreciated.
Here is the output when changing the slider:
New slider position 326
Media player updated position 0
New slider position 816
Media player updated position 0
New slider position 1306
Media player updated position 0
New slider position 1632
Media player updated position 1000
New slider position 1959
Media player updated position 1000
New slider position 2449
Media player updated position 2000
New slider position 2938
Media player updated position 2000
New slider position 3428
Media player updated position 2000
New slider position 3755
Media player updated position 3000
New slider position 4081
Media player updated position 3000
New slider position 4571
Media player updated position 4000
New slider position 4897
Media player updated position 4000
New slider position 5224
Media player updated position 4000
New slider position 5550
Media player updated position 5000
New slider position 5714
Media player updated position 5000
New slider position 6040
Media player updated position 5000
New slider position 6203
Media player updated position 6000
New slider position 6530
I've also tried setting specific positions that I retrieved from the mediaPlayer.position () to no avail.
Here is the contrived app:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSize, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import QMainWindow, QLabel, QWidget, QSlider
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout
#
# Reference https://pythonprogramminglanguage.com/pyqt5-video-widget/
#
class MainWindow (QMainWindow):
def __init__ (self, parent=None):
super (QMainWindow, self).__init__ (parent)
#
# Set the filename here
#
filename = '/Users/yuval/src/lab/S4560001.MP4'
self.setMinimumSize (QSize (640, 480))
self.setWindowTitle ('Video & Data Viewer and Annotator')
self.mediaPlayer = QMediaPlayer (None, QMediaPlayer.VideoSurface)
videoWidget = QVideoWidget ()
videoWidget.setMinimumSize (640,200)
#
# Position Slider
#
self.positionSlider = QSlider (Qt.Horizontal)
self.positionSlider.setRange (0, 0)
self.positionSlider.sliderMoved.connect (self.setPosition)
#
# Position Label
#
self.positionLabel = QLabel ('00:00:000')
centralWidget = QWidget (self)
self.setCentralWidget (centralWidget)
#
# Layout the Control Widgets
#
ctlLayout = QHBoxLayout ()
ctlLayout.setContentsMargins (0, 0, 0, 0)
ctlLayout.addWidget (self.positionSlider)
ctlLayout.addWidget (self.positionLabel)
#
# Layout the Window
#
layout = QVBoxLayout ()
layout.addWidget (videoWidget)
layout.addLayout (ctlLayout)
centralWidget.setLayout (layout)
self.mediaPlayer.setVideoOutput (videoWidget)
self.mediaPlayer.positionChanged.connect (self.positionChanged)
self.mediaPlayer.durationChanged.connect (self.durationChanged)
self.mediaPlayer.error.connect (self.handleError)
self.mediaPlayer.setNotifyInterval (100)
self.mediaPlayer.setMuted (True)
print (filename)
if filename != '':
self.mediaPlayer.setMedia (
QMediaContent (QUrl.fromLocalFile (filename)))
def positionChanged (self, position):
self.positionSlider.setValue (position)
def durationChanged (self, duration):
self.positionSlider.setRange (0, duration)
def setPosition (self, position):
print (f'New slider position {position}')
self.mediaPlayer.setPosition (position)
print (f'Media player updated position {self.mediaPlayer.position ()}')
def handleError (self):
print ('Error: ' + self.mediaPlayer.errorString ())
if __name__ == '__main__':
app = QtWidgets.QApplication (sys.argv)
mainWin = MainWindow ()
mainWin.show ()
sys.exit (app.exec_ ())
I'm running on MacOS 10.14.16 (Mojave).
Python 3.7.4
Python packages: PyQt5==5.13.0 PyQt5-sip==4.19.18
Ports packages: py37-pyqt5 @5.12.2_0 (active) qt5 @5.12.4_0 (active)
回答1:
Found the answer. The issue is in Qt5 and they "claim" it's not a bug, nor does it seem there are any plans to fix it. It's specific to MacOS and works okay on Windows (and I suspect Linux). The problem is that Qt5 uses a MacOS API that has time tolerance. By default it will only use "keyframes" while it is possible to limit its tolerance. Qt5 does not specify a tolerance therefore it adjusts the time to the keyframes.
If anyone out there is on the MacOS Qt5 development team, it would be nice if you can fix this. And yeah, if I had a development environment I would fix it myself (but I probably would have been voted down anyhow).
See (from 2015) https://bugreports.qt.io/browse/QTBUG-49609?jql=project%20%3D%20QTBUG%20AND%20resolution%20%3D%20Unresolved%20AND%20text%20~%20%22qmediaplayer%22%20ORDER%20BY%20priority%20DESC%2C%20updated%20DESC
The MacOS / AVFoundation API is https://developer.apple.com/documentation/avfoundation/avplayeritem/1387418-seek
来源:https://stackoverflow.com/questions/57889211/pyqt-qmediaplayer-setposition-rounds-the-position-value