QMediaPlayer duration() returns 0 always - c++

I use Qt 5.7
I'm writing Music Player, and have one problem. Method duration() of QMediaPlayer always returns 0. How can I fix it?
Example of code:
QMediaPlayer *player = new QMediaPlayer;
player->setMedia(QMediaContent(QUrl(path)));
qDebug() << player->duration(); // returns 0
player->play(); // it works

You cannot do a player->duration() right after the player->setMedia(QMediaContent(QUrl(path)));.
Indeed, the QMediaPlayer::setMedia is asynchronous so if you call the duration right after it, the media won't be set yet and then the duration will be wrong.
From Qt documentation on setMedia:
Note: 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.
When the duration is updated, QMediaPlayer send a signal named durationChanged(qint64 duration). So that you need to do is to connect this signal with a lambda or a slot.
For example,
QMediaPlayer *player = new QMediaPlayer(this);
connect(player, &QMediaPlayer::durationChanged, this, [&](qint64 dur) {
qDebug() << "duration = " << dur;
});
QUrl file = QUrl::fromLocalFile(QFileDialog::getOpenFileName(this, tr("Open Music"), "", tr("")));
if (file.url() == "")
return ;
player->setMedia(file);
qDebug() << player->duration();
player->setVolume(50);
player->play();
the first qDebug will write 0 as your but the second in the lambda will write the new duration of the QMediaPlayer.

Related

Capture a frame (image) from a video playing in a QT GUI

I have written a simple video player GUI code in QT. The GUI allows the user to browse the local files and select a video for playing in the GUI. The GUI also has options for 'play', 'pause' and 'stop' to apply to the video selected.
I want to add another button 'Capture', that captures the current frame of the video that is being played, and displays this captured image next to the video (The video should should get paused at this point).
I looked into the documentation of QT, specifically: this and this. But I am still not able to understand how to implement this in my case.
Kindly guide.
My code so far is as follows:
#include "qtwidgetsapplication4.h"
#include <iostream>
QtWidgetsApplication4::QtWidgetsApplication4(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
player = new QMediaPlayer(this);
vw = new QVideoWidget(this);
player->setVideoOutput(vw);
this->setCentralWidget(vw);
}
void QtWidgetsApplication4::on_actionOpen_triggered() {
QString filename = QFileDialog::getOpenFileName(this, "Open a File", "", "Video File (*.*)");
on_actionStop_triggered();
player->setSource(QUrl::fromLocalFile(filename));
on_actionPlay_triggered();
qDebug("Error Message in actionOpen");
qDebug()<<player->mediaStatus();
}
void QtWidgetsApplication4::on_actionPlay_triggered() {
player->play();
ui.statusBar->showMessage("Playing");
qDebug("Error Message in actionPlay");
qDebug() << player->mediaStatus();
}
void QtWidgetsApplication4::on_actionPause_triggered() {
player->pause();
ui.statusBar->showMessage("Paused...");
qDebug("Error Message in actionPause");
qDebug() << player->mediaStatus();
}
void QtWidgetsApplication4::on_actionStop_triggered() {
player->stop();
ui.statusBar->showMessage("Stopped");
qDebug("Error Message in actionStop");
qDebug() << player->mediaStatus();
}
You can use QVideoProbe to capture QVideoFrame. Instantiate and connect videoFrameProbed signal to your slot before pausing video. Convert QVideoFrame to QImage in this slot using frame data.
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat());
QImage image(frame.bits(), frame.width(), frame.height(), imageFormat);
Take a look at player example for reference. It uses QVideoProbe to calculate histogram of current frame.

Connect signal of superclass of object

I am working with a QProcess and have connected QProcess's signal readyReadStandardOutput().
That process normally spits out data to the console regularly, but the readyReadStandardOutput() seems to batch results and only emit every half a minute or so (with all accumulated data).
I want to access the "live feed" of the QProcess so I thought maybe QProcess's superclass QIODevice has some other signals.
Other solutions instead of using bytesWritten are also welcome
Now I'm trying to connect bytesWritten, but it doesn't let me.
Code:
void MainWindow::on_Program_clicked() {
program= new QProcess(this);
QString file = "../folder/program/program.exe";
QString directory = "../folder/program/";
//qint64 pid;
program->setWorkingDirectory(directory);
program->start(file, {""});
program->waitForStarted();
connect(program, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
//ERROR: "QObject::connect: No such signal QProcess::bytesWritten() in ..\---\mainwindow.cpp:45
connect(program, SIGNAL(bytesWritten()), this, SLOT(myBytesWritten()));
}
void MainWindow::myBytesWritten() {
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
//Works, but only emits a signal every so often, and not every time a new line is written to the console as when I launch the exe normally
void MainWindow::readOutput(){
QProcess *program = dynamic_cast<QProcess *>(sender());
QByteArray outData = program->readAll();
qDebug() << "DEBUG: " + outData;
}
If you want to use the old, deprecated way of connecting signals to slots, you need to also include the parameter list:
connect(program, SIGNAL(bytesWritten(int)), this, SLOT(myBytesWritten()));
Or you can just use the modern, compile-time Qt5 way:
connect(program, &QProcess::bytesWritten, this, &MainWindow::myBytesWritten);
Also note that bytesWritten fires when a write command from YOUR end has succeeded. You don't seem to be sending any input so this will never fire.

QAudioInput::notify() isn't executed while capturing sound from mic

I want to use QAudioInput to capture sound from mic, process it and then play. As I understood, I need to connect to notify signal and inside handler for it user readAll() function to get raw data. But the problem is, this handler function is never executed. Here is my code:
void MainWindow::on_pushButton_clicked()
{
QList<QAudioDeviceInfo> list = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
if(list.isEmpty())
{
qDebug() << "No audio input device";
return;
}
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
QAudioFormat format;
// Set up the desired format, for example:
format.setSampleRate(44100);
format.setChannelCount(1);
format.setSampleSize(32);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
if (!info.isFormatSupported(format)) {
qWarning() << "Default format not supported, trying to use the nearest.";
format = info.nearestFormat(format);
}
qDebug() << "input device :" << info.deviceName();
audio = new QAudioInput(info, format);
qDebug("created()");
connect(audio, SIGNAL(notify()), this, SLOT(onNotify()));
audio->setNotifyInterval(10);
bufferInput = audio->start();
}
void MainWindow::onNotify()
{
qDebug() << "onNotify()";
//QByteArray array = bufferInput->readAll();
//bufferOutput->write(array);
}
(audio is of type QAudioInput* and bufferInput is of type QIODevice*)
and when I click the button, "input device = " and "created()" messages are displayed, but "onNotify()" isn't shown.
What am I doing wrong?
QAudioInput seems quite broken. Or I'm utterly misunderstanding something.
The only thing that worked reliably for me was to use the readyRead() signal of the QIODevice buffer returned by start(). Unfortunately, that isn't fired very often on my system (around every 40 to 60ms on my system).
What I've found is that notify() starts firing when I either call resume() on the QAudioInput (because it's in idle state after calling start()) or do a readAll() on the QIODevice (!). But at least with PyQt this leads to a stack overflow after a minute or so.
I would suspect the platform matters as well, as the actual QAudioInput implementation depends on the platform and audio system used (PulseAudio on Fedora 32 in my case).
Connecting a slot to readyRead signal of QAudioInput and reading from QIODevice with readAll is the correct solution. Why do you think 40 or 60 ms interval is inappropriate? QAudioInput object must capture some audio during this interval and send signal after it.

QProcess does not complete creating file

I am just trying to create a file with QProcess by the following source code:
void Processmethod()
{
QDialog *ProcessMessage = new QDialog;
Ui::DialogProcessMessage Dialog;
Dialog.setupUi(ProcessMessage);
ProcessMessage->setModal(true);
ProcessMessage->setAttribute(Qt::WA_DeleteOnClose);
ProcessMessage->show();
processmethodONE();
}
void processmethodONE()
{
QString ProcessCommand = "w8 " + blablubli";
Prozess.setWorkingDirectory(Path); //QProcess "Prozess" is globaly defined
Prozess.setStandardOutputFile(Path); //in my class
Prozess.start(ProcessCommand);
QProcess::ExitStatus Status = Prozess.exitStatus();
if (Status == 0)
{
std::cout << "File created!" << std::endl;
}
}
This process creates out of another file which is located in the QString "Path" a new file, let me call it "PROFILE" (PRO for Problem :). The Profile also is created, but is never completed, I guess not even 50% of the file are completed.
Just when using
while(!Prozess.waitForFinished())
{
qApp->processEvents();
std::cerr << "Process Create PROFile running " << std::endl;
}
the file is written completely.
Furthermore I tried to use the QProcess finished(int) Signal to start another method and deleted the while loop (which freezes the whole GUI). I declared it in the constructor with:
connect(&Prozess, SIGNAL(finished(int)), this, (SLOT(processmethodTWO())));
But I guess this could not work because the first process isn't finished completely. Where is the problem?
There is no warranty that right after Prozess.start(ProcessCommand); process will be finished, so calling "Prozess.exitStatus();" right after it will give you "false positive". You certainly will have to wait until process is finished. You may either do it with with while loop, that you suggested or with QEventLoop
// ...
Prozess.setStandardOutputFile(Path); //in my class
QEventLoop loop;
connect(&Prozess, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit()));
connect(&Prozess, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
Prozess.start();
loop.exec();
// Now your process status should be valid:
QProcess::ExitStatus Status = Prozess.exitStatus();

How to use QtMultimedia to play a wav file?

My current code is:
void Sound::run() {
QFile audio_file(mResourcePath);
if(audio_file.open(QIODevice::ReadOnly)) {
audio_file.seek(44); // skip wav header
QByteArray audio_data = audio_file.readAll();
audio_file.close();
QBuffer* audio_buffer = new QBuffer(&audio_data);
qDebug() << audio_buffer->size();
QAudioFormat format;
format.setSampleSize(16);
format.setSampleRate(44100);
format.setChannelCount(2);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format)) {
qWarning()<<"raw audio format not supported by backend, cannot play audio.";
return;
}
qDebug() << info.deviceName();
QAudioOutput* output = new QAudioOutput(info, format);
output->start(audio_buffer);
}
}
This whole thing is started as a QRunnable in a QThreadPool and that part works fine. Problem is I never get any audio. My sound device is operational, the buffer is filled. I don't know what's wrong. I use app.exec(). Help appreciated.
The device (QBuffer) has to be open:
QBuffer audio_buffer(&audio_data);
audio_buffer.open(QIODevice::ReadOnly);
QAudioOutput needs an event loop to play anything, and that loop has to be running in the thread it belongs to. Which is the thread it was created in when you don't explicitly move it to another thread:
// Create the device and start playing...
QAudioOutput output(info, format);
output.start(&audio_buffer);
// ...then wait for the sound to finish
QEventLoop loop;
QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
do {
loop.exec();
} while(output.state() == QAudio::ActiveState);
Everything you allocate should be deallocated when the sound has finished playing, or you would have a memory leak, and the event loop will now run inside the function, so you can allocate everything locally.