Search code examples
pythonwindowspyside2qtmultimediavideo-codecs

QMediaPlayer displays audio only for certain videos


I'm using QMediaPlayer according to the example in the docs.

Here's my code:

from PySide2 import QtWidgets
from PySide2 import QtCore
from PySide2.QtMultimedia import QMediaPlayer
from PySide2.QtMultimediaWidgets import QVideoWidget


class App(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.player = QMediaPlayer()
        self.video = QVideoWidget(self)
        self.player.setMedia(QtCore.QUrl.fromLocalFile("D:/path/to/file.webm"))
        self.player.setVideoOutput(self.video)
        self.video.show()
        self.player.play()

    def closeEvent(self, event):
        self.player.stop()
        event.accept()


if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    window = App()
    window.show()
    app.exec_()

Overriding closeEvent was necessary to stop the program hanging on close.

My test videos:

  • Worked fine:
    • 1080p 60fps vp9/opus webm
    • 1080p 30fps vp9/opus webm
    • 480p 30fps vp9/opus webm
  • Played audio only (blank window appears):
    • 1080p 30fps H.264/aac mp4
    • 480p 30fps H.264/aac mp4
    • 1080p 23.98fps H.264/ac3 mp4
    • 1080p 23.98fps H.264/ac3 mkv
    • 1080p 23.98fps vp9/opus webm
    • 720x404 23.98fps vp9/opus webm

I'm using qt5 on windows and have installed the appropriate codecs (the K-Lite Codec Pack) to play these files (they play in windows media player fine).

Edit:
I've connected to the mediaStatusChanged, videoAvailableChanged and error signals as suggested by @ekhumoro. I get video available as True and mediaStatus as BufferedMedia as soon as I run the program, regardless of whether it is actually displaying the video. The error signal handler is never being triggered.

During these further tests, videos in the "worked fine" section have been occassionaly failing to display video or, even more weirdly, displaying fine but at the minimum window size:

small window

Resizing the window doesn't increase the video's size. No videos in the "played audio only" section of my original tests have ever displayed video so far.

Edit 2:

After a restart (and updated graphics drivers), the first run with a video that didn't originally display video at all displayed in the weird small window. Subsequent runs (I've done 10 or so) have reverted to no video. Similarly a video that originally worked fine had no video on it's first run but is now consistantly small. A different video that originally worked is still playing normally.


Solution

  • The problem was to do with sizing as @ekhumoro suggested.

    When the QWidget I had inherited tried to work out its size it asked the QVideoWidget that was its only child for its size. Sometimes the QVideoWidget had loaded its video and so provided the correct size but other times it hadn't and so gave a sizeHint of (-1, -1). Because the QVideoWidget wasn't in a layout it wasn't able to resize itself and was stuck too small.

    Additionally, the window itself isn't resized by layouts automatically once created (only by the user, or explicitly with adjustSize) so the window stays at its original size even with the QVideoWidget in a layout. The solution is to register a signal handler for the mediaStatusChanged and call updateGeometry and adjustSize.

    Finally the other issue with the program hanging on exit was because I hadn't parented the QMediaPlayer on my window.

    My modified class:

    class App(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            self.player = QMediaPlayer(self)
            self.video = QVideoWidget(self)
            self.player.setMedia(QtCore.QUrl.fromLocalFile("D:/path/to/video.webm"))
            self.player.setVideoOutput(self.video)
            self.player.mediaStatusChanged.connect(self.video_available_changed)
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.video)
            self.player.play()
    
        def video_available_changed(self, available):
            if available:
                self.video.updateGeometry()
                self.video.adjustSize()
                self.adjustSize()