问题描述
有一个包含两个音轨的视频文件:
There is a video file with two audio tracks:
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()
播放视频和第一个音轨.Н如何切换到第二个音轨?
Video and first audio track is played. Нow to switch to the second audio track?
推荐答案
正如@musicamante 在评论中指出的那样,解决方案是访问 QMediaStreamsControl 但 PyQt5 没有公开它.
As @musicamante points out in the comments, the solution is to access the QMediaStreamsControl but PyQt5 does not expose it.
相反,PySide2 确实公开了它,解决方案是使用 shiboken2 进行投射:
Instead PySide2 does expose it and the solution is to cast using 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 和以下代码:
In the case of pyqt5 you should use sip with the following code:
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 中不可用,因此解决方案是公开它,为此您必须:
But as pointed out by QMediaStreamsControl is not available in PyQt5, so a solution is to expose it and for this you must:
- 下载 PyQt5 源码:https://pypi.python.org/packages/source/P/PyQt5/PyQt5-5.14.2.tar.gz
在 PyQt5 源代码的sip/QtMultimedia"文件夹中创建文件 qmediastreamscontrol.sip.
- Download PyQt5 source code: https://pypi.python.org/packages/source/P/PyQt5/PyQt5-5.14.2.tar.gz
Create the file qmediastreamscontrol.sip in "sip/QtMultimedia" folder of the PyQt5 source code.
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 };
将 %Include qmediastreamscontrol.sip 添加到文件 sip/QtMultimedia/QtMultimediamod.sip 的末尾
Add %Include qmediastreamscontrol.sip to the end of file sip/QtMultimedia/QtMultimediamod.sip
使用修改后的源代码编译安装PyQt5.
Compile and install PyQt5 using the modified source code.
总结:
如果你使用 pyside2 解决方案很简单.
If you use pyside2 the solution is simple.
如果你使用 pyqt5,你将不得不修改它的源代码,编译并安装它.希望 @musicamante 报告类 QMediaStreamsControl 在 pyqt5 的未来版本中公开
If you use pyqt5 you will have to modify its source code, compile it and install it. Hopefully with the @musicamante report class QMediaStreamsControl is exposed in future releases of pyqt5