mpg123 library with Qt - qt

I want add support for playback of mp3 file in my Qt app for embedded linux.
I'm not able to use phonon in Qt. After adding QT += phonon in .pro file it gives me the following error during compilation :
/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/libphonon.so: undefined reference to `QWidget::x11Event(_XEvent*)'
/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/libphonon.so: undefined reference to `QDataStream::QDataStream(QByteArray*, int)'
collect2: ld returned 1 exit status
So now i'm thinking of using the mpg123 lib for decoding mp3 files.
I need help integrating the library in Qt. I've never used a pure c++ library in Qt before so i don't have much idea on how to integrate it.

Hey all !! Finally I figured it out !!
int MP3Player::Init(const char *pFileName)
{
mpg123_init();
m_mpgHandle = mpg123_new(0, 0);
if(mpg123_open(m_mpgHandle, pFileName) != MPG123_OK)
{
qFatal("Cannot open %s: %s", pFileName, mpg123_strerror(m_mpgHandle));
return 0;
}
}
int MP3Player::Play()
{
unsigned char *audio;
int mc;
size_t bytes;
qWarning("play_frame");
static unsigned char* arr = 0;
/* The first call will not decode anything but return MPG123_NEW_FORMAT! */
mc = mpg123_decode_frame(m_mpgHandle, &m_framenum, &audio, &bytes);
if(bytes)
{
/* Normal flushing of data, includes buffer decoding. */
/*This function is my already implemented audio class which uses ALSA to output decoded audio to Sound Card*/
if (m_audioPlayer.Play(arr,bytes) < (int)bytes)
{
qFatal("Deep trouble! Cannot flush to my output anymore!");
}
}
/* Special actions and errors. */
if(mc != MPG123_OK)
{
if(mc == MPG123_ERR)
{
qFatal("...in decoding next frame: %s", mpg123_strerror(m_mpgHandle));
return CSoundDecoder::EOFStream;
}
if(mc == MPG123_DONE)
{
return CSoundDecoder::EOFStream;
}
if(mc == MPG123_NO_SPACE)
{
qFatal("I have not enough output space? I didn't plan for this.");
return CSoundDecoder::EOFStream;
}
if(mc == MPG123_NEW_FORMAT)
{
long iFrameRate;
int encoding;
mpg123_getformat(m_mpgHandle, &iFrameRate, &m_iChannels, &encoding);
m_iBytesPerChannel = mpg123_encsize(encoding);
if (m_iBytesPerChannel == 0)
qFatal("bytes per channel is 0 !!");
m_audioPlayer.Init(m_iChannels , iFrameRate , m_iBytesPerChannel);
}
}
}

In order to get mpg123 working with your QT project you try following steps:
1.download and install mpg123: from the folder where you extracted it to (e.g /home/mpg123-1.13.0/) run ./configure and then "sudo make install"
2.if there are no errors put this line to your *.pro file
LIBS += /usr/local/lib/libmpg123.so
3.then code below should run fine for you:
#include "mpg123.h"
#include <QDebug>
void MainWindow::on_pushButton_2_clicked()
{
const char **decoders = mpg123_decoders();
while (*decoders != NULL)
{
qDebug() << *decoders;
decoders++;
}
}
alternatively you can call mpg123 via system call:
system("mpg123 /home/test.mp3");
hope this helps, regards

Related

QMediaPlayer stream play dose not work in windows

I want to play stream video.
The server was configured using ffserver.
ffplay working fine
ffplay http://192.168.0.122:8090/camera2.mjpeg
OpenCV Examples is wokring fine.
int main(int argc, char *argv[])
{
cv::Mat frame;
cv::VideoCapture cap = cv::VideoCapture("http://192.168.0.122:8090/camera2.mjpeg");
if (!cap.isOpened()) {
return -1;
}
while (1)
{
cap >> frame;
imshow("video", frame);
if (cv::waitKey(1) == 27) break;
}
return 0;
}
But in Qt VideoPlayer example is notworking.
It does not work only on Windows.
It works fine on Ubuntu.
In VideoWidget, it was modified as follows.
void VideoPlayer::openFile()
{
#if 0
QFileDialog fileDialog(this);
fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
fileDialog.setWindowTitle(tr("Open Movie"));
QStringList supportedMimeTypes = m_mediaPlayer->supportedMimeTypes();
if (!supportedMimeTypes.isEmpty())
fileDialog.setMimeTypeFilters(supportedMimeTypes);
fileDialog.setDirectory(QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).value(0, QDir::homePath()));
if (fileDialog.exec() == QDialog::Accepted)
setUrl(fileDialog.selectedUrls().constFirst());
#endif
setUrl(QUrl("http://192.168.0.122:8090/camera2.mjpeg"));
}
qmediaplayer object does not have a valid service.
is need a special library in Windows?
Let me know what is wrong for me.
PS.
file is working
mediaPlayer = new QMediaPlayer(this, QMediaPlayer::StreamPlayback); is same result

Adding a custom sqlite function to a Qt application

I am trying to add a custom sqlite3 regexp function into my Qt application (as recommended by this answer). But as soon as I call the sqlite3_create_function function, I get the message The program has unexpectedly finished. When I debug, it terminates in a segmentation fault in sqlite3_mutex_enter. There is a MWE below, with apologies for the absolute file paths.
The regexp implementation in my code is from this site; it also fails with the msign function here. The various checks of driver()->handle() are straight from the Qt docs.
Incidentally, I used select sqlite_version(); to determine that Qt 5.5 uses sqlite version 3.8.8.2. I found that version by looking through old commits in the Qt GitHub repository.
MWE.pro
QT += core gui
TARGET = MWE
TEMPLATE = app
QT += sql
SOURCES += main.cpp \
D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.c
HEADERS += D:\Qt\Qt5.5.0\5.5\Src\3rdparty\sqlite\sqlite3.h
main.cpp
#include <QtSql>
#include "D:/Qt/Qt5.5.0/5.5/Src/3rdparty/sqlite/sqlite3.h"
void qtregexp(sqlite3_context* ctx, int argc, sqlite3_value** argv)
{
QRegExp regex;
QString str1((const char*)sqlite3_value_text(argv[0]));
QString str2((const char*)sqlite3_value_text(argv[1]));
regex.setPattern(str1);
regex.setCaseSensitivity(Qt::CaseInsensitive);
bool b = str2.contains(regex);
if (b)
{
sqlite3_result_int(ctx, 1);
}
else
{
sqlite3_result_int(ctx, 0);
}
}
int main(int argc, char *argv[])
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("my.db");
db.open();
QVariant v = db.driver()->handle();
if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) {
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
// This shows that the database handle is generally valid:
qDebug() << sqlite3_db_filename(db_handle, "main");
sqlite3_create_function(db_handle, "regexp", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, &qtregexp, NULL, NULL);
qDebug() << "This won't be reached."
QSqlQuery query;
query.prepare("select regexp('p$','tap');");
query.exec();
query.next();
qDebug() << query.value(0).toString();
}
}
db.close();
}
You need to call sqlite3_initialize() after you get the database handle from Qt, according to this forum post.
...
sqlite3 *db_handle = *static_cast<sqlite3 **>(v.data());
if (db_handle != 0) { // check that it is not NULL
sqlite3_initialize();
...
This solves the issue.

QT on OS X, how to detect clicking the app Dock Icon

I have an open Qt Mac app.
I am clicking the app Icon
Is there a way to get a notification for this in the app?
I couldn't get the original answer to compile properly due to deprecation warnings (post-OS X 10.5) and type errors; I changed a few type names and got it to compile, but the code still didn't work.
It turns out that newer versions of Qt (4.8.7+, including 5.x; I use 5.4.1) implement the method we want to add, and class_addMethod fails if the method already exists. See this QTBUG.
Note: the above bug report contains a slightly different solution (I found it after fixing the issue myself).
One solution, that works for me, is to check if the method exists. If it does, we replace it. If not, we simply add it.
I have not tested this code on older Qt versions, but it uses OPs logic, so it should work.
Here's my code. As in OPs case, all code is in the .cpp file of a QApplication subclass.
#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>
void setupDockClickHandler();
bool dockClickHandler(id self,SEL _cmd,...);
#endif
My QApplication subclass constructor contains
#ifdef Q_OS_MAC
setupDockClickHandler();
#endif
And finally, somewhere in the same file (at the bottom, in my case):
#ifdef Q_OS_MAC
void setupDockClickHandler() {
Class cls = objc_getClass("NSApplication");
objc_object *appInst = objc_msgSend((objc_object*)cls, sel_registerName("sharedApplication"));
if(appInst != NULL) {
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
if (class_getInstanceMethod(delClass, shouldHandle)) {
if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B#:"))
qDebug() << "Registered dock click handler (replaced original method)";
else
qWarning() << "Failed to replace method for dock click handler";
}
else {
if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B#:"))
qDebug() << "Registered dock click handler";
else
qWarning() << "Failed to register dock click handler";
}
}
}
bool dockClickHandler(id self,SEL _cmd,...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)
// Do something fun here!
qDebug() << "Dock icon clicked!";
// Return NO (false) to suppress the default OS X actions
return false;
}
#endif
Also see the Apple documentation on the applicationShouldHandleReopen:hasVisibleWindows: method.
In order for this to compile, you also need to link with some extra frameworks.
Using qmake, I added the following to my .pro file:
LIBS += -framework CoreFoundation -framework Carbon -lobjc
Those flags are of course exactly what you should add to the c++ or clang++ command line, if you compile manually.
That should be everything that's required.
It's crazy, but i got it, and without any Objective-C coding:
I derived QApplication. In the *.cpp portion of my derived class i put:
#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>
bool dockClickHandler(id self,SEL _cmd,...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
((MyApplictionClass*)qApp)->onClickOnDock();
return true;
}
#endif
in my derived application class constructor I put:
#ifdef Q_OS_MAC
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls,sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass, sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"), (IMP)dockClickHandler,"B#:");
if (!test)
{
// failed to register handler...
}
}
#endif
Added this simple method to my application class (note it's referred to from the handler at the top of my answer)
void MyApplictionClass::onClickOnDock()
{
// do something...
}
Works like charm.
Starting from Qt5.4.0 you can handle QEvent, that related to click on dock: QEvent::ApplicationActivate.
https://bugreports.qt.io/browse/QTBUG-10899
https://doc.qt.io/qt-5/qevent.html
The problem with QEvent::ApplicationActivate is that it will be emitted for every activation - eg., even if you switch to the app on Application Switcher. The native behavior is to show the app only on Dock icon click, not when you are switching by cmd+tab.
But, there is a hack that works at least for Qt 5.9.1. The Dock icon click produces 2 sequential QEvent::ApplicationStateChangeEvent events, meanwhile cmd+tab - only one.
So, this code will emit Dock click signal quite accurately. App class is the application class inherited from QApplication and also an event filter for itself.
bool App::eventFilter(QObject* watched, QEvent* event)
{
#ifdef Q_OS_MACOS
if (watched == this && event->type() == QEvent::ApplicationStateChange) {
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
if (_prevAppState == Qt::ApplicationActive
&& ev->applicationState() == Qt::ApplicationActive) {
emit clickedOnDock();
}
_prevAppState = ev->applicationState();
}
#endif // Q_OS_MACOS
return QApplication::eventFilter(watched, event);
}

Linker error: Qt and libusb-win32

I try to develop a Qt GUI application which will communicate with a board using USB. The library I use is libusb-win32 v1.2.5.0.
When I compile the application, the following errors occur:
./debug/thread_usb_comm.o: In function `ZN15thread_usb_comm15find_usb_deviceEtt':
thread_usb_comm.cpp:15: undefined reference to 'usb_find_busses'
thread_usb_comm.cpp:16: undefined reference to 'usb_find_devices'
thread_usb_comm.cpp:18: undefined reference to 'usb_get_busses'
thread_usb_comm.cpp:26: undefined reference to 'usb_open'
collect2: ld returned 1 exit status
mingw32-make[1]: [debug/CALSYS11_calib_app.exe] Error 1
mingw32-make: [debug] Error 2
The application code is:
(header file)
#ifndef THREAD_USB_COMM_H
#define THREAD_USB_COMM_H
#include <QThread>
#include <QtDebug>
#include "CALSYS11.h"
#include <lusb0_usb.h>
class thread_usb_comm : public QThread
{
Q_OBJECT
public:
thread_usb_comm();
private:
bool device_connected;
usb_dev_handle *p_usb_device;
bool find_usb_device(
unsigned short vendor_id,
unsigned short product_id
);
};
#endif // THREAD_USB_COMM_H
(source file)
#include "thread_usb_comm.h"
thread_usb_comm::thread_usb_comm()
{
device_connected = false;
}
bool thread_usb_comm::find_usb_device(
unsigned short vendor_id,
unsigned short product_id
)
{
struct usb_bus *bus;
struct usb_device *dev;
usb_find_busses();
usb_find_devices();
for (bus = usb_get_busses(); bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
if ((dev->descriptor.idVendor == vendor_id) &&
(dev->descriptor.idProduct == product_id))
{
qDebug ("Device found");
p_usb_device = usb_open(dev);
if (0 == p_usb_device)
{
qCritical ("Could not open USB device");
return false;
}
device_connected = true;
return true;
}
}
}
qDebug ("Cannot find specified device");
return false;
}
I added the link to the libusb library in the .pro file:
LIBS += -L\path\to\libusb-win32\lib\gcc -lusb
I develop on Windows 7.
Thank you,
Johann
Try to build release instead of debug or the static library is incompatible with compiler version (I faced same issue when I tried to build old static library with MinGW 4.4)

How to use a QFile with std::iostream?

Is it possible to use a QFile like a std::iostream? I'm quite sure there must be a wrapper out there. The question is where?
I have another libs, which requires a std::istream as input parameter, but in my program i only have a QFile at this point.
I came up with my own solution using the following code:
#include <ios>
#include <QIODevice>
class QStdStreamBuf : public std::streambuf
{
public:
QStdStreamBuf(QIODevice *dev) : std::streambuf(), m_dev(dev)
{
// Initialize get pointer. This should be zero so that underflow is called upon first read.
this->setg(0, 0, 0);
}
protected:
virtual std::streamsize xsgetn(std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->read(str, n);
}
virtual std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
return m_dev->write(str, n);
}
virtual std::streambuf::pos_type seekoff(std::streambuf::off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*__mode*/)
{
switch(dir)
{
case std::ios_base::beg:
break;
case std::ios_base::end:
off = m_dev->size() - off;
break;
case std::ios_base::cur:
off = m_dev->pos() + off;
break;
}
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::pos_type seekpos(std::streambuf::pos_type off, std::ios_base::openmode /*__mode*/)
{
if(m_dev->seek(off))
return m_dev->pos();
else
return std::streambuf::pos_type(std::streambuf::off_type(-1));
}
virtual std::streambuf::int_type underflow()
{
// Read enough bytes to fill the buffer.
std::streamsize len = sgetn(m_inbuf, sizeof(m_inbuf)/sizeof(m_inbuf[0]));
// Since the input buffer content is now valid (or is new)
// the get pointer should be initialized (or reset).
setg(m_inbuf, m_inbuf, m_inbuf + len);
// If nothing was read, then the end is here.
if(len == 0)
return traits_type::eof();
// Return the first character.
return traits_type::not_eof(m_inbuf[0]);
}
private:
static const std::streamsize BUFFER_SIZE = 1024;
std::streambuf::char_type m_inbuf[BUFFER_SIZE];
QIODevice *m_dev;
};
class QStdIStream : public std::istream
{
public:
QStdIStream(QIODevice *dev) : std::istream(m_buf = new QStdStreamBuf(dev)) {}
virtual ~QStdIStream()
{
rdbuf(0);
delete m_buf;
}
private:
QStdStreamBuf * m_buf;
};
I works fine for reading local files. I haven't tested it for writing files. This code is surely not perfect but it works.
I came up with my own solution (which uses the same idea Stephen Chu suggested)
#include <iostream>
#include <fstream>
#include <cstdio>
#include <QtCore>
using namespace std;
void externalLibFunction(istream & input_stream) {
copy(istream_iterator<string>(input_stream),
istream_iterator<string>(),
ostream_iterator<string>(cout, " "));
}
ifstream QFileToifstream(QFile & file) {
Q_ASSERT(file.isReadable());
return ifstream(::_fdopen(file.handle(), "r"));
}
int main(int argc, char ** argv)
{
QFile file("a file");
file.open(QIODevice::WriteOnly);
file.write(QString("some string").toLatin1());
file.close();
file.open(QIODevice::ReadOnly);
std::ifstream ifs(QFileToifstream(file));
externalLibFunction(ifs);
}
Output:
some string
This code uses std::ifstream move constructor (C++x0 feature) specified in 27.9.1.7 basic_ifstream constructors section of Working Draft, Standard for Programming Language C++:
basic_ifstream(basic_ifstream&& rhs);
Effects: Move constructs from the
rvalue rhs. This is accomplished by
move constructing the base class, and
the contained basic_filebuf. Next
basic_istream::set_rdbuf(&sb) is called to install the contained
basic_filebuf.
See How to return an fstream (C++0x) for discussion on this subject.
If the QFile object you get is not open for read already, you can get filename from it and open an ifstream object.
If it's already open, you can get file handle/descriptor with handle() and go from there. There's no portable way of getting a fstream from platform handle. You will have to find a workaround for your platforms.
Here's a good guide for subclassing std::streambuf to provide a non-seekable read-only std::istream: https://stackoverflow.com/a/14086442/316578
Here is a simple class based on that approach which adapts a QFile into an std::streambuf which can then be wrapped in an std::istream.
#include <iostream>
#include <QFile>
constexpr qint64 ERROR = -1;
constexpr qint64 BUFFER_SIZE = 1024;
class QFileInputStreamBuffer final : public std::streambuf {
private:
QFile &m_file;
QByteArray m_buffer;
public:
explicit QFileInputStreamBuffer(QFile &file)
: m_file(file),
m_buffer(BUFFER_SIZE, Qt::Uninitialized) {
}
virtual int underflow() override {
if (atEndOfBuffer()) {
// try to get more data
const qint64 bytesReadIntoBuffer = m_file.read(m_buffer.data(), BUFFER_SIZE);
if (bytesReadIntoBuffer != ERROR) {
setg(m_buffer.data(), m_buffer.data(), m_buffer.data() + bytesReadIntoBuffer);
}
}
if (atEndOfBuffer()) {
// no more data available
return std::char_traits<char>::eof();
}
else {
return std::char_traits<char>::to_int_type(*gptr());
}
}
private:
bool atEndOfBuffer() const {
return gptr() == egptr();
}
};
If you want to be able to more things like seek, write, etc., then you'd need one of the other more complex solutions here which override more streambuf functions.
If you don't care much for performance you can always read everything from the file and dump it into an std::stringstream and then pass that to your library. (or the otherway, buffer everything to a stringstream and then write to a QFile)
Other than that, it doesn't look like the two can inter-operate. At any rate, Qt to STL inter operations are often a cause for obscure bugs and subtle inconsistencies if the version of STL that Qt was compiled with is different in any way from the version of STL you are using. This can happen for instance if you change the version of Visual Studio.

Resources