PyQt QMediaPlayer setPosition округляет значение позиции

У меня есть приложение, предназначенное для синхронизации экспериментального видео и сигналов данных. Приложение имеет видео-виджет и ползунок, который может установить временное положение видео. Однако QMediaPlayer будет устанавливать положение только с интервалами 500 мс/1000 мс (в моем приложении 500 мс/в надуманном приложении 1000 мс), в то время как видео снимается со скоростью 50 кадров в секунду, что подразумевает интервалы 20 мс. Это делает синхронизацию довольно бесполезной. Я добавил вывод позиции до и после изменения позиции.

Любая помощь будет оценена по достоинству.

Вот вывод при изменении ползунка:

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

Я также пытался установить определенные позиции, которые я получил из mediaPlayer.position(), но безрезультатно.

Вот надуманное приложение:

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_ ())

Я использую MacOS 10.14.16 (Мохаве).

Питон 3.7.4

Пакеты Python: PyQt5==5.13.0 PyQt5-sip==4.19.18

Пакеты портов: py37-pyqt5 @5.12.2_0 (активный) qt5 @5.12.4_0 (активный)


person YuvGM    schedule 11.09.2019    source источник


Ответы (1)


Нашел ответ. Проблема в Qt5, и они «утверждают», что это не ошибка, и, похоже, нет никаких планов по ее исправлению. Это специфично для MacOS и нормально работает в Windows (и, я подозреваю, в Linux). Проблема в том, что Qt5 использует API MacOS, который допускает время. По умолчанию он будет использовать только «ключевые кадры», хотя его допуск можно ограничить. Qt5 не указывает допуск, поэтому время подстраивается под ключевые кадры.

Если кто-то есть в команде разработчиков MacOS Qt5, было бы неплохо, если бы вы могли это исправить. И да, если бы у меня была среда разработки, я бы исправил ее сам (но, вероятно, меня все равно проголосовали бы).

См. (от 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

MacOS/API AVFoundation: https://developer.apple.com/documentation/avfoundation/avplayeritem/1387418-seek

person YuvGM    schedule 16.09.2019
comment
Вы можете решить эту проблему, закодировав видео с меньшим количеством B-кадров и P-кадров. Т.е. использовать больше I-кадров. Особенно непонятно, нельзя ли искать B-кадры. Гораздо проще искать P-кадр; это также требует меньше памяти для воспроизведения, но размер файла увеличивается. - person artless noise; 29.03.2020