15.1.219. tablet_qt/lib/soundfunc.cpp

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

#include "soundfunc.h"
#include <QAudioDevice>
#include <QAudioOutput>
#include <QDebug>
#include <QMediaDevices>
#include <QObject>
#include <QMediaPlayer>
#include "maths/mathfunc.h"

namespace soundfunc {

const QString UNABLE_TO_CREATE_MEDIA_PLAYER("Unable");


void makeMediaPlayer(QSharedPointer<QMediaPlayer>& player)
{
    // qDebug() << "Default QMediaPlayer() flags: " << static_cast<int>(QMediaPlayer::Flags());
    // default "flags" argument to QMediaPlayer() is 0, i.e. no flags set.

    qDebug() << "About to call QMediaPlayer()...";
    player = QSharedPointer<QMediaPlayer>(new QMediaPlayer(),
                                          &QObject::deleteLater);
    auto audio_output = new QAudioOutput();
    audio_output->setDevice(QMediaDevices::defaultAudioOutput());
    player->setAudioOutput(audio_output);
    // https://doc.qt.io/qt-6.5/qsharedpointer.html
    // Failing to use deleteLater() can cause crashes, as there may be
    // outstanding events relating to this object.
    // ... but it's not enough; see finishMediaPlayer().
    qDebug() << "... QMediaPlayer() has returned.";

    /*

    Near-crash; very long pause 2019-09-06. Looks like it relates to GStreamer:

        1   ??                                                                                                                                                                                                                                                   0x7ffff7bcefd7
        2   ??                                                                                                                                                                                                                                                   0x7ffff7bcfba8
        3   ??                                                                                                                                                                                                                                                   0x7ffff7bcfbda
        4   g_type_add_interface_static                                                                                                                                                                                                                          0x7ffff7bd10d6
        5   gst_bin_get_type                                                                                                                                                                                                                                     0x7ffff6d009b0
        6   ??                                                                                                                                                                                                                                                   0x7ffff6cf74fc
        7   g_option_context_parse                                                                                                                                                                                                                               0x7ffff13a58a8
        8   gst_init_check                                                                                                                                                                                                                                       0x7ffff6cf84cf
        9   gst_init                                                                                                                                                                                                                                             0x7ffff6cf8524
        10  QGstreamerPlayerServicePlugin::create(QString const&)                                                                                                                                                                                                0x555555f98cd2
        11  QPluginServiceProvider::requestService(QByteArray const&, QMediaServiceProviderHint const&)                                                                                                                                                          0x555556581e9e
        12  QMediaPlayer::QMediaPlayer(QObject *, QFlags<QMediaPlayer::Flag>)                                                                                                                                                                                    0x5555565aed69
        13  soundfunc::makeMediaPlayer                                                                                                                                                                                      soundfunc.cpp                    31  0x5555558a5214
        14  CardinalExpectationDetection::startTask                                                                                                                                                                         cardinalexpectationdetection.cpp 770 0x555555adbc7b
        ... <More>

    or

        1  sched_yield                                                                                                                                                                                                     syscall-template.S               78  0x7ffff0613e57
        2  ??                                                                                                                                                                                                                                                   0x7fffc78841b9
        3  ??                                                                                                                                                                                                                                                   0x7fffc78708e0
        4  ??                                                                                                                                                                                                                                                   0x7fffc75db94f
        5  ??                                                                                                                                                                                                                                                   0x7fffc75d0f00
        6  vaTerminate                                                                                                                                                                                                                                          0x7fffccb2f542
        7  ??                                                                                                                                                                                                                                                   0x7fffcd7ec3ad
        8  g_object_unref                                                                                                                                                                                                                                       0x7ffff6a8a012
        9  gst_object_replace                                                                                                                                                                                                                                   0x7ffff6cf9a10
        10 ??                                                                                                                                                                                                                                                   0x7fffcd7c17a3
        11 ??                                                                                                                                                                                                                                                   0x7fffcd7cc16b
        12 ??                                                                                                                                                                                                                                                   0x7ffff7269f11
        13 gst_element_change_state                                                                                                                                                                                                                             0x7ffff6d26d5e
        14 ??                                                                                                                                                                                                                                                   0x7ffff6d27499
        15 QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *, QByteArray const&)                                                                                                                                                                         0x555555faef15
        16 QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *, QByteArray const&)                                                                                                                                                             0x555555fb9c55
        17 QGstreamerPlayerService::QGstreamerPlayerService(QObject *)                                                                                                                                                                                          0x555555f991c5
        18 QGstreamerPlayerServicePlugin::create(QString const&)                                                                                                                                                                                                0x555555f98d01
        19 QPluginServiceProvider::requestService(QByteArray const&, QMediaServiceProviderHint const&)                                                                                                                                                          0x55555658206e
        20 QMediaPlayer::QMediaPlayer(QObject *, QFlags<QMediaPlayer::Flag>)                                                                                                                                                                                    0x5555565aef39
        21 soundfunc::makeMediaPlayer                                                                                                                                                                                      soundfunc.cpp                    31  0x5555558a5199
        22 CardinalExpectationDetection::startTask                                                                                                                                                                         cardinalexpectationdetection.cpp 760 0x555555adb996
        ... <More>

    This is potentially related to
    https://github.com/OpenBoard-org/OpenBoard/issues/4. On shrike:

        $ gst-inspect-1.0 --version

        gst-inspect-1.0 version 1.14.5
        GStreamer 1.14.5
        https://launchpad.net/distros/ubuntu/+source/gstreamer1.0

    */
}


void finishMediaPlayer(const QSharedPointer<QMediaPlayer>& player)
{
    // The following seems to prevent a crash (even with deleteLater set up by
    // the QSharedPointer<QMediaPlayer>(new ...) call) whereby ongoing events
    // try to go to a non-existing QMediaPlayer.
    // Looks like this is not an uncommon problem:
    //  https://www.google.com/?ion=1&espv=2#q=delete%20qmediaplayer%20crash
    // The crash comes from QMetaObject, from the Qt event loop.

    if (player) {
        player->stop();
    }
}


void setVolume(const QSharedPointer<QMediaPlayer>& player,
               const int volume_percent)
{
    const qreal volume_proportion = mathfunc::intPercentToProportion(volume_percent);
    setVolume(player, volume_proportion);
}


void setVolume(const QSharedPointer<QMediaPlayer>& player,
               const double volume_proportion)
{
    QAudioOutput* output = player->audioOutput();
    if (output) {
        output->setVolume(volume_proportion);  // argument range: 0 to 1
    }
}


}  // namespace soundfunc