QMediaPlayer fails to play mp3 file and does not signals mediaStatusChanged(QMediaPlayer::EndOfMedia)

223 Views Asked by At

I am writing a program (using Qt 6.5.1, on M1 Mac) which need to play multiple mp3 files (one by one). I run into a bug with the following code:

void MainWindow::PlayMp3FileWithMediaPlayer(const QString &audio_file)
{
    Q_ASSERT(audio_output_ != nullptr);
    QMediaPlayer *player = new QMediaPlayer();
    player->setAudioOutput(audio_output_);
    connect(player, &QMediaPlayer::sourceChanged, this, [this, player]() {
        player->play();
        qDebug() << "Playing:" << player->source();
        //QTimer::singleShot(player->duration(), this, [this]() { FinishWait(); });
    });
    connect(player, &QMediaPlayer::errorChanged, this, [this, player]() {
        qDebug() << player->error() << player->errorString();
        if (player->error() != QMediaPlayer::NoError)
        {
            FinishWait();
        }
    });
    connect(player, &QMediaPlayer::mediaStatusChanged, this, [this, player](QMediaPlayer::MediaStatus status) {
        qDebug() << status;
        if (status == QMediaPlayer::EndOfMedia)
        {
            FinishWait();
        }
    });
    QTimer::singleShot(0, this, [this, player, audio_file]() {
        player->setSource(QUrl::fromLocalFile(audio_file));
    });
    qDebug() << "Going to play:" << audio_file;
    StartWait();
//    const int delay = 10000;
//    QTimer::singleShot(delay, player, &QObject::deleteLater);
//    QTimer::singleShot(delay, output, &QObject::deleteLater);
    player->setAudioOutput(nullptr);
    player->deleteLater();
}

When this func is called (multiple times), it quite often runs into problem that the player hangs and does not signal mediaStatusChanged(QMediaPlayer::EndOfMedia). The player is still in playing state, but it's not progressing. And no sound was from the computer (after a glitch). Then if I open a different program which plays a sound, I can hear that my program is repeating the sound (where it hangs, maybe the last 100 milliseconds).

And here's some output from my program, we can see that after QMediaPlayer::BufferedMedia, there's no QMediaPlayer::EndOfMedia (expected, as the player is hanging).

Going to play: "/Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/7.mp3"
QMediaPlayer::LoadingMedia
QMediaPlayer::BufferedMedia
Playing: QUrl("file:///Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/7.mp3")
[mp3float @ 0x1370d45e0] Could not update timestamps for skipped samples.
[mp3float @ 0x1370d45e0] Could not update timestamps for discarded samples.
QMediaPlayer::EndOfMedia
Going to play: "/Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/Nbd4.mp3"
QMediaPlayer::LoadingMedia
QMediaPlayer::BufferedMedia
Playing: QUrl("file:///Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/Nbd4.mp3")
[mp3float @ 0x137158430] Could not update timestamps for skipped samples.
[mp3float @ 0x137158430] Could not update timestamps for discarded samples.
QMediaPlayer::EndOfMedia
Going to play: "/Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/8.mp3"
QMediaPlayer::LoadingMedia
QMediaPlayer::BufferedMedia
Playing: QUrl("file:///Users/huafeng/Documents/GitHub/chesstools/GameEditor/move_voices/8.mp3")
[mp3float @ 0x1370d57f0] Could not update timestamps for skipped samples.

I have add code to check the player state and it's playing, but position() is not changing. I have tried avoiding new/delete QMediaPlayer, QAudioOutput, but no luck. Alternatively, I use the following code to play the mp3 file with python and everything works as expected.


void MainWindow::PlayAudioFile(const QString &unique_move_id, int sentence, const QString &audio_file)
{
    PlayMp3FileWithMediaPlayer(audio_file);
    //PlayMp3FileWithPython(audio_file);
}

void MainWindow::PlayMp3FileWithPython(const QString &audio_file)
{
    QStringList arguments;
    arguments << QString(_PROJECT_SRC_DIR_) + "/play_sound_file.py" << audio_file;
    QProcess *python_process = new QProcess(this);
    python_process->setProgram(python_program_);
    python_process->setArguments(arguments);

    connect(python_process, &QProcess::finished, this, &MainWindow::FinishWait);
    QTimer::singleShot(0, this, [this, python_process]() { python_process->start(); });
    StartWait();
    python_process->deleteLater();
}

Any idea?

1

There are 1 best solutions below

1
Bondike On

Instead of

connect(player, &QMediaPlayer::mediaStatusChanged, this, [this, player](QMediaPlayer::MediaStatus status) {
    qDebug() << status;
    if (status == QMediaPlayer::EndOfMedia)
    {
        FinishWait();
    }
});

I used

connect(player, &QMediaPlayer::mediaStatusChanged, player, [this](QMediaPlayer::MediaStatus status) {
    qDebug() << status;
    if (status == QMediaPlayer::EndOfMedia)
    {
        FinishWait();
    }
});

And it worked for me. Output looks like

QMediaPlayer::LoadingMedia
QMediaPlayer::BufferedMedia
QMediaPlayer::LoadedMedia

After media playback is finished, the console displays the following

QMediaPlayer::EndOfMedia

Qt 6.5, Windows 11