15.1.221. 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 <QMediaPlayer>
#include <QObject>

#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   ??
        2   ??
        3   ??
        4   g_type_add_interface_static
        5   gst_bin_get_type
        6   ??
        7   g_option_context_parse
        8   gst_init_check
        9   gst_init
        10  QGstreamerPlayerServicePlugin::create(QString const&)
        11  QPluginServiceProvider::requestService(QByteArray const&,
                QMediaServiceProviderHint const&)
        12  QMediaPlayer::QMediaPlayer(QObject *, QFlags<QMediaPlayer::Flag>)
        13  soundfunc::makeMediaPlayer
        14  CardinalExpectationDetection::startTask
        ... <More>

    or

        1  sched_yield
        2  ??
        3  ??
        4  ??
        5  ??
        6  vaTerminate
        7  ??
        8  g_object_unref
        9  gst_object_replace
        10 ??
        11 ??
        12 ??
        13 gst_element_change_state
        14 ??
        15 QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *,
                QByteArray const&)
        16 QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(
                QObject *, QByteArray const&)
        17 QGstreamerPlayerService::QGstreamerPlayerService(QObject *)
        18 QGstreamerPlayerServicePlugin::create(QString const&)
        19 QPluginServiceProvider::requestService(QByteArray const&,
                QMediaServiceProviderHint const&)
        20 QMediaPlayer::QMediaPlayer(QObject *, QFlags<QMediaPlayer::Flag>)
        21 soundfunc::makeMediaPlayer
        22 CardinalExpectationDetection::startTask
        ... <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