mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-06 14:16:32 +02:00
After discussion in #qt, I looked at our threading code and it's all wrong/unsafe. This properly threads scanning. Also makes it idle priority and splits it into a separate scan manager
This commit is contained in:
@@ -42,6 +42,7 @@ SET( tomahawkSources ${tomahawkSources}
|
|||||||
scrobbler.cpp
|
scrobbler.cpp
|
||||||
shortcuthandler.cpp
|
shortcuthandler.cpp
|
||||||
|
|
||||||
|
scanmanager.cpp
|
||||||
tomahawkapp.cpp
|
tomahawkapp.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
@@ -81,6 +82,7 @@ SET( tomahawkHeaders ${tomahawkHeaders}
|
|||||||
musicscanner.h
|
musicscanner.h
|
||||||
scriptresolver.h
|
scriptresolver.h
|
||||||
scrobbler.h
|
scrobbler.h
|
||||||
|
scanmanager.h
|
||||||
shortcuthandler.h
|
shortcuthandler.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -8,14 +8,13 @@
|
|||||||
|
|
||||||
using namespace Tomahawk;
|
using namespace Tomahawk;
|
||||||
|
|
||||||
|
|
||||||
MusicScanner::MusicScanner( const QString& dir, quint32 bs )
|
MusicScanner::MusicScanner( const QString& dir, quint32 bs )
|
||||||
: QThread()
|
: QObject()
|
||||||
, m_dir( dir )
|
, m_dir( dir )
|
||||||
, m_batchsize( bs )
|
, m_batchsize( bs )
|
||||||
|
, m_dirLister( 0 )
|
||||||
|
, m_dirListerThreadController( 0 )
|
||||||
{
|
{
|
||||||
moveToThread( this );
|
|
||||||
|
|
||||||
m_ext2mime.insert( "mp3", "audio/mpeg" );
|
m_ext2mime.insert( "mp3", "audio/mpeg" );
|
||||||
|
|
||||||
#ifndef NO_OGG
|
#ifndef NO_OGG
|
||||||
@@ -31,15 +30,6 @@ MusicScanner::MusicScanner( const QString& dir, quint32 bs )
|
|||||||
// m_ext2mime.insert( "mp4", "audio/mp4" );
|
// m_ext2mime.insert( "mp4", "audio/mp4" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
MusicScanner::run()
|
|
||||||
{
|
|
||||||
QTimer::singleShot( 0, this, SLOT( startScan() ) );
|
|
||||||
exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MusicScanner::startScan()
|
MusicScanner::startScan()
|
||||||
{
|
{
|
||||||
@@ -74,18 +64,21 @@ MusicScanner::scan()
|
|||||||
connect( this, SIGNAL( batchReady( QVariantList ) ),
|
connect( this, SIGNAL( batchReady( QVariantList ) ),
|
||||||
SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection );
|
SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection );
|
||||||
|
|
||||||
DirLister* lister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes );
|
m_dirListerThreadController = new QThread( this );
|
||||||
|
m_dirLister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes );
|
||||||
|
m_dirLister->moveToThread( m_dirListerThreadController );
|
||||||
|
|
||||||
connect( lister, SIGNAL( fileToScan( QFileInfo ) ),
|
connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ),
|
||||||
SLOT( scanFile( QFileInfo ) ), Qt::QueuedConnection );
|
SLOT( scanFile( QFileInfo ) ), Qt::QueuedConnection );
|
||||||
|
|
||||||
// queued, so will only fire after all dirs have been scanned:
|
// queued, so will only fire after all dirs have been scanned:
|
||||||
connect( lister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ),
|
connect( m_dirLister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ),
|
||||||
SLOT( listerFinished( const QMap<QString, unsigned int>& ) ), Qt::QueuedConnection );
|
SLOT( listerFinished( const QMap<QString, unsigned int>& ) ), Qt::QueuedConnection );
|
||||||
|
|
||||||
connect( lister, SIGNAL( finished() ), lister, SLOT( deleteLater() ) );
|
connect( m_dirLister, SIGNAL( destroyed(QObject*) ), this, SLOT( listerDestroyed(QObject*) ) );
|
||||||
|
|
||||||
lister->start();
|
m_dirListerThreadController->start();
|
||||||
|
QMetaObject::invokeMethod( m_dirLister, "go" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,9 +104,20 @@ MusicScanner::listerFinished( const QMap<QString, unsigned int>& newmtimes )
|
|||||||
qDebug() << "Skipped the following files (no tags / no valid audio):";
|
qDebug() << "Skipped the following files (no tags / no valid audio):";
|
||||||
foreach( const QString& s, m_skippedFiles )
|
foreach( const QString& s, m_skippedFiles )
|
||||||
qDebug() << s;
|
qDebug() << s;
|
||||||
|
|
||||||
|
m_dirLister->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MusicScanner::listerDestroyed( QObject* dirLister )
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
m_dirLister = 0;
|
||||||
|
m_dirListerThreadController->deleteLater();
|
||||||
|
m_dirListerThreadController = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MusicScanner::commitBatch( const QVariantList& tracks )
|
MusicScanner::commitBatch( const QVariantList& tracks )
|
||||||
{
|
{
|
||||||
|
@@ -5,69 +5,26 @@
|
|||||||
#include <taglib/tag.h>
|
#include <taglib/tag.h>
|
||||||
|
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <QThread>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
class MusicScanner : public QThread
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
MusicScanner( const QString& dir, quint32 bs = 0 );
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void run();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
//void fileScanned( QVariantMap );
|
|
||||||
void finished( int, int );
|
|
||||||
void batchReady( const QVariantList& );
|
|
||||||
|
|
||||||
private:
|
|
||||||
QVariant readFile( const QFileInfo& fi );
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
|
|
||||||
void scanFile( const QFileInfo& fi );
|
|
||||||
void startScan();
|
|
||||||
void scan();
|
|
||||||
void setMtimes( const QMap<QString, unsigned int>& m );
|
|
||||||
void commitBatch( const QVariantList& );
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_dir;
|
|
||||||
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
|
|
||||||
unsigned int m_scanned;
|
|
||||||
unsigned int m_skipped;
|
|
||||||
|
|
||||||
QList<QString> m_skippedFiles;
|
|
||||||
|
|
||||||
QMap<QString, unsigned int> m_dirmtimes;
|
|
||||||
QMap<QString, unsigned int> m_newdirmtimes;
|
|
||||||
|
|
||||||
QList<QVariant> m_scannedfiles;
|
|
||||||
quint32 m_batchsize;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
// descend dir tree comparing dir mtimes to last known mtime
|
// descend dir tree comparing dir mtimes to last known mtime
|
||||||
// emit signal for any dir with new content, so we can scan it.
|
// emit signal for any dir with new content, so we can scan it.
|
||||||
// finally, emit the list of new mtimes we observed.
|
// finally, emit the list of new mtimes we observed.
|
||||||
class DirLister : public QThread
|
class DirLister : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DirLister( QDir d, QMap<QString, unsigned int>& mtimes )
|
DirLister( QDir d, QMap<QString, unsigned int>& mtimes )
|
||||||
: QThread(), m_dir( d ), m_dirmtimes( mtimes )
|
: QObject(), m_dir( d ), m_dirmtimes( mtimes )
|
||||||
{
|
{
|
||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
moveToThread(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~DirLister()
|
~DirLister()
|
||||||
@@ -75,13 +32,6 @@ public:
|
|||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
|
||||||
void run()
|
|
||||||
{
|
|
||||||
QTimer::singleShot(0,this,SLOT(go()));
|
|
||||||
exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileToScan( QFileInfo );
|
void fileToScan( QFileInfo );
|
||||||
void finished( const QMap<QString, unsigned int>& );
|
void finished( const QMap<QString, unsigned int>& );
|
||||||
@@ -130,4 +80,46 @@ private:
|
|||||||
QMap<QString, unsigned int> m_newdirmtimes;
|
QMap<QString, unsigned int> m_newdirmtimes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MusicScanner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MusicScanner( const QString& dir, quint32 bs = 0 );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
//void fileScanned( QVariantMap );
|
||||||
|
void finished( int, int );
|
||||||
|
void batchReady( const QVariantList& );
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant readFile( const QFileInfo& fi );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
|
||||||
|
void listerDestroyed( QObject* dirLister );
|
||||||
|
void scanFile( const QFileInfo& fi );
|
||||||
|
void startScan();
|
||||||
|
void scan();
|
||||||
|
void setMtimes( const QMap<QString, unsigned int>& m );
|
||||||
|
void commitBatch( const QVariantList& );
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_dir;
|
||||||
|
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
|
||||||
|
unsigned int m_scanned;
|
||||||
|
unsigned int m_skipped;
|
||||||
|
|
||||||
|
QList<QString> m_skippedFiles;
|
||||||
|
|
||||||
|
QMap<QString, unsigned int> m_dirmtimes;
|
||||||
|
QMap<QString, unsigned int> m_newdirmtimes;
|
||||||
|
|
||||||
|
QList<QVariant> m_scannedfiles;
|
||||||
|
quint32 m_batchsize;
|
||||||
|
|
||||||
|
DirLister* m_dirLister;
|
||||||
|
QThread* m_dirListerThreadController;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
58
src/scanmanager.cpp
Normal file
58
src/scanmanager.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include "scanmanager.h"
|
||||||
|
#include "musicscanner.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
ScanManager* ScanManager::s_instance = 0;
|
||||||
|
|
||||||
|
|
||||||
|
ScanManager*
|
||||||
|
ScanManager::instance()
|
||||||
|
{
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScanManager::ScanManager( QObject* parent )
|
||||||
|
: QObject( parent )
|
||||||
|
, m_scanner( 0 )
|
||||||
|
, m_musicScannerThreadController( 0 )
|
||||||
|
{
|
||||||
|
s_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScanManager::~ScanManager()
|
||||||
|
{
|
||||||
|
s_instance = 0;
|
||||||
|
m_musicScannerThreadController->deleteLater();
|
||||||
|
m_musicScannerThreadController = 0;
|
||||||
|
m_scanner->deleteLater();
|
||||||
|
m_scanner = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ScanManager::runManualScan( const QString &path )
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero
|
||||||
|
{
|
||||||
|
m_musicScannerThreadController = new QThread( this );
|
||||||
|
MusicScanner* m_scanner = new MusicScanner( path );
|
||||||
|
m_scanner->moveToThread( m_musicScannerThreadController );
|
||||||
|
connect( m_scanner, SIGNAL( finished() ), m_scanner, SLOT( deleteLater() ) );
|
||||||
|
connect( m_scanner, SIGNAL( destroyed(QObject*) ), this, SLOT( scanDestroyed(QObject*) ) );
|
||||||
|
m_musicScannerThreadController->start( QThread::IdlePriority );
|
||||||
|
QMetaObject::invokeMethod( m_scanner, "startScan" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ScanManager::scannerDestroyed( QObject* scanner )
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
m_scanner = 0;
|
||||||
|
m_musicScannerThreadController->deleteLater();
|
||||||
|
m_musicScannerThreadController = 0;
|
||||||
|
}
|
32
src/scanmanager.h
Normal file
32
src/scanmanager.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef SCANMANAGER_H
|
||||||
|
#define SCANMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "dllmacro.h"
|
||||||
|
|
||||||
|
class MusicScanner;
|
||||||
|
class QThread;
|
||||||
|
|
||||||
|
class DLLEXPORT ScanManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static ScanManager* instance();
|
||||||
|
|
||||||
|
explicit ScanManager( QObject* parent = 0 );
|
||||||
|
virtual ~ScanManager();
|
||||||
|
|
||||||
|
void runManualScan( const QString &path );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void scannerDestroyed( QObject *scanner );
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ScanManager* s_instance;
|
||||||
|
|
||||||
|
MusicScanner* m_scanner;
|
||||||
|
QThread* m_musicScannerThreadController;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@@ -22,6 +22,7 @@
|
|||||||
#include "sip/SipHandler.h"
|
#include "sip/SipHandler.h"
|
||||||
#include "sip/twitter/tomahawkoauthtwitter.h"
|
#include "sip/twitter/tomahawkoauthtwitter.h"
|
||||||
#include <database/database.h>
|
#include <database/database.h>
|
||||||
|
#include "scanmanager.h"
|
||||||
|
|
||||||
static QString
|
static QString
|
||||||
md5( const QByteArray& src )
|
md5( const QByteArray& src )
|
||||||
@@ -145,11 +146,7 @@ SettingsDialog::~SettingsDialog()
|
|||||||
s->setScriptResolvers( resolvers );
|
s->setScriptResolvers( resolvers );
|
||||||
|
|
||||||
if( rescan )
|
if( rescan )
|
||||||
{
|
ScanManager::instance()->runManualScan( s->scannerPath() );
|
||||||
MusicScanner* scanner = new MusicScanner(s->scannerPath() );
|
|
||||||
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
|
|
||||||
scanner->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if( rejabber )
|
if( rejabber )
|
||||||
{
|
{
|
||||||
@@ -180,21 +177,6 @@ SettingsDialog::showPathSelector()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
SettingsDialog::doScan()
|
|
||||||
{
|
|
||||||
// TODO this doesnt really belong here..
|
|
||||||
QString path = ui->lineEditMusicPath->text();
|
|
||||||
MusicScanner* scanner = new MusicScanner( path );
|
|
||||||
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
|
|
||||||
scanner->start();
|
|
||||||
|
|
||||||
QMessageBox::information( this, tr( "Scanning Started" ),
|
|
||||||
tr( "Scanning now, check console output. TODO." ),
|
|
||||||
QMessageBox::Ok );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SettingsDialog::onRejected()
|
SettingsDialog::onRejected()
|
||||||
{
|
{
|
||||||
|
@@ -48,7 +48,6 @@ protected:
|
|||||||
private slots:
|
private slots:
|
||||||
void onRejected();
|
void onRejected();
|
||||||
void showPathSelector();
|
void showPathSelector();
|
||||||
void doScan();
|
|
||||||
|
|
||||||
void toggleUpnp( bool preferStaticEnabled );
|
void toggleUpnp( bool preferStaticEnabled );
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
|
#include <QThread>
|
||||||
#include <utils/tomahawkutils.h>
|
#include <utils/tomahawkutils.h>
|
||||||
|
|
||||||
using namespace gloox;
|
using namespace gloox;
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include "scriptresolver.h"
|
#include "scriptresolver.h"
|
||||||
#include "sourcelist.h"
|
#include "sourcelist.h"
|
||||||
#include "shortcuthandler.h"
|
#include "shortcuthandler.h"
|
||||||
|
#include "scanmanager.h"
|
||||||
#include "tomahawksettings.h"
|
#include "tomahawksettings.h"
|
||||||
|
|
||||||
#include "audio/audioengine.h"
|
#include "audio/audioengine.h"
|
||||||
@@ -149,6 +150,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
|
|||||||
|
|
||||||
new TomahawkSettings( this );
|
new TomahawkSettings( this );
|
||||||
m_audioEngine = new AudioEngine;
|
m_audioEngine = new AudioEngine;
|
||||||
|
new ScanManager( this );
|
||||||
|
|
||||||
new Pipeline( this );
|
new Pipeline( this );
|
||||||
new SourceList( this );
|
new SourceList( this );
|
||||||
|
@@ -32,13 +32,13 @@
|
|||||||
#include "widgets/welcomewidget.h"
|
#include "widgets/welcomewidget.h"
|
||||||
|
|
||||||
#include "audiocontrols.h"
|
#include "audiocontrols.h"
|
||||||
#include "musicscanner.h"
|
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "tomahawksettings.h"
|
#include "tomahawksettings.h"
|
||||||
#include "sourcelist.h"
|
#include "sourcelist.h"
|
||||||
#include "transferview.h"
|
#include "transferview.h"
|
||||||
#include "tomahawktrayicon.h"
|
#include "tomahawktrayicon.h"
|
||||||
#include "playlist/dynamic/GeneratorInterface.h"
|
#include "playlist/dynamic/GeneratorInterface.h"
|
||||||
|
#include "scanmanager.h"
|
||||||
|
|
||||||
using namespace Tomahawk;
|
using namespace Tomahawk;
|
||||||
|
|
||||||
@@ -249,23 +249,9 @@ TomahawkWindow::rescanCollectionManually()
|
|||||||
s->scannerPath(), &ok );
|
s->scannerPath(), &ok );
|
||||||
s->setValue( "scannerpath", path );
|
s->setValue( "scannerpath", path );
|
||||||
if ( ok && !path.isEmpty() )
|
if ( ok && !path.isEmpty() )
|
||||||
{
|
ScanManager::instance()->runManualScan( path );
|
||||||
MusicScanner* scanner = new MusicScanner( path );
|
|
||||||
connect( scanner, SIGNAL( finished() ), this, SLOT( scanFinished() ) );
|
|
||||||
scanner->start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
TomahawkWindow::scanFinished()
|
|
||||||
{
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
|
||||||
MusicScanner* scanner = (MusicScanner*) sender();
|
|
||||||
scanner->deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TomahawkWindow::addPeerManually()
|
TomahawkWindow::addPeerManually()
|
||||||
{
|
{
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
class QAction;
|
class QAction;
|
||||||
|
|
||||||
|
class MusicScanner;
|
||||||
class AudioControls;
|
class AudioControls;
|
||||||
class TomahawkTrayIcon;
|
class TomahawkTrayIcon;
|
||||||
|
|
||||||
@@ -46,11 +47,9 @@ public slots:
|
|||||||
void createPlaylist();
|
void createPlaylist();
|
||||||
void loadSpiff();
|
void loadSpiff();
|
||||||
void showSettingsDialog();
|
void showSettingsDialog();
|
||||||
|
|
||||||
private slots:
|
|
||||||
void scanFinished();
|
|
||||||
void rescanCollectionManually();
|
void rescanCollectionManually();
|
||||||
|
|
||||||
|
private slots:
|
||||||
void onSipConnected();
|
void onSipConnected();
|
||||||
void onSipDisconnected();
|
void onSipDisconnected();
|
||||||
void onSipError();
|
void onSipError();
|
||||||
|
Reference in New Issue
Block a user