Search code examples
pythonffmpegpyqt6qtmultimedia

PyQt6 6.7.0 - How to fix error: No QtMultimedia backends found


Problem on Windows 10 and Windows 11 using Anaconda.

Here is the full error message for PyQt6=6.7.0:

No QtMultimedia backends found. Only QMediaDevices, QAudioDevice, QSoundEffect, QAudioSink, and QAudioSource are available.
Failed to initialize QMediaPlayer "Not available"
Failed to create QVideoSink "Not available"

Installed PyQt6 using a requirements file:

PyQt6
PyQt6-WebEngine
requests
pyserial
pynput

Here are a couple things I tried:

  1. Reroll version back to PyQt6=6.6.1. This results in an error as well: ImportError: DLL load failed while importing QtGui: The specified procedure could not be found.
  2. I thought that missing ffmpeg might be the issue so I installed it, but the issue persists.
  3. Tried the setup on Ubuntu (WSL2) and the issue disappears, but there is just a black screen and nothing gets displayed in the widget. (EDIT: Got this up and running, the problem was with differences in file paths in linux vs windows.)

I am new to PyQt so any pointers will be helpful!

Edit: Here is generic code (taken from here) that gives the same error:

from PyQt6.QtGui import QIcon, QFont
from PyQt6.QtCore import QDir, Qt, QUrl, QSize
from PyQt6.QtMultimedia import QMediaPlayer
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel, QStyleFactory,
        QPushButton, QSizePolicy, QSlider, QStyle, QVBoxLayout, QWidget, QStatusBar)


class VideoPlayer(QWidget):

    def __init__(self, parent=None):
        super(VideoPlayer, self).__init__(parent)

        self.mediaPlayer = QMediaPlayer()

        btnSize = QSize(16, 16)
        videoWidget = QVideoWidget()

        openButton = QPushButton("Open Video")   
        openButton.setToolTip("Open Video File")
        openButton.setStatusTip("Open Video File")
        openButton.setFixedHeight(24)
        openButton.setIconSize(btnSize)
        openButton.setFont(QFont("Noto Sans", 8))
        openButton.setIcon(QIcon.fromTheme("document-open", QIcon("D:/_Qt/img/open.png")))
        openButton.clicked.connect(self.abrir)

        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setFixedHeight(24)
        self.playButton.setIconSize(btnSize)
        self.playButton.setIcon(self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

        self.positionSlider = QSlider(Qt.Orientation.Horizontal)
        self.positionSlider.setRange(0, 0)
        self.positionSlider.sliderMoved.connect(self.setPosition)

        self.statusBar = QStatusBar()
        self.statusBar.setFont(QFont("Noto Sans", 7))
        self.statusBar.setFixedHeight(14)

        controlLayout = QHBoxLayout()
        controlLayout.setContentsMargins(0, 0, 0, 0)
        controlLayout.addWidget(openButton)
        controlLayout.addWidget(self.playButton)
        controlLayout.addWidget(self.positionSlider)

        layout = QVBoxLayout()
        layout.addWidget(videoWidget)
        layout.addLayout(controlLayout)
        layout.addWidget(self.statusBar)

        self.setLayout(layout)

        #help(self.mediaPlayer)
        self.mediaPlayer.setVideoOutput(videoWidget)
        self.mediaPlayer.playbackStateChanged.connect(self.mediaStateChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.errorChanged.connect(self.handleError)
        self.statusBar.showMessage("Ready")

    def abrir(self):
        fileName, _ = QFileDialog.getOpenFileName(self, "Select Media",
                ".", "Video Files (*.mp4 *.flv *.ts *.mts *.avi)")

        if fileName != '':
            self.mediaPlayer.setSource(QUrl.fromLocalFile(fileName))
            self.playButton.setEnabled(True)
            self.statusBar.showMessage(fileName)
            self.play()

    def play(self):
        if self.mediaPlayer.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self, state):
        if self.mediaPlayer.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
            self.playButton.setIcon(
                    self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPause))
        else:
            self.playButton.setIcon(
                    self.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay))

    def positionChanged(self, position):
        self.positionSlider.setValue(position)

    def durationChanged(self, duration):
        self.positionSlider.setRange(0, duration)

    def setPosition(self, position):
        self.mediaPlayer.setPosition(position)

    def handleError(self):
        self.playButton.setEnabled(False)
        self.statusBar.showMessage("Error: " + self.mediaPlayer.errorString())

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    player = VideoPlayer()
    player.setWindowTitle("Player")
    player.resize(900, 600)
    player.show()
    sys.exit(app.exec())

The videos I want to play are in the same folder as this .py file. The conda env (python 3.9.2) I am working on has the following packages:

certifi                     2024.6.2
charset-normalizer          3.3.2
idna                        3.7
pip                         24.0
pynput                      1.7.6
PyQt6                       6.7.0
PyQt6-Qt6                   6.7.1
PyQt6-sip                   13.6.0
PyQt6-WebEngine             6.7.0
PyQt6-WebEngine-Qt6         6.7.1
PyQt6-WebEngineSubwheel-Qt6 6.7.1
pyserial                    3.5
requests                    2.31.0
setuptools                  69.5.1
six                         1.16.0
urllib3                     2.2.1
wheel                       0.43.0

PS: MacOS seems to have the same issue.


Solution

  • The issue with the backend is caused by the new version of PyQt6=6.7.0. Simply rerolling the version of PyQt6 and PyQt6-WebEngine to a previous version was not enough as sub-packages that were installed as a part of this were not rerolled automatically. This caused an issue due to package mismatch:

    PyQt6: DLL load failed while importing QtGui: The specified procedure could not be found.
    

    To fix this, reroll all the relevant packages manually. Here is the requirements.txt file I generated after I got it working:

    pynput==1.7.7
    PyQt6==6.6.0
    PyQt6-Qt6==6.6.0
    PyQt6-sip==13.6.0
    PyQt6-WebEngine==6.6.0
    PyQt6-WebEngine-Qt6==6.6.0
    PyQt6-WebEngineSubwheel-Qt6==6.7.1
    pyserial==3.5
    requests==2.32.3
    

    NOTE: I believe the devs are aware of this problem in and have resolved it. It will be released with the next version (check @musicamante's comment as well).