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
Playing it with this code:
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?
As @musicamante points out in the comments, the solution is to access the QMediaStreamsControl but PyQt5 does not expose it.
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_())
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_())
But as pointed out by QMediaStreamsControl is not available in PyQt5, so a solution is to expose it and for this you must:
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
};
Add %Include qmediastreamscontrol.sip
to the end of file sip/QtMultimedia/QtMultimediamod.sip
Compile and install PyQt5 using the modified source code.
In conclusion:
If you use pyside2 the solution is simple.
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