QMediaPlayer. Как воспроизвести видео с несколькими звуками?

Имеется видеофайл с двумя звуковыми дорожками:

  Duration: 01:05:09.12, start: 0.000000, bitrate: 2781 kb/s
    Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 720x400 [SAR 1:1 DAR 9:5], 1998 kb/s, 25 fps, 25 tbr, 25 tbn, 25 tbc
    Stream #0:1: Audio: ac3 ([0] [0][0] / 0x2000), 48000 Hz, 5.1(side), fltp, 384 kb/s
    Stream #0:2: Audio: ac3 ([0] [0][0] / 0x2000), 48000 Hz, 5.1(side), fltp, 384 kb/s

Воспроизведение с этим кодом:

self.player = QMediaPlayer()
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(fileName)))
self.player.play()

Воспроизведение видео и первой звуковой дорожки. Как переключиться на вторую звуковую дорожку?


person voron.run    schedule 17.04.2020    source источник
comment
Теоретически кажется, что это должно быть возможно, если получить интерфейс QMediaStreamsControl с помощью player.service().requestControl('org.qt-project.qt.mediastreamscontrol/5.0'), но все мои тесты не увенчались успехом (он всегда возвращал базовый QMediaControl, который не предоставляет интерфейса для этого), так что может< /i> означает, что он недоступен в PyQt (пока). Я отправил электронное письмо в список рассылки PyQt (за которым также следует официальный сопровождающий PyQt), чтобы получить дополнительную информацию.   -  person musicamante    schedule 18.04.2020


Ответы (1)


Как указывает @musicamante в комментариях, решение состоит в том, чтобы получить доступ к QMediaStreamsControl, но PyQt5 не предоставляет его.

Вместо этого PySide2 раскрывает его, и решение состоит в том, чтобы использовать shiboken2:

import os

from PySide2 import QtCore, QtWidgets, QtMultimedia, QtMultimediaWidgets

import shiboken2


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        video_widget = QtMultimediaWidgets.QVideoWidget()
        self.player = QtMultimedia.QMediaPlayer(
            self, QtMultimedia.QMediaPlayer.VideoSurface
        )
        file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test5.mkv")
        self.player.setMedia(
            QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file))
        )
        self.player.setVideoOutput(video_widget)
        self.player.play()
        self.setCentralWidget(video_widget)

        control = self.player.service().requestControl(
            "org.qt-project.qt.mediastreamscontrol/5.0"
        )
        qptr = shiboken2.getCppPointer(control)[0]
        self.qcontrol = shiboken2.wrapInstance(qptr, QtMultimedia.QMediaStreamsControl)
        self.resize(640, 480)

    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu()
        group = QtWidgets.QActionGroup(menu)
        group.setExclusive(True)
        index = 0
        for i in range(self.qcontrol.streamCount()):
            t = self.qcontrol.streamType(i)
            if t == QtMultimedia.QMediaStreamsControl.AudioStream:
                action = menu.addAction("Audio-{}".format(index))
                action.setCheckable(True)
                if self.qcontrol.isActive(i):
                    action.setChecked(True)
                action.setData(i)
                menu.addAction(action)
                index += 1
        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action is not None:
            i = action.data()
            self.qcontrol.setActive(i, True)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

В случае pyqt5 вы должны использовать sip со следующим кодом:

import os

from PyQt5 import QtCore, QtWidgets, QtMultimedia, QtMultimediaWidgets

import sip


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        video_widget = QtMultimediaWidgets.QVideoWidget()
        self.player = QtMultimedia.QMediaPlayer(
            self, QtMultimedia.QMediaPlayer.VideoSurface
        )
        file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test5.mkv")
        self.player.setMedia(
            QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file))
        )
        self.player.setVideoOutput(video_widget)
        self.player.play()
        self.setCentralWidget(video_widget)

        control = self.player.service().requestControl(
            "org.qt-project.qt.mediastreamscontrol/5.0"
        )
        self.qcontrol = sip.cast(control, QtMultimedia.QMediaStreamsControl)
        self.resize(640, 480)

    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu()
        group = QtWidgets.QActionGroup(menu)
        group.setExclusive(True)
        index = 0
        for i in range(self.qcontrol.streamCount()):
            t = self.qcontrol.streamType(i)
            if t == QtMultimedia.QMediaStreamsControl.AudioStream:
                action = menu.addAction("Audio-{}".format(index))
                action.setCheckable(True)
                if self.qcontrol.isActive(i):
                    action.setChecked(True)
                action.setData(i)
                menu.addAction(action)
                index += 1
        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action is not None:
            i = action.data()
            self.qcontrol.setActive(i, True)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Но, как указывает QMediaStreamsControl, он недоступен в PyQt5, поэтому решение состоит в том, чтобы выставить его, и для этого вы должны:

  1. Загрузите исходный код PyQt5: https://pypi.python.org/packages/source/P/PyQt5/PyQt5-5.14.2.tar.gz
  2. Создайте файл qmediastreamscontrol.sip в папке «sip/QtMultimedia» исходного кода PyQt5.

    qmediastreamscontrol.sip

    class QMediaStreamsControl : QMediaControl
    {
    %TypeHeaderCode
    #include <qmediastreamscontrol.h>
    %End
    
    public:
        enum StreamType { 
            UnknownStream, 
            VideoStream, 
            AudioStream, 
            SubPictureStream, 
            DataStream 
        };
    
        virtual ~QMediaStreamsControl();
        virtual int streamCount() = 0;
        virtual QMediaStreamsControl::StreamType streamType(int streamNumber) = 0;
        virtual QVariant metaData(int streamNumber, const QString &key) = 0;
        virtual bool isActive(int streamNumber) = 0;
        virtual void setActive(int streamNumber, bool state) = 0;
    
    signals:
        void streamsChanged();
        void activeStreamsChanged();
    
    protected:
    %If (Qt_5_6_1 -)
        explicit QMediaStreamsControl(QObject *parent /TransferThis/ = 0);
    %End
    %If (- Qt_5_6_1)
        QMediaStreamsControl(QObject *parent /TransferThis/ = 0);
    %End
    };
    
  3. Добавьте %Include qmediastreamscontrol.sip в конец файла sip/QtMultimedia/QtMultimediamod.sip

  4. Скомпилируйте и установите PyQt5, используя измененный исходный код.

В заключение:

  • Если вы используете pyside2, решение простое.

  • Если вы используете pyqt5, вам придется изменить его исходный код, скомпилировать и установить. Надеемся, что класс отчета @musicamante QMediaStreamsControl будет представлен в будущих выпусках pyqt5.

person eyllanesc    schedule 18.04.2020
comment
Большое спасибо за подробный ответ и особенно за простое решение для pyside2. - person voron.run; 19.04.2020
comment
Сопровождающий PyQt только что написал в списке рассылки, что текущий последний снимок PyQt5 реализует все отсутствующие подклассы QMediaControl, поэтому, если вы можете выполнить обновление, вы сможете его использовать. - person musicamante; 22.04.2020