PyQt5 — Unable to playback video from stream

2019-08-01 06:11发布

问题:

I've seen a number of other questions like this floating around but it seems many of them are unresolved or unrelated to my situation, so here goes.

I'm attempting to play back a video stored as serialized data (through pickle) on a mongodb collection.

Here's the code:

    binary_file = my_database_entry['binary video']
    unpickle = pickle.dumps(binary_file)

    outByteArray = QByteArray(unpickle)
    mediaStream = QBuffer()
    mediaStream.setBuffer(outByteArray)
    mediaStream.open(QIODevice.ReadWrite)

    mediaPlayer.setMedia(QMediaContent(), mediaStream)
    mediaPlayer.play()

where 'my_database_entry' is the mongoDB entry and 'binary video' is the dictionary key for the pickled video entry. This also assumes that mediaPlayer is properly created and initialized within my user interface i.e.

    mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
    videoPlayer = QVideoWidget()
    mediaPlayer.setVideoOutput(videoPlayer)

I also tried initializing mediaPlayer with a 'QMediaPlayer.StreamPlayback' flag but again, nothing.

It crashes when I try it on windows and it's just a black screen when I try it on mac. No error logs or anything (nothing enlightening at any rate).

Has anyone gotten this to successfully work for them and if so, how did you do it?

Thanks! -Mark

回答1:

You need to keep a reference to both the buffer and the underlying data, otherwise they will just be garbage-collected after the player starts.

And note that in your example, it is utterly pointless pickling the video data, as it is just bytes and so there's nothing worth serializing. Pickle is only useful for structured python objects, such as a list or dict.

Below is a demo script with a complete video player. It initally gets the video resource from the file-system, but it would work just the same if it came from a database:

from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtMultimedia, QtMultimediaWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.player = QtMultimedia.QMediaPlayer(self)
        self.viewer = QtMultimediaWidgets.QVideoWidget(self)
        self.player.setVideoOutput(self.viewer)
        self.player.stateChanged.connect(self.handleStateChanged)
        self.button1 = QtWidgets.QPushButton('Play', self)
        self.button2 = QtWidgets.QPushButton('Stop', self)
        self.button1.clicked.connect(self.handleButton)
        self.button2.clicked.connect(self.player.stop)
        self.button2.setEnabled(False)
        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(self.viewer, 0, 0, 1, 2)
        layout.addWidget(self.button1, 1, 0)
        layout.addWidget(self.button2, 1, 1)
        self._buffer = QtCore.QBuffer(self)
        self._data = None

    def handleButton(self):
        path = QtWidgets.QFileDialog.getOpenFileName(self)[0]
        if path:
            self.button1.setEnabled(False)
            self.button2.setEnabled(True)
            with open(path, 'rb') as stream:
                self._data = stream.read()
                self._buffer.setData(self._data)
                self._buffer.open(QtCore.QIODevice.ReadOnly)
                self.player.setMedia(
                    QtMultimedia.QMediaContent(), self._buffer)
                self.player.play()

    def handleStateChanged(self, state):
        if state == QtMultimedia.QMediaPlayer.StoppedState:
            self._buffer.close()
            self._data = None
            self.button1.setEnabled(True)
            self.button2.setEnabled(False)

if __name__ == '__main__':

    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 50, 640, 480)
    window.show()
    sys.exit(app.exec_())

UPDATE:

The solution above will only work on Windows and Linux, because there is currently no support for streaming on OSX:

  • See: Qt Multimedia Backends