added special VOIP buttons to chat dialog through plugin

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@4989 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2012-02-25 12:18:40 +00:00
parent 96fd780502
commit 12b3ed02f1
8 changed files with 343 additions and 48 deletions

View File

@ -4,6 +4,8 @@ CONFIG += qt uic qrc resources
CONFIG += mobility
MOBILITY = multimedia
INCLUDEPATH += ../../retroshare-gui/src/temp/ui
QMAKE_CXXFLAGS *= -Wall
SOURCES = services/p3vors.cc \
@ -14,6 +16,7 @@ SOURCES = services/p3vors.cc \
gui/SpeexProcessor.cpp \
gui/audiodevicehelper.cpp \
gui/VoipStatistics.cpp \
gui/AudioPopupChatDialog.cpp \
VOIPPlugin.cpp
HEADERS = services/p3vors.h \
@ -24,6 +27,7 @@ HEADERS = services/p3vors.h \
gui/SpeexProcessor.h \
gui/audiodevicehelper.h \
gui/VoipStatistics.h \
gui/AudioPopupChatDialog.h \
interface/rsvoip.h
FORMS = gui/AudioInputConfig.ui \

View File

@ -9,6 +9,7 @@
#include "gui/VoipStatistics.h"
#include "gui/AudioInputConfig.h"
#include "gui/AudioPopupChatDialog.h"
static void *inited = new VOIPPlugin() ;
@ -46,6 +47,11 @@ ConfigPage *VOIPPlugin::qt_config_page() const
return new AudioInputConfig() ;
}
PopupChatDialog *VOIPPlugin::qt_allocate_new_popup_chat_dialog() const
{
return new AudioPopupChatDialog() ;
}
std::string VOIPPlugin::qt_transfers_tab_name() const
{
return QObject::tr("RTT Statistics").toStdString() ;

View File

@ -14,6 +14,7 @@ class VOIPPlugin: public RsPlugin
virtual ConfigPage *qt_config_page() const ;
virtual RsAutoUpdatePage *qt_transfers_tab() const ;
virtual std::string qt_transfers_tab_name() const ;
virtual PopupChatDialog *qt_allocate_new_popup_chat_dialog() const ;
virtual QTranslator *qt_translator(QApplication *app, const QString& languageCode) const;

View File

@ -0,0 +1,175 @@
#include <QPushButton>
#include <QPropertyAnimation>
#include <QIcon>
#include "AudioPopupChatDialog.h"
AudioPopupChatDialog::AudioPopupChatDialog(QWidget *parent)
{
audioListenToggleButton = new QPushButton ;
audioListenToggleButton->setMinimumSize(QSize(28,28)) ;
audioListenToggleButton->setMaximumSize(QSize(28,28)) ;
audioListenToggleButton->setText(QString()) ;
std::cerr << "****** VOIPLugin: Creating new AudioPopupChatDialog !!" << std::endl;
QIcon icon ;
icon.addPixmap(QPixmap(":/images/deafened_self.svg"),QIcon::Normal,QIcon::Off) ;
icon.addPixmap(QPixmap(":/images/self_undeafened.svg"),QIcon::Normal,QIcon::On) ;
icon.addPixmap(QPixmap(":/images/self_undeafened.svg"),QIcon::Disabled,QIcon::On) ;
icon.addPixmap(QPixmap(":/images/self_undeafened.svg"),QIcon::Active,QIcon::On) ;
icon.addPixmap(QPixmap(":/images/self_undeafened.svg"),QIcon::Selected,QIcon::On) ;
icon.addPixmap(QPixmap(":/images/deafened_self.svg")) ;
audioListenToggleButton->setIcon(icon) ;
audioListenToggleButton->setIconSize(QSize(22,22)) ;
audioListenToggleButton->setFlat(true) ;
audioMuteCaptureToggleButton = new QPushButton ;
audioMuteCaptureToggleButton->setMinimumSize(QSize(28,28)) ;
audioMuteCaptureToggleButton->setMaximumSize(QSize(28,28)) ;
audioMuteCaptureToggleButton->setText(QString()) ;
QIcon icon2 ;
icon2.addPixmap(QPixmap(":/images/muted_self.svg"),QIcon::Normal,QIcon::Off) ;
icon2.addPixmap(QPixmap(":/images/talking_off.svg"),QIcon::Normal,QIcon::On) ;
icon2.addPixmap(QPixmap(":/images/talking_off.svg"),QIcon::Disabled,QIcon::On) ;
icon2.addPixmap(QPixmap(":/images/talking_off.svg"),QIcon::Active,QIcon::On) ;
icon2.addPixmap(QPixmap(":/images/talking_off.svg"),QIcon::Selected,QIcon::On) ;
icon2.addPixmap(QPixmap(":/images/muted_self.svg")) ;
audioMuteCaptureToggleButton->setIcon(icon2) ;
audioMuteCaptureToggleButton->setIconSize(QSize(22,22)) ;
audioMuteCaptureToggleButton->setFlat(true) ;
connect(audioListenToggleButton, SIGNAL(clicked()), this , SLOT(toggleAudioListen()));
connect(audioMuteCaptureToggleButton, SIGNAL(clicked()), this , SLOT(toggleAudioMuteCapture()));
addButton(audioListenToggleButton) ;
addButton(audioMuteCaptureToggleButton) ;
//ui.chatWidget->resetStatusBar();
outputProcessor = NULL ;
outputDevice = NULL ;
inputProcessor = NULL ;
inputDevice = NULL ;
}
void AudioPopupChatDialog::toggleAudioListen()
{
std::cerr << "******** VOIPLugin: Toggling audio listen!" << std::endl;
if (audioListenToggleButton->isChecked()) {
} else {
audioListenToggleButton->setChecked(false);
/*if (outputDevice) {
outputDevice->stop();
}*/
}
}
void AudioPopupChatDialog::toggleAudioMuteCapture()
{
std::cerr << "******** VOIPLugin: Toggling audio mute capture!" << std::endl;
if (audioMuteCaptureToggleButton->isChecked()) {
//activate audio output
audioListenToggleButton->setChecked(true);
//activate audio input
if (!inputProcessor) {
inputProcessor = new QtSpeex::SpeexInputProcessor();
if (outputProcessor) {
connect(outputProcessor, SIGNAL(playingFrame(QByteArray*)), inputProcessor, SLOT(addEchoFrame(QByteArray*)));
}
inputProcessor->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
}
if (!inputDevice) {
inputDevice = AudioDeviceHelper::getPreferedInputDevice();
}
connect(inputProcessor, SIGNAL(networkPacketReady()), this, SLOT(sendAudioData()));
inputDevice->start(inputProcessor);
} else {
disconnect(inputProcessor, SIGNAL(networkPacketReady()), this, SLOT(sendAudioData()));
if (inputDevice) {
inputDevice->stop();
}
}
}
void AudioPopupChatDialog::addAudioData(const QString name, QByteArray* array)
{
if (!audioListenToggleButton->isChecked()) {
//launch an animation. Don't launch it if already animating
if (!audioListenToggleButton->graphicsEffect() ||
(audioListenToggleButton->graphicsEffect()->inherits("QGraphicsOpacityEffect") &&
((QGraphicsOpacityEffect*)audioListenToggleButton->graphicsEffect())->opacity() == 1)
) {
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(audioListenToggleButton);
audioListenToggleButton->setGraphicsEffect(effect);
QPropertyAnimation *anim = new QPropertyAnimation(effect, "opacity");
anim->setStartValue(1);
anim->setKeyValueAt(0.5,0);
anim->setEndValue(1);
anim->setDuration(400);
anim->start();
}
//TODO make a toaster and a sound for the incoming call
return;
}
if (!outputDevice) {
outputDevice = AudioDeviceHelper::getDefaultOutputDevice();
}
if (!outputProcessor) {
//start output audio device
outputProcessor = new QtSpeex::SpeexOutputProcessor();
if (inputProcessor) {
connect(outputProcessor, SIGNAL(playingFrame(QByteArray*)), inputProcessor, SLOT(addEchoFrame(QByteArray*)));
}
outputProcessor->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
outputDevice->start(outputProcessor);
}
if (outputDevice && outputDevice->error() != QAudio::NoError) {
std::cerr << "Restarting output device. Error before reset " << outputDevice->error() << " buffer size : " << outputDevice->bufferSize() << std::endl;
outputDevice->stop();
outputDevice->reset();
if (outputDevice->error() == QAudio::UnderrunError)
outputDevice->setBufferSize(20);
outputDevice->start(outputProcessor);
}
outputProcessor->putNetworkPacket(name, *array);
//check the input device for errors
if (inputDevice && inputDevice->error() != QAudio::NoError) {
std::cerr << "Restarting input device. Error before reset " << inputDevice->error() << std::endl;
inputDevice->stop();
inputDevice->reset();
inputDevice->start(inputProcessor);
}
}
void AudioPopupChatDialog::sendAudioData() {
while(inputProcessor && inputProcessor->hasPendingPackets()) {
QByteArray qbarray = inputProcessor->getNetworkPacket();
if (qbarray != NULL) {
std::wstring s2 ( L"" );
char * buff = new char[qbarray.size()];
memcpy(buff,qbarray.constData(),qbarray.size()) ;
#ifdef VOIP_SUSPENDED
rsMsgs->sendPrivateChat(peerId, s2,buff, qbarray.size());
#endif
}
}
}
void AudioPopupChatDialog::updateStatus(const QString& peer_id,int status)
{
audioListenToggleButton->setEnabled(true);
audioMuteCaptureToggleButton->setEnabled(true);
updateStatus(peer_id,status) ;
}

View File

@ -0,0 +1,42 @@
#include <QObject>
#include <QGraphicsEffect>
#include <gui/SpeexProcessor.h>
#include <gui/chat/PopupChatDialog.h>
#include <gui/audiodevicehelper.h>
class QPushButton;
class AudioPopupChatDialog: public PopupChatDialog
{
Q_OBJECT
public:
AudioPopupChatDialog(QWidget *parent = NULL);
virtual ~AudioPopupChatDialog()
{
if(inputDevice != NULL)
inputDevice->stop() ;
}
private slots:
void toggleAudioListen();
void toggleAudioMuteCapture();
public slots:
void sendAudioData();
protected:
QAudioInput* inputDevice;
QAudioOutput* outputDevice;
QtSpeex::SpeexInputProcessor* inputProcessor;
QtSpeex::SpeexOutputProcessor* outputProcessor;
virtual void updateStatus(const QString& peer_id,int status) ;
void addAudioData(const QString name, QByteArray* array) ;
QPushButton *audioListenToggleButton ;
QPushButton *audioMuteCaptureToggleButton ;
};

View File

@ -25,14 +25,27 @@ class RsVoipPongResult
double mOffset;
};
struct RsVoipDataChunk
{
void *data ; // create/delete using malloc/free.
uint32_t size ;
};
class RsVoip
{
public:
virtual int sendVoipData(const std::string& peer_id,const void *data,uint32_t size) = 0;
virtual int sendVoipHangUpCall(const std::string& peer_id) = 0;
virtual int sendVoipRinging(const std::string& peer_id) = 0;
virtual int sendVoipAcceptCall(const std::string& peer_id) = 0;
// Sending data. The client keeps the memory ownership and must delete it after calling this.
virtual int sendVoipData(const std::string& peer_id,const RsVoipDataChunk& chunk) = 0;
// The server fill in the data and gives up memory ownership. The client must delete the memory
// in each chunk once it has been used.
//
virtual bool getIncomingData(const std::string& peer_id,std::vector<RsVoipDataChunk>& chunks) = 0;
typedef enum { AudioTransmitContinous = 0, AudioTransmitVAD = 1, AudioTransmitPushToTalk = 2 } enumAudioTransmit ;
// Config stuff

View File

@ -222,9 +222,9 @@ int p3VoRS::sendVoipRinging(const std::string& peer_id)
return true ;
}
int p3VoRS::sendVoipData(const std::string& peer_id,const void *data,uint32_t size)
int p3VoRS::sendVoipData(const std::string& peer_id,const RsVoipDataChunk& chunk)
{
std::cerr << "Sending " << size << " bytes of voip data." << std::endl;
std::cerr << "Sending " << chunk.size << " bytes of voip data." << std::endl;
RsVoipDataItem *item = new RsVoipDataItem ;
@ -233,14 +233,14 @@ int p3VoRS::sendVoipData(const std::string& peer_id,const void *data,uint32_t si
std::cerr << "Cannot allocate RsVoipDataItem !" << std::endl;
return false ;
}
item->voip_data = malloc(size) ;
item->voip_data = malloc(chunk.size) ;
if(item->voip_data == NULL)
{
std::cerr << "Cannot allocate RsVoipDataItem.voip_data of size " << size << " !" << std::endl;
std::cerr << "Cannot allocate RsVoipDataItem.voip_data of size " << chunk.size << " !" << std::endl;
return false ;
}
memcpy(item->voip_data,data,size) ;
memcpy(item->voip_data,chunk.data,chunk.size) ;
item->flags = 0 ;
item->PeerId(peer_id) ;
@ -251,8 +251,6 @@ int p3VoRS::sendVoipData(const std::string& peer_id,const void *data,uint32_t si
void p3VoRS::sendPingMeasurements()
{
/* we ping our peers */
/* who is online? */
std::list<std::string> idList;
@ -297,7 +295,65 @@ void p3VoRS::sendPingMeasurements()
}
void p3VoRS::handleProtocol(RsVoipProtocolItem *item)
{
// should we keep a list of received requests?
// we notify the notifier that something occurred.
// mNotify->notifyPluginAction(mPluginId, PluginSignalId, (void *)data_to_send_upward);
}
void p3VoRS::handleData(RsVoipDataItem *item)
{
RsStackMutex stack(mVorsMtx); /****** LOCKED MUTEX *******/
// store the data in a queue.
std::map<std::string,VorsPeerInfo>::iterator it = mPeerInfo.find(item->PeerId()) ;
if(it == mPeerInfo.end())
{
std::cerr << "Peer unknown to VOIP process. Dropping data" << std::endl;
delete item ;
}
else
{
it->second.incoming_queue.push_back(item) ; // be careful with the delete action!
// notify->notifyPluginAction(mPluginId, PluginSignalId, (void *)data_to_send_upward);
}
}
bool p3VoRS::getIncomingData(const std::string& peer_id,std::vector<RsVoipDataChunk>& incoming_data_chunks)
{
RsStackMutex stack(mVorsMtx); /****** LOCKED MUTEX *******/
incoming_data_chunks.clear() ;
std::map<std::string,VorsPeerInfo>::iterator it = mPeerInfo.find(peer_id) ;
if(it == mPeerInfo.end())
{
std::cerr << "Peer unknown to VOIP process. No data returned. Probably a bug !" << std::endl;
return false ;
}
for(std::list<RsVoipDataItem*>::const_iterator it2(it->second.incoming_queue.begin());it2!=it->second.incoming_queue.end();++it2)
{
RsVoipDataChunk chunk ;
chunk.size = (*it2)->data_size ;
chunk.data = malloc((*it2)->data_size) ;
memcpy(chunk.data,(*it2)->voip_data,(*it2)->data_size) ;
incoming_data_chunks.push_back(chunk) ;
delete *it2 ;
}
it->second.incoming_queue.clear() ;
return true ;
}
int p3VoRS::processIncoming()
{
@ -305,53 +361,44 @@ int p3VoRS::processIncoming()
RsItem *item = NULL;
while(NULL != (item = recvItem()))
{
bool keep = false ;
switch(item->PacketSubType())
{
default:
break;
case RS_PKT_SUBTYPE_VOIP_PING:
{
handlePing(item);
}
break;
case RS_PKT_SUBTYPE_VOIP_PONG:
{
handlePong(item);
}
break;
case RS_PKT_SUBTYPE_VOIP_PING: handlePing(dynamic_cast<RsVoipPingItem*>(item));
break;
case RS_PKT_SUBTYPE_VOIP_PONG: handlePong(dynamic_cast<RsVoipPongItem*>(item));
break;
case RS_PKT_SUBTYPE_VOIP_PROTOCOL: handleProtocol(dynamic_cast<RsVoipProtocolItem*>(item)) ;
break ;
case RS_PKT_SUBTYPE_VOIP_DATA: handleData(dynamic_cast<RsVoipDataItem*>(item));
keep = true ;
break;
#if 0
/* THESE ARE ALL FUTURISTIC DATA TYPES */
case RS_DATA_ITEM:
{
handleData(item);
}
break;
/* THESE ARE ALL FUTURISTIC DATA TYPES */
case RS_BANDWIDTH_PING_ITEM: handleBandwidthPing(item);
break;
case RS_BANDWIDTH_PING_ITEM:
{
handleBandwidthPing(item);
}
break;
case RS_BANDWIDTH_PONG_ITEM:
{
handleBandwidthPong(item);
}
break;
case RS_BANDWIDTH_PONG_ITEM: handleBandwidthPong(item);
break;
#endif
default:
break;
}
/* clean up */
delete item;
if(!keep)
delete item;
}
return true ;
}
int p3VoRS::handlePing(RsItem *item)
int p3VoRS::handlePing(RsVoipPingItem *ping)
{
/* cast to right type */
RsVoipPingItem *ping = (RsVoipPingItem *) item;
#ifdef DEBUG_VORS
std::cerr << "p3VoRS::handlePing() Recvd Packet from: " << ping->PeerId();
@ -382,10 +429,9 @@ int p3VoRS::handlePing(RsItem *item)
}
int p3VoRS::handlePong(RsItem *item)
int p3VoRS::handlePong(RsVoipPongItem *pong)
{
/* cast to right type */
RsVoipPongItem *pong = (RsVoipPongItem *) item;
#ifdef DEBUG_VORS
std::cerr << "p3VoRS::handlePong() Recvd Packet from: " << pong->PeerId();
@ -420,9 +466,6 @@ int p3VoRS::handlePong(RsItem *item)
return true ;
}
int p3VoRS::storePingAttempt(std::string id, double ts, uint32_t seqno)
{
RsStackMutex stack(mVorsMtx); /****** LOCKED MUTEX *******/

View File

@ -52,6 +52,7 @@ class VorsPeerInfo
uint32_t mSentPings;
std::list<RsVoipPongResult> mPongResults;
std::list<RsVoipDataItem*> incoming_queue ;
};
@ -74,7 +75,15 @@ class p3VoRS: public RsPQIService, public RsVoip
// Call stuff.
//
virtual int sendVoipData(const std::string& peer_id,const void *data,uint32_t size) ;
// Sending data. The client keeps the memory ownership and must delete it after calling this.
virtual int sendVoipData(const std::string& peer_id,const RsVoipDataChunk& chunk) ;
// The server fill in the data and gives up memory ownership. The client must delete the memory
// in each chunk once it has been used.
//
virtual bool getIncomingData(const std::string& peer_id,std::vector<RsVoipDataChunk>& chunks) ;
virtual int sendVoipHangUpCall(const std::string& peer_id) ;
virtual int sendVoipRinging(const std::string& peer_id) ;
virtual int sendVoipAcceptCall(const std::string& peer_id) ;
@ -122,12 +131,14 @@ class p3VoRS: public RsPQIService, public RsVoip
void sendPingMeasurements();
int processIncoming();
int handlePing(RsItem *item);
int handlePong(RsItem *item);
int handlePing(RsVoipPingItem *item);
int handlePong(RsVoipPongItem *item);
int storePingAttempt(std::string id, double ts, uint32_t mCounter);
int storePongResult(std::string id, uint32_t counter, double ts, double rtt, double offset);
void handleProtocol(RsVoipProtocolItem*) ;
void handleData(RsVoipDataItem*) ;
RsMutex mVorsMtx;