0

How can I set the audio output of a QMediaPlayer to a specific output in Windows 7 and later?
This was really easy in PySide (using Phonon) but I can't find a way to do it in PySide2.

There are some related questions already, like this old but still not solved one, or this one that asks exactly what I want.
They are both in c++ and its difficult to convert it to PySide2.
The second one is answered with this code:

QMediaService *svc = player->service();
if (svc != nullptr)
{
    QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
                                       (svc->requestControl(QAudioOutputSelectorControl_iid));
    if (out != nullptr)
    {
        out->setActiveOutput(this->ui->comboBox->currentText());
        svc->releaseControl(out);
    }
}

Another one with an attempt to python conversion didn't work also.

I tried to convert them to Python code, but the result was not successful.
Here is my minimal attempt:

import sys
from PySide2 import QtMultimedia
from PySide2.QtCore import QUrl, Qt
from PySide2.QtMultimedia import QMediaPlayer, QMediaContent
from PySide2.QtWidgets import (QPushButton, QSlider, QHBoxLayout, QVBoxLayout,
                               QFileDialog, QStyle, QApplication, QDialog, QComboBox)


class Window(QDialog):
    def __init__(self):
        super().__init__()

        self.out_combo = QComboBox()
        mode = QtMultimedia.QAudio.AudioOutput
        devices = QtMultimedia.QAudioDeviceInfo.availableDevices(mode)
        for item in [(dev.deviceName(), dev) for dev in devices]:
            self.out_combo.addItem(item[0], item[1])
        self.out_combo.currentIndexChanged.connect(self.out_device_changed)

        openBtn = QPushButton('Open file')
        openBtn.clicked.connect(self.open_file)

        self.playBtn = QPushButton()
        self.playBtn.setEnabled(False)
        self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playBtn.clicked.connect(self.play_file)

        self.slider = QSlider(Qt.Horizontal)
        self.slider.setRange(0, 0)
        self.slider.sliderMoved.connect(self.set_position)

        hor_layout = QHBoxLayout()
        hor_layout.setContentsMargins(0, 0, 0, 0)
        hor_layout.addWidget(openBtn)
        hor_layout.addWidget(self.playBtn)
        hor_layout.addWidget(self.slider)

        ver_layout = QVBoxLayout()
        ver_layout.addWidget(self.out_combo)
        ver_layout.addLayout(hor_layout)

        self.setLayout(ver_layout)

        self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)

        self.player.stateChanged.connect(self.mediastate_changed)
        self.player.positionChanged.connect(self.position_changed)
        self.player.durationChanged.connect(self.duration_changed)

        self.show()

    def open_file(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Open file")
        if file_name != '':
            self.player.setMedia(QMediaContent(QUrl.fromLocalFile(file_name)))
            # self.label.setText(basename(file_name))
            self.playBtn.setEnabled(True)

    def play_file(self):
        if self.player.state() == QMediaPlayer.PlayingState:
            self.player.pause()
        else:
            self.player.play()

    def mediastate_changed(self, state):
        if self.player.state() == QMediaPlayer.PlayingState:
            self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
        else:
            self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

    def position_changed(self, position):
        self.slider.setValue(position)

    def duration_changed(self, duration):
        self.slider.setRange(0, duration)

    def set_position(self, position):
        self.player.setPosition(position)

    def out_device_changed(self, idx):
        device = self.out_combo.itemData(idx)

        service = self.player.service()
        if service:
            out = service.requestControl("org.qt-project.qt.mediastreamscontrol/5.0")
            if out:
                out.setActiveOutput(self.out_combo.currentText())
                service.releaseControl(out)
            else:
                print("No output found!")


app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
noEmbryo
  • 2,176
  • 2
  • 10
  • 15
  • As far as I know, the control interface has limited support in general and even further limited support on PyQt (depending on the version), and that is also suggested by the fact that all interfaces have an obsolete warning in the docs: QtMultimedia had serious problems in Qt4, the situation slightly improved on Qt5 but the API has always soffered problems and compatibility issues (especially across different platforms). It seems that the situation has been improved since the module has been completely rewritten and reintroduced in Qt6.2, but I don't know if it already supports controls too. – musicamante Oct 23 '21 at 22:30
  • The only alternative I see, if you don't want to switch to Qt6 yet, is to use alternative solutions, possibly by integrating the vlc python module. – musicamante Oct 23 '21 at 22:31
  • Multimedia in Qt4 was working fine for me, but while Qt5 removed Phonon, it introduced an inferior (for my needs) player in its place. VLC is an extra dependency, that I couldn't make it work with PySide2, even with their own examples.. Not even once! – noEmbryo Oct 24 '21 at 19:20
  • It probably worked fine for you, but it had serious compatibility issues (including Phonon) and there were also problems with module/classes being moved here and there across versions. Qt5 support was slightly better in general due to a better cross-platform support, but it still was/is problematic mostly for lack of developers: they simply didn't have enough manpower for it, so they had to lose/ignore some platform/feature specific support in order to keep cross-platform compatibility. As far as I can remember, the PyQt maintainer added support for `QMediaControl` classes, but since it's -> – musicamante Oct 24 '21 at 19:36
  • -> a very rarely used class type and manual corrections are required to the code, it's possible that he forgot something (and this is still assuming that the audio selector control is *actually* supported by the platform). I suggest you to write to the [PyQt ML](https://www.riverbankcomputing.com/mailman/listinfo/pyqt) and provide a [mre], maybe you'll get an answer about that. Obviously, if it still doesn't work, you have no other alternative: you either rely on VLC or you try to switch to PyQt6. Note that AFAIK the vlc module should work with PySide, so eventually post another question. – musicamante Oct 24 '21 at 19:42

0 Answers0