Handle threads between classes with C++, libvlc and ubuntu - c++

I have an application in C++ with a GUI interface that needs to reproduce some mp3 depending on user's interactions. I need to reproduce the mp3 without blocking the program's flow.
In order to save code, I decided to write a class to handle the mp3 reproducing and to reproduce it in a new thread, but I'm having problems when I need to stop the playing.
I know that libvlc have already some locking function, but the flow of the program stops when the mp3 is playing.
The mp3 starts correctly, but if I try to call the stop_mp3() function, I get a core dumped error.
The error is generated when I call the stop function from the secondpanel.cpp.
// replay.h
#include <vlc/vlc.h>
class rePlay
{
public:
rePlay();
virtual ~rePlay();
void play_mp3(const char*);
void stop_mp3();
protected:
libvlc_instance_t *inst;
libvlc_media_player_t *mp;
libvlc_media_t *m;
private:
};
// rePlay.cpp
#include "rePlay.h"
#include <vlc/vlc.h>
#include <mutex>
std::mutex mp3_mutex;
rePlay::rePlay()
{
//ctor
}
rePlay::~rePlay()
{
//dtor
}
void rePlay::play_mp3(const char* path){
mp3_mutex.lock();
// load the vlc engine
inst = libvlc_new(0, NULL);
printf("apro il file %d\n", inst);
// create a new item
m = libvlc_media_new_path(inst, path);
// create a media play playing environment
mp = libvlc_media_player_new_from_media(m);
// no need to keep the media now
libvlc_media_release(m);
// play the media_player
libvlc_media_player_play(mp);
printf("Done.\n");
}
void rePlay::stop_mp3(){
mp3_mutex.unlock();
// stop playing
libvlc_media_player_stop(mp);
// free the media_player
libvlc_media_player_release(mp);
libvlc_release(inst);
}
// firstpanel.h
class firstpanel: public wxPanel
{
public:
firstpanel(wxWindow* parent, Isola02Frame*, wxWindowID id=wxID_ANY,const wxPoint& pos=wxDefaultPosition,const wxSize& size=wxDefaultSize);
virtual ~firstpanel();
void checkValue(wxCommandEvent& event);
void check_cf(wxTimerEvent& event);
rePlay *mp3_apertura_porta = new rePlay(); // <-- I DECLARED THE pointer here
//(*Declarations(firstpanel)
wxStaticText* assistenza;
wxStaticText* first_panel;
wxStaticText* identificazione;
wxTextCtrl* tessera;
//*)
...
}
// firstpanel.cpp
std::thread second = std::thread([this]() noexcept {
this->mp3_apertura_porta->play_mp3("/home/robodyne/Project/audio/scegli-rifiuto.mp3"); });
second.join();
// secondpanel.cpp
void secondpanel::OnBitmapButton2Click(wxCommandEvent& event)
{
firstpanel *ptr;
ptr->mp3_apertura_porta->stop_mp3();
}
EDIT1: Thanks to #Ted Lyngmo, I used the libvlcpp library which seems to be async somehow and it works fine. The only problem is that I do not know how to call mp.stopAsync() from stop_mp3() to stop the audio file because variable mp is not global.
#include "rePlay.h"
#include <vlc/vlc.h>
#include <mutex>
#include <unistd.h>
#include "vlcpp/vlc.hpp"
std::mutex mp3_mutex;
rePlay::rePlay()
{
//ctor
}
rePlay::~rePlay()
{
//dtor
}
void rePlay::play_mp3(const char* path){
auto instance = VLC::Instance(0, nullptr);
auto media = VLC::Media(instance, path, VLC::Media::FromPath);
auto mp = VLC::MediaPlayer(media);
auto mp.play();
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)
#else
mp.stop();
#endif
}
void rePlay::stop_mp3(){
mp.stopAsync(); <-- variable mp is not global!
}
EDIT2:
I think the libvlcpp doesn't work well with GUI applications.
If I run it in a console application, I'm able to perform other operations in parallel, but when I execute it in the WxWidgets application, it blocks the flow.
This is the terminal console application:
#include "vlcpp/vlc.hpp"
#include <thread>
#include <iostream>
int main(int ac, char** av)
{
if (ac < 2)
{
std::cerr << "usage: " << av[0] << " <file to play>" << std::endl;
return 1;
}
auto instance = VLC::Instance(0, nullptr);
auto media = VLC::Media(instance, av[1], VLC::Media::FromPath);
auto mp = VLC::MediaPlayer(media);
mp.play();
for (int i=0; i < 10000000; i++){
printf("%d\n", i);
}
std::this_thread::sleep_for( std::chrono::seconds( 10 ) );
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)
mp.stopAsync();
#else
mp.stop();
#endif
}
the for() cycle works in parallel while the mp3 is playing.
The same doesn't happen if I use it with my application.

Related

Google Speech Recognition doesn't work because of colliding threads Qt C++

I'm using Google's Speech-To-Text API in my Qt C++ application.
Google's C++ documentation is helpful but to an extent.
In my code below, if I uncomment
std::this_thread::sleep_for(std::chrono::seconds(1));
The speech recognition is working, but not properly - it's skipping some words. But without this line, it doesn't work at all. I think that's because the while loop of MicrophoneThreadMain() collide with the while loop of start_speech_to_text(). But I'm not sure.
I want these two functions to run side-by-side simultaneously, without interruptions, and with no delays.
I tried to use QThreads and Signal and Slots but couldn’t make it work.
speech_to_text.cpp
#include "speechtotext.h"
using google::cloud::speech::v1::StreamingRecognitionConfig;
using google::cloud::speech::v1::RecognitionConfig;
using google::cloud::speech::v1::Speech;
using google::cloud::speech::v1::StreamingRecognizeRequest;
using google::cloud::speech::v1::StreamingRecognizeResponse;
SpeechToText::SpeechToText(QObject *parent) : QObject(parent)
{
}
void SpeechToText::initialize()
{
QAudioFormat qtFormat;
// Get default audio input device
QAudioDeviceInfo qtInfo = QAudioDeviceInfo::defaultInputDevice();
// Set the audio format settings
qtFormat.setCodec("audio/pcm");
qtFormat.setByteOrder(QAudioFormat::Endian::LittleEndian);
qtFormat.setChannelCount(1);
qtFormat.setSampleRate(16000);
qtFormat.setSampleSize(16);
qtFormat.setSampleType(QAudioFormat::SignedInt);
// Check whether the format is supported
if (!qtInfo.isFormatSupported(qtFormat)) {
qWarning() << "Default format is not supported";
exit(3);
}
// Instantiate QAudioInput with the settings
audioInput = new QAudioInput(qtFormat);
// Start receiving data from audio input
ioDevice = audioInput->start();
emit finished_initializing();
}
void SpeechToText::MicrophoneThreadMain(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *streamer)
{
StreamingRecognizeRequest request;
std::size_t size_read;
while(true)
{
audioDataBuffer.append(ioDevice->readAll());
size_read = audioDataBuffer.size();
// And write the chunk to the stream.
request.set_audio_content(&audioDataBuffer.data()[0], size_read);
std::cout << "Sending " << size_read / 1024 << "k bytes." << std::endl;
streamer->Write(request);
//std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void SpeechToText::start_speech_to_text()
{
StreamingRecognizeRequest request;
auto *streaming_config = request.mutable_streaming_config();
RecognitionConfig *recognition_config = new RecognitionConfig();
recognition_config->set_language_code("en-US");
recognition_config->set_sample_rate_hertz(16000);
recognition_config->set_encoding(RecognitionConfig::LINEAR16);
streaming_config->set_allocated_config(recognition_config);
// Create a Speech Stub connected to the speech service.
auto creds = grpc::GoogleDefaultCredentials();
auto channel = grpc::CreateChannel("speech.googleapis.com", creds);
std::unique_ptr<Speech::Stub> speech(Speech::NewStub(channel));
// Begin a stream.
grpc::ClientContext context;
auto streamer = speech->StreamingRecognize(&context);
// Write the first request, containing the config only.
streaming_config->set_interim_results(true);
streamer->Write(request);
// The microphone thread writes the audio content.
std::thread microphone_thread(&SpeechToText::MicrophoneThreadMain, this, streamer.get());
// Read responses.
StreamingRecognizeResponse response;
while (streamer->Read(&response)) // Returns false when no more to read.
{
// Dump the transcript of all the results.
for (int r = 0; r < response.results_size(); ++r)
{
auto result = response.results(r);
std::cout << "Result stability: " << result.stability() << std::endl;
for (int a = 0; a < result.alternatives_size(); ++a)
{
auto alternative = result.alternatives(a);
std::cout << alternative.confidence() << "\t"
<< alternative.transcript() << std::endl;
}
}
}
grpc::Status status = streamer->Finish();
microphone_thread.join();
if (!status.ok()) {
// Report the RPC failure.
qDebug() << "error RPC";
std::cerr << status.error_message() << std::endl;
}
}
speech_to_text.h
#ifndef SPEECHTOTEXT_H
#define SPEECHTOTEXT_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <thread>
#include <chrono>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include <functional>
#include <QtMultimedia>
#include <QtMultimedia/QAudioInput>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QIODevice>
#include <QtConcurrent>
#include <QMutex>
#include <grpc++/grpc++.h>
#include "google/cloud/speech/v1/cloud_speech.grpc.pb.h"
using google::cloud::speech::v1::StreamingRecognitionConfig;
using google::cloud::speech::v1::RecognitionConfig;
using google::cloud::speech::v1::Speech;
using google::cloud::speech::v1::StreamingRecognizeRequest;
using google::cloud::speech::v1::StreamingRecognizeResponse;
class SpeechToText : public QObject
{
Q_OBJECT
public:
explicit SpeechToText(QObject *parent = nullptr);
signals:
void finished_initializing();
void finished_speech_to_text(QString);
public slots:
void initialize();
void start_speech_to_text();
private:
void MicrophoneThreadMain(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *);
QAudioInput *audioInput;
QIODevice *ioDevice;
QByteArray audioDataBuffer;
};
#endif // SPEECHTOTEXT_H
Any idea on how to solve this?
I post here the solution to my problem. Thanks #allquixotic for all the helpful information.
in mainwindow.cpp
void MainWindow::setUpMicrophoneRecorder()
{
microphone_thread = new QThread(this);
microphone_recorder_engine.moveToThread(microphone_thread);
connect(microphone_thread, SIGNAL(started()), &microphone_recorder_engine, SLOT(start_listen()));
connect(&microphone_recorder_engine, &MicrophoneRecorder::microphone_data_raw,
this, [this] (const QByteArray &data) {
this->speech_to_text_engine.listen(data);
});
microphone_thread->start();
}
void MainWindow::setUpSpeechToTextEngine()
{
speech_to_text_thread = new QThread(this);
speech_to_text_engine.moveToThread(speech_to_text_thread);
connect(speech_to_text_thread, SIGNAL(started()), &speech_to_text_engine, SLOT(initialize()));
connect(&speech_to_text_engine, SIGNAL(finished_speech_to_text(QString)), this, SLOT(process_user_input(QString)));
speech_to_text_thread->start();
}
microphonerecorder.h
#ifndef MICROPHONERECORDER_H
#define MICROPHONERECORDER_H
#include <QObject>
#include <QByteArray>
#include <QDebug>
#include <QtMultimedia>
#include <QtMultimedia/QAudioInput>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QIODevice>
class MicrophoneRecorder : public QObject
{
Q_OBJECT
public:
explicit MicrophoneRecorder(QObject *parent = nullptr);
signals:
void microphone_data_raw(const QByteArray &);
public slots:
void start_listen();
private slots:
void listen(const QByteArray &);
private:
QAudioInput *audioInput;
QIODevice *ioDevice;
QByteArray audioDataBuffer;
};
#endif // MICROPHONERECORDER_H
microphonerecorder.cpp
#include "microphonerecorder.h"
MicrophoneRecorder::MicrophoneRecorder(QObject *parent) : QObject(parent)
{
}
void MicrophoneRecorder::listen(const QByteArray &audioData)
{
emit microphone_data_raw(audioData);
}
void MicrophoneRecorder::start_listen()
{
QAudioFormat qtFormat;
// Get default audio input device
QAudioDeviceInfo qtInfo = QAudioDeviceInfo::defaultInputDevice();
// Set the audio format settings
qtFormat.setCodec("audio/pcm");
qtFormat.setByteOrder(QAudioFormat::Endian::LittleEndian);
qtFormat.setChannelCount(1);
qtFormat.setSampleRate(16000);
qtFormat.setSampleSize(16);
qtFormat.setSampleType(QAudioFormat::SignedInt);
// Check whether the format is supported
if (!qtInfo.isFormatSupported(qtFormat)) {
qWarning() << "Default format is not supported";
exit(3);
}
// Instantiate QAudioInput with the settings
audioInput = new QAudioInput(qtFormat);
// Start receiving data from audio input
ioDevice = audioInput->start();
// Listen to the received data for wake words
QObject::connect(ioDevice, &QIODevice::readyRead, [=] {
listen(ioDevice->readAll());
});
}
speechtotext.h
#ifndef SPEECHTOTEXT_H
#define SPEECHTOTEXT_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>
#include <thread>
#include <chrono>
#include <string>
#include <QtMultimedia>
#include <QtMultimedia/QAudioInput>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QIODevice>
#include <QtConcurrent>
#include <QMutex>
#include <grpc++/grpc++.h>
#include "google/cloud/speech/v1/cloud_speech.grpc.pb.h"
using google::cloud::speech::v1::StreamingRecognitionConfig;
using google::cloud::speech::v1::RecognitionConfig;
using google::cloud::speech::v1::Speech;
using google::cloud::speech::v1::StreamingRecognizeRequest;
using google::cloud::speech::v1::StreamingRecognizeResponse;
class SpeechToText : public QObject
{
Q_OBJECT
public:
explicit SpeechToText(QObject *parent = nullptr);
signals:
void finished_initializing();
void in_speech_to_text();
void out_of_speech_to_text();
void finished_speech_to_text(QString);
public slots:
void initialize();
void listen(const QByteArray &);
void start_speech_to_text();
private:
void MicrophoneThreadMain(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *);
void StreamerThread(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *);
QByteArray audioDataBuffer;
int m_start_time;
};
#endif // SPEECHTOTEXT_H
speechtotext.cpp
#include "speechtotext.h"
using google::cloud::speech::v1::StreamingRecognitionConfig;
using google::cloud::speech::v1::RecognitionConfig;
using google::cloud::speech::v1::Speech;
using google::cloud::speech::v1::StreamingRecognizeRequest;
using google::cloud::speech::v1::StreamingRecognizeResponse;
SpeechToText::SpeechToText(QObject *parent) : QObject(parent)
{
}
void SpeechToText::initialize()
{
emit finished_initializing();
}
void SpeechToText::MicrophoneThreadMain(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *streamer)
{
StreamingRecognizeRequest request;
std::size_t size_read;
while (time(0) - m_start_time <= TIME_RECOGNITION)
{
int chunk_size = 64 * 1024;
if (audioDataBuffer.size() >= chunk_size)
{
QByteArray bytes_read = QByteArray(audioDataBuffer);
size_read = std::size_t(bytes_read.size());
// And write the chunk to the stream.
request.set_audio_content(&bytes_read.data()[0], size_read);
bool ok = streamer->Write(request);
/*if (ok)
{
std::cout << "Sending " << size_read / 1024 << "k bytes." << std::endl;
}*/
audioDataBuffer.clear();
audioDataBuffer.resize(0);
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
qDebug() << "Out of speech recognition: " << end_date;
emit out_of_speech_to_text();
streamer->WritesDone();
}
void SpeechToText::StreamerThread(grpc::ClientReaderWriterInterface<StreamingRecognizeRequest,
StreamingRecognizeResponse> *streamer)
{
// Read responses.
StreamingRecognizeResponse response;
while (time(0) - m_start_time <= TIME_RECOGNITION)
{
if(streamer->Read(&response)) // Returns false when no more to read.
{
// Dump the transcript of all the results.
if (response.results_size() > 0)
{
auto result = response.results(0);
if (result.alternatives_size() > 0)
{
auto alternative = result.alternatives(0);
auto transcript = QString::fromStdString(alternative.transcript());
if (result.is_final())
{
qDebug() << "Speech recognition: " << transcript;
emit finished_speech_to_text(transcript);
}
}
}
}
}
}
void SpeechToText::listen(const QByteArray &audioData)
{
audioDataBuffer.append(audioData);
}
void SpeechToText::start_speech_to_text()
{
qDebug() << "in start_speech_to_text: " << start_date;
emit in_speech_to_text();
m_start_time = time(0);
audioDataBuffer.clear();
audioDataBuffer.resize(0);
StreamingRecognizeRequest request;
auto *streaming_config = request.mutable_streaming_config();
RecognitionConfig *recognition_config = new RecognitionConfig();
recognition_config->set_language_code("en-US");
recognition_config->set_sample_rate_hertz(16000);
recognition_config->set_encoding(RecognitionConfig::LINEAR16);
streaming_config->set_allocated_config(recognition_config);
// Create a Speech Stub connected to the speech service.
auto creds = grpc::GoogleDefaultCredentials();
auto channel = grpc::CreateChannel("speech.googleapis.com", creds);
std::unique_ptr<Speech::Stub> speech(Speech::NewStub(channel));
// Begin a stream.
grpc::ClientContext context;
auto streamer = speech->StreamingRecognize(&context);
// Write the first request, containing the config only.
streaming_config->set_interim_results(true);
streamer->Write(request);
// The microphone thread writes the audio content.
std::thread microphone_thread(&SpeechToText::MicrophoneThreadMain, this, streamer.get());
std::thread streamer_thread(&SpeechToText::StreamerThread, this, streamer.get());
microphone_thread.join();
streamer_thread.join();
}
You should really follow Google's example and only do 64k at a time.
You should use WritesDone() on the streamer when you intend the request to be shipped to Google's server.
It appears that you aren't ever clearing out your QByteArray's data, so it will just pile up over time with each successive append call on your QByteArray. Since you're using a pointer to the first element of data in the underlying array, each time you run through your loop, you're sending the entire audio data that's been captured up to that point to streamer. I suggest a nested loop that calls QIODevice::read(char *data, qint64 maxSize) repeatedly until your QByteArray has exactly 64KB. You'll need to handle a return value of -1 indicating end of stream, and adjust maxSize downwards based on how much more data is needed to fill your array up to 64k. Requests to Google's API with too little data (e.g. just a couple of bytes as your current loop appears to do at first) may get you rate-limited, or create upstream congestion on the Internet connection due to the high protocol overhead to data ratio. Also it's probably easier to handle this with a plain C-style array of a fixed size (64k) rather than a QByteArray because you don't need resizing, and AFAIK QByteArray::clear() could cause memory allocation (not great for performance). To avoid re-sending old data on a short write (e.g. when the microphone stream closes before the 64k buffer is full), you should also memset(array, 0, sizeof array); after each ClientReaderWriterInterface::WritesDone() call.
If the network can't keep up with the incoming microphone data, you may end up with an overrun situation on the QAudioInput where it runs out of local buffer to store the audio. More buffering makes this less likely but also decreases responsiveness. You may want to just buffer all the data that comes off of the QAudioInput into an unbounded QByteArray and read out of that 64k at a time (you can do so by wrapping it in a QBuffer and all your code dealing with QIODevice in MicrophoneThreadMain() will be compatible.) I think, normally, for projects like yours, the user would prefer to have worse responsiveness, as opposed to having to repeat themselves, in case of a network related overrun. But there's probably a threshold - maybe 5 seconds or so - after which the buffered data might become "out of date" as the user may try speaking into the mic again, causing a weird effect of multiple STT events happening in rapid succession once the upstream bottleneck frees up.

Difference between R.3.4.4 and R.3.5.1 in R API for C

I have a C++ program that uses R API to send commands to R and display the result. Once reduced to it's minimal form, it's an R console coded in C++.
It used to work fine (most of the time) with R.3.4.3 and R.3.4.4, but everything falls appart when I tried to make the transition to R.3.5.1.
For some commands (typically a call to "par", or "barplot", or anything related to graphics), I get the error message: "Error in < My command > : the base graphics system is not registered"
I never encountered this error before, and google searching it gives surprisingly few results.
My console using R3.4.3 (it's the same with 3.4.4) :
The same commands using R3.5.1:
Note that this behavior does not happen in a regular R console, so it must have something to do with the R-API for C/C++ (and the way it handles graphics devices, maybe?).
My code consists essentially in a RManager class that calls the API to exchange with R, and a simple window providing a lineEdit where user can input its command, and a text field where R results are displayed (see images above).
I'll provide the full code for reproducibility, but if you want to jump where R communication is really handled, it all happens in rmanager.cpp, the rest is just the GUI.
main.cpp:
#include "mainwindow.h"
#include <QApplication>
#include <thread>
#include <iostream>
#include <chrono>
#ifdef _WIN32
#include <windows.h>
#include <tlhelp32.h>
#include <QProcess>
#include <cwchar>
#endif
int main(int argc, char *argv[])
{
int result = 0;
QApplication a(argc, argv);
MainWindow w;
w.show();
result = a.exec();
return result;
}
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTextStream>
#include <QFile>
#include <QTimer>
#include <rmanager.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
// Command received from GUI
void on_inputCmd_returnPressed();
// Read result from the R Manager
void getResult(QString);
//Read errors from the R Manager
void getError(QString);
private:
Ui::MainWindow *ui;
QTimer pollInput;
RManager *rconsole;
// Result buffer for last command
QString resultBuffer;
// Send command directly to R
void sendRCmd(QString command);
signals:
// Starts the R Manager event loop
void runConsole();
};
#endif // MAINWINDOW_H
mainWindow.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this); // Just a QLineEdit for inputs and a QTextEdit to display result.
ui->outputConsole->document()->setMaximumBlockCount(500);
// R API connection
rconsole = new RManager(parent);
// Signals connection
connect(rconsole, SIGNAL(writeConsole(QString)), this, SLOT(getResult(QString)));
connect(rconsole, SIGNAL(writeConsoleError(QString)), this, SLOT(getError(QString)));
connect(this, SIGNAL(runConsole()), rconsole, SLOT(runConsole()));
pollInput.start(10); // Check for R results every 10 ms.
// R Callbacks event loop
emit runConsole();
}
MainWindow::~MainWindow()
{
delete ui;
pollInput.stop();
}
/**
* #brief MainWindow::getResult Aggregate results from R until an only '\n' is sent
* Then send it to the user (RPP or GUI)
* #param res
*/
void MainWindow::getResult(QString res)
{
// While != "\n" add res to the result buffer
if (res != "\n") {
resultBuffer.append(res);
} else {
// the res to the resultBuffer to conserve the last \n
resultBuffer.append(res);
// Get the current text values from the text fields and append the result
ui->outputConsole->append(resultBuffer);
resultBuffer.clear();
}
}
/**
* #brief MainWindow::getError Send immediatly any error from R
* #param error
*/
void MainWindow::getError(QString error)
{
qDebug() << "getError called with error: " << error ;
// Get the current text values from the text fields and append the result
ui->outputConsole->append(error);
}
/**
* #brief MainWindow::sendRCmd Low level method to send command to R
* Display the command in the GUI
* #param command
*/
void MainWindow::sendRCmd(QString command)
{
ui->outputConsole->append("> "+command+"\n");
// Send the command to R
rconsole->parseEval(command);
ui->inputCmd->clear();
}
/**
* #brief MainWindow::on_inputCmd_returnPressed Send command to R from the GUI
*/
void MainWindow::on_inputCmd_returnPressed()
{
// Get the current text values from the text fields
QString command = ui->inputCmd->text();
sendRCmd(command);
}
ui_mainwindow.h (generated by Qt Creator):
/********************************************************************************
** Form generated from reading UI file 'mainwindow.ui'
**
** Created by: Qt User Interface Compiler version 5.11.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QTextEdit>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_MainWindow
{
public:
QWidget *centralWidget;
QVBoxLayout *verticalLayout;
QTextEdit *outputConsole;
QLineEdit *inputCmd;
void setupUi(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QStringLiteral("MainWindow"));
MainWindow->resize(382, 413);
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QStringLiteral("centralWidget"));
verticalLayout = new QVBoxLayout(centralWidget);
verticalLayout->setSpacing(6);
verticalLayout->setContentsMargins(11, 11, 11, 11);
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
outputConsole = new QTextEdit(centralWidget);
outputConsole->setObjectName(QStringLiteral("outputConsole"));
outputConsole->setFocusPolicy(Qt::NoFocus);
outputConsole->setUndoRedoEnabled(false);
outputConsole->setReadOnly(true);
verticalLayout->addWidget(outputConsole);
inputCmd = new QLineEdit(centralWidget);
inputCmd->setObjectName(QStringLiteral("inputCmd"));
inputCmd->setClearButtonEnabled(true);
verticalLayout->addWidget(inputCmd);
MainWindow->setCentralWidget(centralWidget);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
void retranslateUi(QMainWindow *MainWindow)
{
MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr));
} // retranslateUi
};
namespace Ui {
class MainWindow: public Ui_MainWindow {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_MAINWINDOW_H
rmanager.h
#ifndef RMANAGER_H
#define RMANAGER_H
#include <QObject>
class RManager : public QObject
{
Q_OBJECT
public:
explicit RManager(QObject *parent = 0);
// R side methods/callbacks
int parseEval(const QString & line);
// R interface callbacks
void myShowMessage( const char* message );
void myWriteConsoleEx( const char* message, int len, int oType );
int myReadConsole(const char *, unsigned char *, int, int);
int winReadConsole(const char*, char*, int, int);
void myResetConsole();
void myFlushConsole();
void myCleanerrConsole();
void myBusy( int which );
static RManager &r();
signals:
void writeConsole(QString);
void writeConsoleError(QString);
public slots:
void runConsole();
private:
bool R_is_busy;
static RManager *r_inst;
};
// Functions to match the library : call RManager's methods
void myR_ShowMessage( const char* message );
void myR_WriteConsoleEx( const char* message, int len, int oType );
int myR_ReadConsole(const char *prompt, unsigned char *buf, int len, int addtohistory);
int ReadConsole(const char *prompt, char *buf, int len, int addtohistory);
void myR_ResetConsole();
void myR_FlushConsole();
void myR_ClearerrConsole();
void myR_Busy( int which );
void myR_CallBack();
void myR_AskOk(const char *);
int myR_AskYesNoCancel(const char *);
#endif // RMANAGER_H
And finally, rmanager.cpp
#include "rmanager.h"
#include <qmessagebox.h>
#include <QDebug>
#define R_INTERFACE_PTRS
#include <Rembedded.h>
#ifndef _WIN32
#include <Rinterface.h> // For Linux.
#endif
#include <R_ext/RStartup.h>
#include <Rinternals.h>
#include <R_ext/Parse.h>
#include <locale.h>
RManager* RManager::r_inst = 0 ;
RManager::RManager(QObject *parent) : QObject(parent)
{
if (r_inst) {
throw std::runtime_error( tr("Il ne peut y avoir qu'une instance de RppConsole").toStdString() ) ;
} else {
r_inst = this ;
}
const char *argv[] = {"RConsole", "--gui=none", "--no-save",
"--silent", "--vanilla", "--slave"};
int argc = sizeof(argv) / sizeof(argv[0]);
setlocale(LC_NUMERIC, "C"); //try to ensure R uses .
#ifndef _WIN32
R_SignalHandlers = 0; // Don't let R set up its own signal handlers
#endif
Rf_initEmbeddedR(argc, (char**)argv); // The call that is supposed to register the graphics system, amongst other things.
R_ReplDLLinit(); // this is to populate the repl console buffers
structRstart Rst;
R_DefParams(&Rst);
Rst.R_Interactive = (Rboolean) false; // sets interactive() to eval to false
#ifdef _WIN32
Rst.rhome = getenv("R_HOME");
Rst.home = getRUser();
Rst.CharacterMode = LinkDLL;
Rst.ReadConsole = ReadConsole;
Rst.WriteConsole = NULL;
Rst.WriteConsoleEx = myR_WriteConsoleEx;
Rst.CallBack = myR_CallBack;
Rst.ShowMessage = myR_AskOk;
Rst.YesNoCancel = myR_AskYesNoCancel;
Rst.Busy = myR_Busy;
#endif
R_SetParams(&Rst);
// Assign callbacks to R's
#ifndef _WIN32
ptr_R_ShowMessage = myR_ShowMessage ;
ptr_R_ReadConsole = myR_ReadConsole;
ptr_R_WriteConsoleEx = myR_WriteConsoleEx ;
ptr_R_WriteConsole = NULL;
ptr_R_ResetConsole = myR_ResetConsole;
ptr_R_FlushConsole = myR_FlushConsole;
ptr_R_ClearerrConsole = myR_ClearerrConsole;
ptr_R_Busy = myR_Busy;
R_Outputfile = NULL;
R_Consolefile = NULL;
#endif
#ifdef TIME_DEBUG
_earliestSendToRBool = false;
#endif
Rf_endEmbeddedR(0);
}
RManager &RManager::r()
{
return *r_inst;
}
void RManager::runConsole()
{
// Start the event loop to get results from R
R_ReplDLLinit();
while (R_ReplDLLdo1() > 0) {}
}
/**
* #brief RManager::parseEval is the core of this console, sending commands to R.
* #param line
* #return
*/
int RManager::parseEval(const QString &line) {
ParseStatus status;
SEXP cmdSexp, cmdexpr = R_NilValue;
int i, errorOccurred, retVal=0;
// Convert the command line to SEXP
PROTECT(cmdSexp = Rf_allocVector(STRSXP, 1));
SET_STRING_ELT(cmdSexp, 0, Rf_mkChar(line.toLocal8Bit().data()));
cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
switch (status){
case PARSE_OK:
// Loop is needed here as EXPSEXP might be of length > 1
for(i = 0; ((i < Rf_length(cmdexpr)) && (retVal==0)); i++){
R_tryEval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv, &errorOccurred);
if (errorOccurred) {
retVal = -1;
}
}
break;
case PARSE_INCOMPLETE:
// need to read another line
retVal = 1;
break;
case PARSE_NULL:
Rf_warning(tr("%s: Etat d'analyse de commande : NULL (%d)\n").toStdString().data(), "RPPConsole", status);
retVal = -2;
break;
case PARSE_ERROR:
Rf_warning(tr("Erreur d'analyse de la commande : \"%s\"\n").toStdString().data(), line.toStdString().c_str());
retVal = -2;
break;
case PARSE_EOF:
Rf_warning(tr("%s: Etat d'analyse de commande : EOF (%d)\n").toStdString().data(), "RPPConsole", status);
break;
default:
Rf_warning(tr("%s: Etat d'analyse de commande non documenté %d\n").toStdString().data(), "RPPConsole", status);
retVal = -2;
break;
}
UNPROTECT(2);
return retVal;
}
// RManager callbacks implementation
void RManager::myShowMessage(const char *message)
{
// Never called till now
QMessageBox::information(qobject_cast<QWidget*>(parent()),QString(tr("Bonjour le monde")),QString(message),QMessageBox::Ok,QMessageBox::NoButton);
}
void RManager::myWriteConsoleEx(const char *message, int len, int oType)
{
QString msg;
if (len) {
msg = QString::fromLocal8Bit(message, len);
if(!oType)
emit writeConsole(msg);
else
emit writeConsoleError(msg);
}
}
int RManager::myReadConsole(const char* /*prompt*/, unsigned char* /*buf*/, int /*len*/, int /*addtohistory*/ ){
return 0;
}
// For Windows, unsigned char is replaced by char
int RManager::winReadConsole(const char* /*prompt*/, char* /*buf*/, int /*len*/, int /*addtohistory*/ ){
return 0;
}
void RManager::myResetConsole()
{
}
void RManager::myFlushConsole()
{
}
void RManager::myCleanerrConsole()
{
}
void RManager::myBusy( int which ){
R_is_busy = static_cast<bool>( which ) ;
}
// Connects R callbacks to RManager static methods
void myR_ShowMessage( const char* message ){
RManager::r().myShowMessage( message ) ;
}
void myR_WriteConsoleEx( const char* message, int len, int oType ){
RManager::r().myWriteConsoleEx(message, len, oType);
}
int myR_ReadConsole(const char *prompt, unsigned char *buf, int len, int addtohistory){
return RManager::r().myReadConsole( prompt, buf, len, addtohistory ) ;
}
int ReadConsole(const char *prompt, char *buf, int len, int addtohistory) {
return RManager::r().winReadConsole( prompt, buf, len, addtohistory ) ;
}
void myR_ResetConsole(){
RManager::r().myResetConsole();
}
void myR_FlushConsole(){
RManager::r().myFlushConsole();
}
void myR_ClearerrConsole(){
RManager::r().myCleanerrConsole();
}
void myR_Busy( int which ){
RManager::r().myBusy(which);
}
void myR_CallBack() {
// Called during i/o, eval, graphics in ProcessEvents
}
void myR_AskOk(const char* /*info*/) {
}
int myR_AskYesNoCancel(const char* /*question*/) {
const int yes = 1;
return yes;
}
Thank you in advance for your ideas on what the problem might be. Is it a R.3.5.1 bug, or is there something that I should have defined/connected and missed ? I read the R.3.5.1 changes description without finding a clue about it.
PS: I'm under windows 10, compiling with Microsoft Visual C++ Compiler 15.0 (32 bits), and using Qt 5.11.0 (for the GUI components).
PPS: Following user2554330's advice, I checked for calls to GEregisterSystem, that is supposed to set the graphics system, and thus prevent this error. I found that in both cases, this function is called, at application launch, but not with the same call stack.
For R.3.4.3:
For R.3.5.1:
I found a solution (thanks to Luke Tierney on R-devel mailing list).
I just needed to move the call to Rf_endEmbeddedR to the destructor of RManager, where it should have been.
It doesn't really explain why it worked the way it was before with R.3.4 and not with R.3.5, but it does solve the practical issue.
Maybe this was never supposed to work with Rf_endEmbeddedR called so soon, and it only used to, thanks to a bug that has been fixed.

Assertion failed on __pthread_mutex_cond_lock_full in a load test

I used the following code to create a timer object in my c++ application running on a debian 8.
class Timer
{
private:
std::condition_variable cond_;
std::mutex mutex_;
int duration;
void *params;
public:
Timer::Timer(void (*func)(void*))
{
this->handler = func;
this->duration = 0;
this->params = NULL;
};
Timer::~Timer(){};
void Timer::start(int duree, void* handlerParams)
{
this->duration = duree;
this->params = handlerParams;
/*
* Launch the timer thread and wait it
*/
std::thread([this]{
std::unique_lock<std::mutex> mlock(mutex_);
std::cv_status ret = cond_.wait_for(mlock,
std::chrono::seconds(duration));
if ( ret == std::cv_status::timeout )
{
handler(params);
}
}).detach();
};
void Timer::stop()
{
cond_.notify_all();
}
};
It works correctly under gdb and under normal conditions, but in a load test of 30 requests or more, it crashes with the assertion :
nptl/pthread_mutex_lock.c:350: __pthread_mutex_cond_lock_full: Assertion `(-(e)) != 3 || !robust' failed.
I don't understand the cause of this assertion. Can anyone help me please ??
Thank you
Basically you have a detached thread that accesses the timer object, so it's likely that you destroyed the Timer object but the thread is still running and accessing it's member(mutex, conditional variable).
The assert itself says, from glibc source code, that the owner of the mutex has died.
Thanks a lot for your comments ! I'll try to change the thread detach, and do the load tests.
This is a MVCE of my problem, which is a part of a huge application.
/**
* \file Timer.hxx
* \brief Definition of Timer class.
*/
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>
class Timer
{
private:
std::condition_variable cond_;
std::mutex mutex_;
int duration;
void *params;
public:
Timer(void (*func)(void*));
~Timer();
void (*handler)(void*);
void start(int duree, void* handlerParams);
void stop();
};
/*
* Timer.cxx
*/
#include "Timer.hxx"
Timer::Timer(void (*func)(void*))
{
//this->set_handler(func, params);
this->handler = func;
this->duration = 0;
this->params = NULL;
}
Timer::~Timer()
{
}
void Timer::start(int duree, void* handlerParams)
{
this->duration = duree;
this->params = handlerParams;
/*
* Launch the timer thread and wait it
*/
std::thread([this]{
std::unique_lock<std::mutex> mlock(mutex_);
std::cv_status ret = cond_.wait_for(mlock, std::chrono::seconds(duration));
if ( ret == std::cv_status::timeout )
{
handler(params);
}
}).detach();
}
void Timer::stop()
{
cond_.notify_all();
}
/*
* MAIN
*/
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include "Timer.hxx"
using namespace std;
void timeoutHandler(void* params)
{
char* data= (char*)params;
cout << "Timeout triggered !! Received data is: " ;
if (data!=NULL)
cout << data << endl;
}
int main(int argc, char **argv)
{
int delay=5;
char data[20] ="This is a test" ;
Timer *t= new Timer(&timeoutHandler) ;
t->start(delay, data);
cout << "Timer started !! " << endl;
sleep(1000);
t->stop();
delete t;
cout << "Timer deleted !! " << endl;
return 0;
}

C++ moving class with thread inside

i'm writing server with will handle client connection, but i have problem moving class with thread inside to vector, i know if i had only thread i can move it to vector with std::move(), but here i have thread inside a class and i'm getting a lot of errors because thread is non-movable object.
Core.cpp:
#define OS_LINUX
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <csignal>
#include <cstdlib>
#include <TCPServer/TCPServer.h>
#include <TCPServer/TCPServerConnection.h>
#include <ProcessManip/ProcessManip.h>
#include <Log/Log.h>
#include "ConnectionHandler.h"
//Global
bool TERMNINATE = false;//If true loops must end
const int PORT = 8822;
//Proto
void terminate(int sig);
void receive_message(ConnectionHandler *handler,string message);
using namespace std;
int main(int argc,char *argv[])
{
//Add Terminate handler
signal(SIGINT,terminate);
//Load configuration
Log::logDebug("Starting ...");
//Init
vector<ConnectionHandler> connections;
//Init modules
//Main
Log::logDebug("Running");
TCPServer server;
if(!server._bind(PORT))
return 1;
if(!server._listen())
return 2;
ProcessManip::cleanProcess(); /* Clean dead processes */
server.setBlocking(false);/* accept returns invalid socket if no connection */
Log::logDebug("Listening ...");
while(!TERMNINATE)
{
TCPServerConnection conn = server._accept();
if(!conn.isValid())
continue;
Log::logDebug((string)"Got connection from: "+conn.getAddress());/* Print IP address of client */
ConnectionHandler ch(&TERMNINATE,std::move(conn));
ch.setCallback(receive_message);
ch.start();
connections.push_back(std::move(ch)); //here is problem
/*connections.push_back(ConnectionHandler(&TERMNINATE,conn)); /* Add it to vector */
/*connections.back().setCallback(receive_message);
connections.back().start();*/
Log::logDebug("Connection added to vector");
}
server.setBlocking(true);
//Dispose
Log::logDebug("Stopping ...");
/*for(auto it = connections.begin();it!=connections.end();)
{
Log::logDebug((string)"Closed connection with: "+(*it).getConnection().getAddress());/* Print IP address of client */
//(*it).close(); /* Close connetion */
// it = connections.erase(it); /* Delete ConnectionHandler from vector */
// }
server._close();
Log::logDebug("Closed");
return 0;
}
void terminate(int sig)
{
//Change global value to true
TERMNINATE = true;
}
void receive_message(ConnectionHandler *handler,string message)
{
Log::logDebug((string)"Message ("+handler->getConnection().getAddress()+") : "+message);
}
ConnectionHandler.h
#ifndef EXT_CONNECTIONHANDLER
#define EXT_CONNECTIONHANDLER
#include <TCPServer/TCPServerConnection.h>
#include <thread>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
class ConnectionHandler
{
public:
ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection);
ConnectionHandler(TCPServerConnection pConnection);
~ConnectionHandler();
void start(); /* Start listening */
void stop(); /* Stop listening */
void close(); /* Stops listening + close connection */
void setConnection(TCPServerConnection pConnection);
TCPServerConnection getConnection();
void setCallback(function<void(ConnectionHandler*,string)> pFunction);
private:
bool *mainTerminate = NULL;
bool handler_terminate = false;
short status = 0;
TCPServerConnection connection;
bool needTerminate();
void run();
void waitForEnd();
function<void(ConnectionHandler*,string)> callback = NULL;
std::thread m_thread;
};
#endif
ConnectionHandler.cpp
#include "ConnectionHandler.h"
ConnectionHandler::ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection)
{
this->mainTerminate = pMainTerminate;
this->connection = pConnection;
}
ConnectionHandler::ConnectionHandler(TCPServerConnection pConnection)
{
this->mainTerminate = NULL;
this->connection = pConnection;
}
ConnectionHandler::~ConnectionHandler()
{
this->close();
}
void ConnectionHandler::start()
{
m_thread = std::thread(&ConnectionHandler::run, this);
this->status = 1;
}
void ConnectionHandler::waitForEnd()
{
if(this->m_thread.joinable())
this->m_thread.join();
}
bool ConnectionHandler::needTerminate()
{
if(mainTerminate!=NULL)
return this->handler_terminate||*(this->mainTerminate);
else
return this->handler_terminate;
}
void ConnectionHandler::run()
{
string message = "";
string tmp = "";
this->connection.setBlocking(false); // So we can terminate any time
while(!this->needTerminate())
{
message = this->connection._receive();
if(message!="")
{
do
{
tmp = this->connection._receive();
message+=tmp;
}while(tmp!=""); /* If we get longer message than we can grab at one time */
this->connection._send(message); /* TODO Remove */
if(this->callback!=NULL)
this->callback(this,message);
message = "";
}
}
this->connection.setBlocking(true);
}
void ConnectionHandler::stop()
{
this->handler_terminate = true; /* Signals thread to stop */
this->waitForEnd();
this->status = 2;
}
void ConnectionHandler::close()
{
this->stop();
this->connection._close(); /* Close connection */
this->status = 3;
}
TCPServerConnection ConnectionHandler::getConnection()
{
return this->connection;
}
void ConnectionHandler::setConnection(TCPServerConnection pConnection)
{
this->connection = pConnection;
}
void ConnectionHandler::setCallback(function<void(ConnectionHandler*,string)> pFunction)
{
this->callback = pFunction;
}
Because this class violates the Rule Of Three, even if the std::thread issue gets addressed, other problems will likely appear; most likely taking the form of mysterious runtime bugs.
The compilation issue with std::thread is not the problem, it's merely a symptom of the real problem: this class should not be moved or copied. This class should only be new-constructed, then stuffed into a std::shared_ptr (or a reasonable facsimile) and stay there until it gets destroyed. Only the std::shared_ptr should be passed around, stuffed into a vector, etc...

OpenCV camera capture from within a thread

This is a small part of the code I'm trying to get to work. This is also one of my first times working with C++. I'm used to higher-level languages, like Java or C#.
The main version is meant to be run as a shared object or DLL. The idea is that an external program (in C#) will start the main loops. The frames from the camera will be captured in a thread. Information will processed inside of that thread and copied to an array ("dataArray"). This copy process will be done while a class mutex is locked. Then, another function called externally will copy that saved array ("dataArray") to a second array ("outArray") and return a pointer to the second array. The external program will use the pointer to copy the data from the second Array, which will not be modified until the function is called again.
But for all that to work, I need the frames to constantly be captured. I realized that I needed something to keep my main function going, so I'm keeping an infinite loop in there. In the "real" version, the keepRunning variable will be changed by the external program running the library.
I was recently lectured on StackOverflow about not making global variables, so I'm keeping the one instance of my class in a static member. That's pretty standard in Java. I don't know if it's bad practice in C++. I was also taken by surprise as to how C++ threads start as soon as they're created, without an explicit "start" instructions. That's why I'm putting my only thread in a vector. That seems to be what most people recommend.
I understand that without keepRunning never being actually changed, the threads will never be joined, but I'll dear with that later. I'm running this on a Mac, but I'll need it to eventually run on Windows, Mac and Linux.
Here's my header:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
using namespace cv;
using namespace std;
class MyCap {
public:
MyCap();
VideoCapture cap;
static MyCap * instance;
void run();
static void RunThreads(MyCap * cap);
bool keepRunning = true; // Will be changed by the external program.
vector<thread> capThreads;
private:
Mat frame;
};
And here's my code:
#include "theheader.h"
MyCap * MyCap::instance = NULL;
int main(int argc, char** argv) {
MyCap::instance = new MyCap();
MyCap::instance->capThreads.push_back(thread(MyCap::RunThreads, MyCap::instance));
// Outside loop.
while(MyCap::instance->keepRunning) {
}
for (int i = 0; i < MyCap::instance->capThreads.size(); i++) {
MyCap::instance->capThreads[i].join();
}
}
MyCap::MyCap() {
namedWindow("flow", 1);
cap.open(0);
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
// Inside loop.
while(keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
With this code, I get a black screen. If I run cap.open(0) from within the run method, I don't even get that. I'm obviously doing something very wrong. But what really puzzles me is: why does it make a difference where that same code is called from? If I run what is now in run inside of main it will work. If I change the call of cap.open(0) from the constructor to run, that changes what the method does. Also the waitKey condition stops working from within the thread. What big thing am I missing?
Version 2
Based on the suggestions of #darien-pardibas, I made this second version:
Header:
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
using namespace cv;
using namespace std;
class MyCap {
public:
MyCap();
void run();
bool keepRunning = true; // Will be changed by the external program.
static void RunThreads(MyCap * cap);
static vector<thread> capThreads;
static MyCap * getInstance();
private:
static MyCap * instance;
};
The main file:
#include "theprogram.h" // I'll admit that, even for a placeholder, it was a bad name.
MyCap * MyCap::instance = NULL;
vector<thread> MyCap::capThreads;
MyCap::MyCap() {
cout << "Instantiate" << endl;
}
MyCap * MyCap::getInstance() {
if (MyCap::instance == NULL) {
MyCap::instance = new MyCap;
}
return MyCap::instance;
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
cout << "Run" << endl;
namedWindow("flow", 1);
cout << "Window created." << endl;
VideoCapture cap(0); // HANGS HERE!
cout << "Camera open." << endl; // This never gets printed.
// Inside loop.
Mat frame;
while(keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
int main(int argc, char** argv) {
MyCap::capThreads.push_back(thread(&MyCap::RunThreads, MyCap::getInstance()));
for (int i = 0; i < MyCap::capThreads.size(); i++) {
MyCap::capThreads[i].join();
}
}
This prints:
Instantiate
Run
Window created.
And hangs there.
But if I move the code from run to main and change keepRunning to true, then it works as expected. I think I'm missing something else, and I'm guessing it has something to do with how C++ works.
Okay, without looking at resolving all design patterns issues I can see in your code, I can confirm that the code below works. I think the main problem was that you needed to create the namedWindow in the same thread where you will be capturing the image and remove the while loop you had in your main method.
// "theheader.h"
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <thread>
#include <vector>
class MyCap {
public:
void run();
static void RunThreads(MyCap * cap);
bool keepRunning = true; // Will be changed by the external program.
std::vector<std::thread> capThreads;
private:
cv::Mat frame;
cv::VideoCapture cap;
MyCap() { }
static MyCap * s_instance;
public:
static MyCap *instance();
};
// "theheader.cpp"
#include "theheader.h"
#pragma comment(lib, "opencv_core248d")
#pragma comment(lib, "opencv_highgui248d")
using namespace std;
using namespace cv;
MyCap * MyCap::s_instance = NULL;
MyCap* MyCap::instance() {
if (s_instance == NULL)
s_instance = new MyCap();
return s_instance;
}
void MyCap::RunThreads(MyCap * cap) {
cap->run();
}
void MyCap::run() {
namedWindow("flow", 1);
cap.open(0);
// Inside loop.
while (keepRunning) {
cap >> frame;
imshow("flow", frame);
if (waitKey(30) >= 0) {
break;
}
}
}
int main(int argc, char** argv) {
MyCap::instance()->capThreads.push_back(thread(&MyCap::RunThreads, MyCap::instance()));
for (int i = 0; i < MyCap::instance()->capThreads.size(); i++) {
MyCap::instance()->capThreads[i].join();
}
}