This is a simple VoIP application that takes audio from a microphone and video from the video test source, encodes them with speex and h264 respectively and sends them to the other side using RTP.
The same application is both a client and a server. To make a call between them, you need to configure the destination ports of the one side to be the source ports of the other and vice versa.
#include "ui_voip.h"
#include <QApplication>
#include <QDialog>
#include <QGlib/Connect>
#include <QGlib/Error>
#include <QGst/Init>
#include <QGst/Pipeline>
#include <QGst/ElementFactory>
#include <QGst/Pad>
#include <QGst/Structure>
#include <QGst/Bus>
#include <QGst/Message>
class VoipExample : public QDialog
{
Q_OBJECT
public:
explicit VoipExample(QWidget *parent = 0);
virtual ~VoipExample();
private Q_SLOTS:
void on_startCallButton_clicked();
void on_endCallButton_clicked();
void on_audioRtpSrcSpinBox_valueChanged(int value) { m_ui.audioRtcpSrcSpinBox->setValue(value + 1); }
void on_audioRtpDestSpinBox_valueChanged(int value) { m_ui.audioRtcpDestSpinBox->setValue(value + 1); }
void on_videoRtpSrcSpinBox_valueChanged(int value) { m_ui.videoRtcpSrcSpinBox->setValue(value + 1); }
void on_videoRtpDestSpinBox_valueChanged(int value) { m_ui.videoRtcpDestSpinBox->setValue(value + 1); }
private:
private:
Ui::VoipExample m_ui;
};
VoipExample::VoipExample(QWidget *parent)
: QDialog(parent)
{
m_ui.setupUi(this);
}
VoipExample::~VoipExample()
{
if (m_pipeline) {
on_endCallButton_clicked();
}
}
void VoipExample::on_startCallButton_clicked()
{
m_pipeline = QGst::Pipeline::create();
if (!rtpbin) {
qFatal("Failed to create rtpbin");
}
m_pipeline->add(rtpbin);
if (m_ui.audioGroupBox->isChecked()) {
try {
"autoaudiosrc ! queue ! audioconvert ! audiorate ! audio/x-raw,rate=8000 "
"! speexenc ! rtpspeexpay"
);
qCritical() << error;
qFatal("One ore more required elements are missing. Aborting...");
}
m_pipeline->add(audiosrc);
audiosrc->link(rtpbin, "send_rtp_sink_1");
if (!rtpudpsink) {
qFatal("Failed to create udpsink. Aborting...");
}
rtpudpsink->setProperty("host", m_ui.remoteHostLineEdit->text());
rtpudpsink->setProperty("port", m_ui.audioRtpDestSpinBox->value());
m_pipeline->add(rtpudpsink);
rtpbin->link("send_rtp_src_1", rtpudpsink);
if (!rtpudpsrc) {
qFatal("Failed to create udpsrc. Aborting...");
}
rtpudpsrc->setProperty("port", m_ui.audioRtpSrcSpinBox->value());
rtpudpsrc->setProperty("caps", QGst::Caps::fromString("application/x-rtp,"
"media=(string)audio,"
"clock-rate=(int)8000,"
"encoding-name=(string)SPEEX"));
m_pipeline->add(rtpudpsrc);
rtpudpsrc->link(rtpbin, "recv_rtp_sink_1");
rtcpudpsrc->setProperty("port", m_ui.audioRtcpSrcSpinBox->value());
m_pipeline->add(rtcpudpsrc);
rtcpudpsrc->link(rtpbin, "recv_rtcp_sink_1");
rtcpudpsink->setProperty("host", m_ui.remoteHostLineEdit->text());
rtcpudpsink->setProperty("port", m_ui.audioRtcpDestSpinBox->value());
rtcpudpsink->setProperty("sync", false);
rtcpudpsink->setProperty("async", false);
m_pipeline->add(rtcpudpsink);
rtpbin->link("send_rtcp_src_1", rtcpudpsink);
}
if (m_ui.videoGroupBox->isChecked()) {
try {
"videotestsrc is-live=true ! video/x-raw,width=320,height=240,framerate=15/1 "
"! x264enc tune=zerolatency byte-stream=true bitrate=300 ! rtph264pay"
);
qCritical() << error;
qFatal("One ore more required elements are missing. Aborting...");
}
m_pipeline->add(videosrc);
videosrc->link(rtpbin, "send_rtp_sink_2");
if (!rtpudpsink) {
qFatal("Failed to create udpsink. Aborting...");
}
rtpudpsink->setProperty("host", m_ui.remoteHostLineEdit->text());
rtpudpsink->setProperty("port", m_ui.videoRtpDestSpinBox->value());
m_pipeline->add(rtpudpsink);
rtpbin->link("send_rtp_src_2", rtpudpsink);
if (!rtpudpsrc) {
qFatal("Failed to create udpsrc. Aborting...");
}
rtpudpsrc->setProperty("port", m_ui.videoRtpSrcSpinBox->value());
rtpudpsrc->setProperty("caps", QGst::Caps::fromString("application/x-rtp,"
"media=(string)video,"
"clock-rate=(int)90000,"
"encoding-name=(string)H264"));
m_pipeline->add(rtpudpsrc);
rtpudpsrc->link(rtpbin, "recv_rtp_sink_2");
rtcpudpsrc->setProperty("port", m_ui.videoRtcpSrcSpinBox->value());
m_pipeline->add(rtcpudpsrc);
rtcpudpsrc->link(rtpbin, "recv_rtcp_sink_2");
rtcpudpsink->setProperty("host", m_ui.remoteHostLineEdit->text());
rtcpudpsink->setProperty("port", m_ui.videoRtcpDestSpinBox->value());
rtcpudpsink->setProperty("sync", false);
rtcpudpsink->setProperty("async", false);
m_pipeline->add(rtcpudpsink);
rtpbin->link("send_rtcp_src_2", rtcpudpsink);
}
QGlib::connect(rtpbin,
"pad-added",
this, &VoipExample::onRtpBinPadAdded);
m_pipeline->bus()->addSignalWatch();
QGlib::connect(m_pipeline->bus(),
"message::error",
this, &VoipExample::onBusErrorMessage);
m_ui.stackedWidget->setCurrentIndex(1);
m_ui.videoWidget->watchPipeline(m_pipeline);
m_pipeline->setState(QGst::StatePlaying);
}
void VoipExample::onRtpBinPadAdded(
const QGst::PadPtr & pad)
{
try {
if (pad->name().startsWith(QLatin1String("recv_rtp_src_1"))) {
"rtpspeexdepay ! speexdec ! audioconvert ! autoaudiosink"
);
} else {
"rtph264depay ! avdec_h264 ! videoconvert ! autovideosink"
);
}
qCritical() << error;
qFatal("One ore more required elements are missing. Aborting...");
}
m_pipeline->add(bin);
bin->syncStateWithParent();
pad->link(bin->getStaticPad("sink"));
}
{
on_endCallButton_clicked();
}
void VoipExample::on_endCallButton_clicked()
{
m_pipeline->setState(QGst::StateNull);
m_ui.videoWidget->stopPipelineWatch();
m_ui.stackedWidget->setCurrentIndex(0);
m_pipeline.clear();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
VoipExample gui;
gui.show();
return app.exec();
}
#include "main.moc"