这是一个通过Qt实现的语音对话程序,我将通过几个步骤带你程序实现逻辑
设计思路:
1、使用QAudioInput进行音频输入;使用QAudioOutput进行音频输出
2、使用UDP进行音频数据传输
3、使用QTimer进行通话计时
头文件定义:
#include <QWidget>
#include <QUdpSocket>
#include <QAudioInput>
#include <QAudioOutput>
#include <QIODevice>
#include <QTimer>
#include <QTime>
namespace Ui {
class VoiceCall;
}
class VoiceCall : public QWidget
{
Q_OBJECT
public:
explicit VoiceCall(QWidget *parent = 0);
~VoiceCall();
private:
//初始化音频
void initAudio();
//开始通话
void startCall();
//挂断通话
void hungUp();
private slots:
void on_btn_dial_clicked();
void socketRead();
void audioRead();
void on_btn_hungUp_clicked();
void timeOut();
private:
Ui::VoiceCall *ui;
//UDP
QUdpSocket *m_pSocket;
//音频输入
QAudioInput *m_pAudioInput;
//音频输出
QAudioOutput *m_pAudioOutput;
//音频输入IO
QIODevice *m_pAudioInputIO;
//音频输出IO
QIODevice *m_pAudioOutputIO;
//对方IP
QHostAddress m_address;
//对话端口
int m_port;
//通话计时
QTime m_time;
//通话计时定时器
QTimer *m_pTimer;
};
创建UDP通信和初始化音频
VoiceCall::VoiceCall(QWidget *parent) :
QWidget(parent),
ui(new Ui::VoiceCall)
{
ui->setupUi(this);
m_pSocket = new QUdpSocket(this);
m_pSocket->bind(QHostAddress::Any, 8000);
connect(m_pSocket, SIGNAL(readyRead()), this, SLOT(socketRead()));
ui->btn_dial->setEnabled(true);
m_pTimer = new QTimer(this);
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(timeOut()));
initAudio();
}
VoiceCall::~VoiceCall()
{
delete ui;
}
void VoiceCall::initAudio()
{
//设置音频格式
QAudioFormat format;
format.setSampleRate(8000);
format.setSampleSize(8);
format.setSampleType(QAudioFormat::SignedInt);
format.setChannelCount(1);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
//获取默认的输入设备
QAudioDeviceInfo device = QAudioDeviceInfo::defaultInputDevice();
//判断设备是否有效,如果无效 获取最近的有效设备
if (!device.isFormatSupported(format))
{
format = device.nearestFormat(format);
}
//初始化输入
m_pAudioInput = new QAudioInput(format, this);
//初始化输出
m_pAudioOutput = new QAudioOutput(format, this);
}
进行语音通话
void VoiceCall::on_btn_dial_clicked()
{
QString address = ui->lineEdit_ipAddress->text();
if (address.isEmpty())
{
QMessageBox::information(this, "提示", "IP地址不能空!");
return;
}
else
{
m_pSocket->writeDatagram(QByteArray("call"), QHostAddress(address), 8000);
}
}
结束语音通话
void VoiceCall::hungUp()
{
ui->label_time->setText("");
ui->label_user->setText("");
m_pTimer->stop();
m_pAudioInputIO->close();
m_pAudioOutputIO->close();
disconnect(m_pAudioInputIO, SIGNAL(readyRead()), this, SLOT(audioRead()));
m_pAudioInput->stop();
m_pAudioOutput->stop();
ui->btn_dial->setEnabled(true);
ui->btn_hungUp->setEnabled(false);
}
void VoiceCall::on_btn_hungUp_clicked()
{
m_pSocket->writeDatagram(QByteArray("disConnect"), QHostAddress(m_address), m_port);
hungUp();
}
开始通话以及音频数据读取
void VoiceCall::startCall()
{
ui->btn_hungUp->setEnabled(true);
ui->btn_dial->setEnabled(false);
//对话已连接
qDebug() << "对话已连接";
//开始录音
m_pAudioInputIO = m_pAudioInput->start();
connect(m_pAudioInputIO, SIGNAL(readyRead()), this, SLOT(audioRead()));
m_pAudioOutputIO = m_pAudioOutput->start();
m_time = QTime(0, 0, 0);
ui->label_time->show();
ui->label_time->setText("通话时长:" + m_time.toString("hh:mm::ss"));
ui->label_user->show();
ui->label_user->setText("通话来自:" + m_address.toString().mid(7));
m_pTimer->start(1000);
}
void VoiceCall::audioRead()
{
QByteArray buffer;
buffer = m_pAudioInputIO->readAll();
//qDebug() << buffer.size();
if (buffer.size() > 1280)
{
return;
}
buffer.insert(0, "audio");
m_pSocket->writeDatagram(buffer, m_address, m_port);
}
UDP通信数据解析
void VoiceCall::socketRead()
{
while (m_pSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(m_pSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
m_pSocket->readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
if (datagram == "call")
{
if (QMessageBox::question(this, "提示", QString("来自%1的通话,是否接通?").arg(sender.toString().mid(7))) == QMessageBox::Yes)
{
m_pSocket->writeDatagram(QByteArray("connect"), sender, senderPort);
//保存对话的IP
m_address = sender;
m_port = senderPort;
startCall();
}
else
{
m_pSocket->writeDatagram(QByteArray("connectionDenied"), sender, senderPort);
}
}
else if (datagram == "connect")
{
qDebug() << "对方已接通";
//保存对话的IP
m_address = sender;
m_port = senderPort;
startCall();
}
else if (datagram == "connectionDenied")
{
//对方拒绝通话
QMessageBox::information(this, "提示", "对方拒绝通话");
}
else if (datagram == "disConnect")
{
//挂断通话
hungUp();
QMessageBox::information(this, "提示", "对方已经挂断通话");
}
else
{
//接收到通话数据
if (datagram.startsWith("audio"))
{
//说明是接受到了录音数据
m_pAudioOutputIO->write(datagram.mid(QByteArray("audio").size()));
}
}
}
}
通话计时
void VoiceCall::timeOut()
{
m_time = m_time.addSecs(1);
ui->label_time->setText("通话时长:" + m_time.toString("hh:mm::ss"));
}