[PyQt] PyQt5 custom video player using QAbstractVideoSurface
Timothy W. Grove
tim_grove at sil.org
Fri Oct 4 11:02:26 BST 2013
Hello Folks!
I'm not a C++ programmer, but below is my best attempt at translating a
C++ code example found in QtEnterprise
for creating a custom video widget using a custom video surface. Please
feel free to use it ... if you can get it to
work!
When I run the code the widget appears okay. The only error I'm getting
is "QWidget::paintEngine: Should no longer be called",
but I'm not getting any video playback when I open a file. Looking at
the code I don't see anything visible that would 'start' the
video surface, but I'm guessing that that is the responsibility of the
QMediaPlayer instance? (self.mediaPlayer)
If anyone has any suggestions, I'd love to hear them.
I'm programming under Windows 7, with Python 3.3.2 and PyQt5-5.1, 32 bit.
Best regards,
Timothy Grove
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""The Video Widget example shows how to implement a video widget
using QtMultimedia's QAbstractVideoSurface.
The following is a translation into PyQt5 from the C++ example found in
C:\QtEnterprise\5.1.1\msvc2010\examples\multimediawidgets\customvideosurface\customvideowidget."""
import sys
import os
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtMultimedia import *
from PyQt5.QtMultimediaWidgets import *
class VideoWidgetSurface(QAbstractVideoSurface):
def __init__(self, widget, parent=None):
super(VideoWidgetSurface, self).__init__(parent)
self.widget = widget
self.imageFormat = QImage.Format_Invalid
def supportedPixelFormats(self,
handleType=QAbstractVideoBuffer.NoHandle):
formats = [QVideoFrame.PixelFormat()]
if (handleType == QAbstractVideoBuffer.NoHandle):
for f in [QVideoFrame.Format_RGB32,
QVideoFrame.Format_ARGB32,
QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555
]:
formats.append(f)
return formats
def isFormatSupported(self, _format):
imageFormat =
QVideoFrame.imageFormatFromPixelFormat(_format.pixelFormat())
size = _format.frameSize()
_bool = False
if (imageFormat != QImage.Format_Invalid and not
size.isEmpty() and
_format.handleType() == QAbstractVideoBuffer.NoHandle):
_bool = True
return _bool
def start(self, _format):
imageFormat =
QVideoFrame.imageFormatFromPixelFormat(_format.pixelFormat())
size = _format.frameSize()
if (imageFormat != QImage.Format_Invalid and not size.isEmpty()):
self.imageFormat = imageFormat
self.imageSize = size
self.sourceRect = _format.viewport()
QAbstractVideoSurface.start(self, _format)
self.widget.updateGeometry()
self.updateVideoRect()
return True
else:
return False
def stop(self):
self.currentFrame = QVideoFrame()
self.targetRect = QRect()
QAbstractVideoSurface.stop(self)
self.widget.update()
def present(self, frame):
if (self.surfaceFormat().pixelFormat() != frame.pixelFormat() or
self.surfaceFormat().frameSize() != frame.size()):
self.setError(QAbstractVideoSurface.IncorrectFormatError)
self.stop()
return False
else:
self.currentFrame = frame
self.widget.repaint(self.targetRect)
return True
def videoRect(self):
return self.targetRect
def updateVideoRect(self):
size = self.surfaceFormat().sizeHint()
size.scale(self.widget.size().boundedTo(size), Qt.KeepAspectRatio)
self.targetRect = QRect(QPoint(0, 0), size);
self.targetRect.moveCenter(self.widget.rect().center())
def paint(self, painter):
if (self.currentFrame.map(QAbstractVideoBuffer.ReadOnly)):
oldTransform = painter.transform()
if (self.surfaceFormat().scanLineDirection() ==
QVideoSurfaceFormat.BottomToTop):
painter.scale(1, -1);
painter.translate(0, -self.widget.height())
image = QImage(self.currentFrame.bits(),
self.currentFrame.width(),
self.currentFrame.height(),
self.currentFrame.bytesPerLine(),
self.imageFormat
)
painter.drawImage(self.targetRect, image, self.sourceRect)
painter.setTransform(oldTransform)
self.currentFrame.unmap()
class VideoWidget(QWidget):
def __init__(self, parent=None):
super(VideoWidget, self).__init__(parent)
self.setAutoFillBackground(False)
self.setAttribute(Qt.WA_NoSystemBackground, True)
self.setAttribute(Qt.WA_PaintOnScreen, True)
palette = self.palette()
palette.setColor(QPalette.Background, Qt.black)
self.setPalette(palette)
self.setSizePolicy(QSizePolicy.MinimumExpanding,
QSizePolicy.MinimumExpanding)
self.surface = VideoWidgetSurface(self)
def videoSurface(self):
return self.surface
def closeEvent(self, event):
del self.surface
def sizeHint(self):
return self.surface.surfaceFormat().sizeHint()
def paintEvent(self, event):
painter = QPainter(self)
if (self.surface.isActive()):
videoRect = self.surface.videoRect()
if not videoRect.contains(event.rect()):
region = event.region()
region.subtract(videoRect)
brush = self.palette().background()
for rect in region.rects():
painter.fillRect(rect, brush)
self.surface.paint(painter)
else:
painter.fillRect(event.rect(), self.palette().window())
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
self.surface.updateVideoRect()
class VideoPlayer(QWidget):
def __init__(self, parent=None):
super(VideoPlayer, self).__init__(parent)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.videoWidget = VideoWidget()
self.openButton = QPushButton("Open...")
self.openButton.clicked.connect(self.openFile)
self.playButton = QPushButton()
self.playButton.setEnabled(False)
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.play)
self.positionSlider = QSlider(Qt.Horizontal)
self.positionSlider.setRange(0, 0)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.controlLayout = QHBoxLayout()
self.controlLayout.setContentsMargins(0, 0, 0, 0)
self.controlLayout.addWidget(self.openButton)
self.controlLayout.addWidget(self.playButton)
self.controlLayout.addWidget(self.positionSlider)
layout = QVBoxLayout()
layout.addWidget(self.videoWidget)
layout.addLayout(self.controlLayout)
self.setLayout(layout)
self.mediaPlayer.setVideoOutput(self.videoWidget.videoSurface())
self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
def openFile(self):
file_name = QFileDialog.getOpenFileName(self, "Open Movie",
QDir.homePath())[0]
if os.path.exists(file_name):
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(file_name)))
self.playButton.setEnabled(True)
def play(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediaStateChanged(self, state):
if state == QMediaPlayer.PlayingState:
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playButton.setIcon(self.style().standardIcon(QStyle.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)
if __name__ == '__main__':
app = QApplication(sys.argv)
player = VideoPlayer()
player.show()
sys.exit(app.exec_())
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20131004/0989956a/attachment-0001.html>
More information about the PyQt
mailing list