PyQT5 QAbstractVideoSurface: Play Video with alpha channel (on windows)

28 Views Asked by At

I want to play a video in PyQT with a transparent background using QAbstractVideoSurface.

I have found a program which works on Linux on this topic : Video with alpha channel overlay on background image: Alpha shows black

...but in windows it does not work and the background is black.

** Here's the script :**

from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class VideoWidget(QWidget):
    image = QImage()
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def setImage(self, image):
        self.image = image
        self.update()

    def sizeHint(self):
        return QSize(640, 480)

    def paintEvent(self, event):
        qp = QPainter(self)
        # ensure that smooth transformation is used while scaling pixmaps
        qp.setRenderHints(qp.SmoothPixmapTransform)

        # provide compliancy with background set using stylesheets, see:
        # https://doc.qt.io/qt-5/stylesheet-reference.html#qwidget-widget
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, qp, self)

        # draw the image, scaled to the widget size; if you need fixed sizes
        # or keep aspect ratio, implement this (or the widget) accordingly
        qp.drawImage(self.rect(), self.image, self.image.rect())

class AlphaVideoDrawer(QAbstractVideoSurface):

    def __init__(self, videoWidget=None, widgetOptions=None):
        super().__init__()
        if videoWidget:
            if not hasattr(videoWidget, 'setImage'):
                raise NotImplementedError(
                    'setImage() must be implemented for videoWidget!')
        else:
            if not isinstance(widgetOptions, dict):
                widgetOptions = {}
            elif not 'styleSheet' in widgetOptions:
                # just a default background for testing purposes
                widgetOptions = {'styleSheet': 'background: darkGray;'}
            videoWidget = VideoWidget(**widgetOptions)
        self.videoWidget = videoWidget

        # QVideoFrame.image() has been introduced since Qt 5.15
        version, majVersion, minVersion = map(int, QT_VERSION_STR.split('.'))
        if version < 6 and majVersion < 15:
            self.imageFromFrame = self._imageFromFrameFix
        else:
            self.imageFromFrame = lambda frame: frame.image()

    def _imageFromFrameFix(self, frame):
        clone_frame = QVideoFrame(frame)
        clone_frame.map(QAbstractVideoBuffer.ReadOnly)
        image = QImage(
            clone_frame.bits(), frame.width(), frame.height(), frame.bytesPerLine(), 
            QVideoFrame.imageFormatFromPixelFormat(frame.pixelFormat()))
        clone_frame.unmap()
        return image

    def supportedPixelFormats(self, type):
        return [QVideoFrame.Format_ARGB32]

    def present(self, frame: QVideoFrame):
        if frame.isValid():
            self.videoWidget.setImage(self.imageFromFrame(frame))

        if self.surfaceFormat().pixelFormat() != frame.pixelFormat() or \
            self.surfaceFormat().frameSize() != frame.size():
                self.setError(QAbstractVideoSurface.IncorrectFormatError)
                self.stop()
                return False
        else:
            return True


class AlphaVideoTest(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setStyleSheet('''
            QFrame#mainFrame {
                background: blue;
            }
        ''')

        mainFrame = QFrame(objectName='mainFrame')
        self.setCentralWidget(mainFrame)

        layout = QVBoxLayout(mainFrame)
        self.playButton = QPushButton('Play', checkable=True)
        layout.addWidget(self.playButton)

        self.drawer = AlphaVideoDrawer()
        layout.addWidget(self.drawer.videoWidget)

        self.mediaPlayer1 = QMediaPlayer(self, QMediaPlayer.VideoSurface)
        self.playlist = QMediaPlaylist(self)
        path = QDir.current().absoluteFilePath('vida1test.mov')
        self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(path)))
        self.playlist.setCurrentIndex(1)
        self.playlist.setPlaybackMode(QMediaPlaylist.CurrentItemInLoop)

        self.mediaPlayer1.setPlaylist(self.playlist)
        self.mediaPlayer1.setVideoOutput(self.drawer)

        self.playButton.toggled.connect(self.togglePlay)

    def togglePlay(self, play):
        if play:
            self.mediaPlayer1.play()
            self.playButton.setText('Pause')
        else:
            self.mediaPlayer1.pause()
            self.playButton.setText('Play')

import sys
app = QApplication(sys.argv)
test = AlphaVideoTest()
test.show()
sys.exit(app.exec_())

I tried with .mov, .avi, and webm files with VP8 and VP9 codec but it did not work at all. I have also installed K-Lite Codec Pack on my windows to read these files.

Is it even possible ?

Thank you very much if you can help me !

0

There are 0 best solutions below