0

Here is my code:

QString BoatProgramming::setDuration(QString path)
{
    if (path.isNull()) { return ""; }
    QMediaPlayer mp;
    mp.setMedia(QUrl::fromLocalFile("/home/akiva/deleteme.ogg"));
    qDebug() << mp.duration(); // Outputting a value of -1

    m_Duration = QString::number(mp.duration());
    emit durationChanged();
    return m_Duration;
}

There is obviously an error somewhere, but beyond checking the filename, I am lamentably amiss as to what the problem is. Could it be simply that .ogg is not supported? Am I calling the function before the object is fully loaded into memory? Or is it something else?

Thanks.

Anon
  • 2,267
  • 3
  • 34
  • 51

1 Answers1

1

QMediaPlayer::setMedia() performs loading asynchronously, from the docs:

This function returns immediately after recording the specified source of the media. It does not wait for the media to finish loading and does not check for errors. Listen for the mediaStatusChanged() and error() signals to be notified when the media is loaded and when an error occurs during loading.

This means that querying QMediaPlayer for duration() after calling setMedia() immediately may not work, since QMediaPlayer might not have loaded the media yet.

In order to guarantee that loading has finished before calling duration(), you have to listen for mediaStatusChanged() signal, and get duration() only when mediaStatus() returns QMediaPlayer::LoadedMedia. Here is a minimal example:

#include <QtWidgets>
#include <QtMultimedia>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    QLabel label; //a label to display duration

    QString fileName = QFileDialog::getOpenFileName(nullptr, "Open Media File");

    QMediaPlayer mp;
    mp.setMedia(QUrl::fromLocalFile(fileName));
    QObject::connect(&mp, &QMediaPlayer::mediaStatusChanged,
                     [&](QMediaPlayer::MediaStatus status){
        if(status == QMediaPlayer::LoadedMedia) //when loading is finished
        {
            //show duration in a label
            qint64 duration= mp.duration();
            label.setText(QString("Duration: %1 ms.\n\nThat is: %2")
                          .arg(duration)
                          .arg(QDateTime::fromTime_t(duration/1000).toUTC()
                               .toString("hh:mm:ss")));
            label.show();
        }
    });

    return app.exec();
}
Mike
  • 8,055
  • 1
  • 30
  • 44
  • The issue for me I think is different. After trying the code in main, things work swimmingly, so your code works. Trying it however in my own class, I can wait for a year and that signal will never trigger. Changing it to this however: `QObject::connect(&m_MediaPlayer, &QMediaPlayer::durationChanged, [&](qint64 fun)` worked. – Anon Dec 01 '16 at 04:44
  • By the way, I tried googling it, but for the love of me I can't find anything on it. What does the syntax `[&]` do? I have never seen anything like it. – Anon Dec 01 '16 at 04:46
  • 1
    @Akiva , This is a C++11 lambda expression, see [this question](http://stackoverflow.com/q/7627098/2666212). the `[&]` syntax captures the variables being used by the lambda by reference. Do **NOT** do that for local variables in your class's methods, you will end up using dangling references in your lambda (this maybe the reason for things working in main but not in your class). If you are not used to lambda expressions, use a normal slot instead to avoid such problems (i.e., just replace this lambda function with a normal slot that is defined in your class). – Mike Dec 01 '16 at 08:45