织梦网站专题页面如何做,WordPress只能ssl,网站建设营销的公司,优良的网站邮箱服务器提供商isp实现功能主要思路#xff1a;在网页端进行语音输入#xff0c;PC机可以实时接收并播放语音流。
此时#xff0c;Qt程序做客户端#xff0c;Web端做服务器#xff0c;使用QWebSocket进行通讯#xff0c;实时播放接收的语音流。
功能实现
想要实现该功能#xff0c;需要…实现功能主要思路在网页端进行语音输入PC机可以实时接收并播放语音流。
此时Qt程序做客户端Web端做服务器使用QWebSocket进行通讯实时播放接收的语音流。
功能实现
想要实现该功能需要完成以下两大部分。
第一部分QWebSocket通讯实现。简单
第二部分语音流实时播放功能。稍微有点难度
接下来对于该功能实现进行具体的讲解。
1建立通讯
1.1创建QWebSocket通讯
添加头文件
#include QWebSocketServer
#include QWebSocket
声明WebSocket对象并响应消息
m_pWebClient new QWebSocket;
connect(m_pWebClient, QWebSocket::connected, this, QWebSocketManager::MsgRecievd_Server_Connected);
connect(m_pWebClient, QWebSocket::disconnected, this, QWebSocketManager::MsgRecievd_Server_Disconnected);
connect(m_pWebClient, QWebSocket::textMessageReceived, this, QWebSocketManager::MsgRecievd_Server_TextMessageReceived);
connect(m_pWebClient, QWebSocket::binaryMessageReceived, this, QWebSocketManager::MsgRecievd_Server_BinaryMessageReceived);
分别响应了连接、断开、接收字符串内容、接收二进制内容。
1.2建立心跳包
一说到通讯首先想到的应该是心跳包机制。在与Web通讯也是如此为了防止掉线程序中也需要设定一个心跳包机制。
为了保证心跳包有连接但不频繁发送可以采用在无数据发送时采用3秒~10秒之间发送一条。
使用方法QTimer进行心跳包发送。
在程序使用过程中不需要精确发送时间只要在指定时间范围内3s~10s发送就可以了。
定义时间更新变量
DWORD m_dwReciveTime;//接收到WebSocket消息的时间
每次接收到web服务端发送数据时实时更新接收时间。
void QWebSocketManager::MsgRecievd_Server_TextMessageReceived(const QString message)
{qDebug() QStringLiteral(接收内容) message;m_dwReciveTime GetTickCount(); //更新接收时间
}void QWebSocketManager::MsgRecievd_Server_BinaryMessageReceived(const QByteArray message)
{qDebug() QStringLiteral(接收内容) message;m_dwReciveTime GetTickCount(); //更新接收时间
}
在项目中重写了两个接收消息所以都需要实时更新接收时间。
此时需要开启定时器假设每间隔3秒访问一次定时器核心代码如下
DWORD dwCalc GetTickCount() - pThis-m_dwReciveTime; //时间差 最新时间 - 模拟人上传数据时间
if (dwCalc g_nWebSocektHeartTime)
{//时间差 最小心跳包
}
else if ((dwCalc g_nWebSocektHeartTime) (dwCalc g_nWebSocketLostConnectTime))
{//发送心跳包协议
}
else if(dwCalc g_nWebSocketLostConnectTime)
{qDebug() QStringLiteral(连接超时);
}
注意这是我在通讯过程中进行了一点点小小优化大家也可以采用哟~
每次触发定时器时并没有直接发送心跳包而是当间隔超过10秒后代表断开连接了。
1.3接收web端音频流
在1.1中实现了QWebSocket的两个消息数据接收textMessageReceived、binaryMessageReceived
具体使用哪个消息需要对应服务端是如何发送的一般而言音频流采用二进制流的方式比较安全。
接收语音流数据实例代码如下
void QWebSocketManager::MsgRecievd_Server_BinaryMessageReceived(const QByteArray message)
{//qDebug() QStringLiteral(MsgRecievd_Server_BinaryMessageReceived内容) message;
}接收到音频流以后该如何进行播放呢
接下来就需要进行第二步重要功能语音流实时播放功能
2语音流实时播放功能
在这里我用的是QAudioOutput类使用该类方便操作。
2.1初始化输出音频参数
QAudioFormat audio_out_format;
//设置录音的格式
audio_out_format.setSampleRate(8000); //采样率
audio_out_format.setChannelCount(1); //通道数
audio_out_format.setSampleSize(16);
audio_out_format.setCodec(audio/pcm); //编码格式
audio_out_format.setByteOrder(QAudioFormat::LittleEndian); //样本是小端字节顺序
audio_out_format.setSampleType(QAudioFormat::SignedInt); //样本类型QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());m_pAudioOutput new QAudioOutput(audio_out_format);
m_pStreamOutput m_pAudioOutput-start();
m_nPeriodSize m_pAudioOutput-periodSize();
代码分析
录音的格式要与服务端输入的音频流格式才能保证客户端接收到清晰完整的音频流。
此时需要注意的是最后一行代码m_nPeriodSize m_pAudioOutput-periodSize();
这是实现播放音频流的核心之一
2.2播放接收的音频流
针对这部分实现方式我经历了以下几个步骤已踩坑希望对大家有用
简单有问题的实现方式
void QWebSocketManager::MsgRecievd_Server_BinaryMessageReceived(const QByteArray message)
{//qDebug() QStringLiteral(MsgRecievd_Server_BinaryMessageReceived内容) message;m_dwReciveTime GetTickCount(); //更新接收时间m_pStreamOutput-write(array); //播放音频流
}
接收到音频流就直接播放。使用这种方法会发现音频是可以播放但是叽里呱啦的每次智能听到说话的第一个字其余的全都听不到了。
此时你会怀疑是不是服务端传入的音频流不正确呢因为客户端可以播放声音。如果你沿着这条路走那你就错了。
原因之所以只能听到说话的第一个字是因为频繁地接收数据上一次接收的音频流还未播放完毕就立刻播放下一条音频流所以会出现这种问题了。
那么该如何解决这种问题呢
在这里就用到了初始化时我所说的核心代码了。
m_nPeriodSize 是每次播放一条完整音频格式的大小服务端传入的数据大小我们无法控制但是可以在播放时每次取m_nPeriodSize 大小的数据进行播放就能保证数据的完整性。
那么如何知道上一次播放的音频流已经完成了呢
使用m_pAudioOutput-bytesFree()循环进行判断只有当释放的缓存数小于m_nPeriodSize 才能够继续播放音频流
下面为大家展示有效地实现方法。
复杂有效的实现方式
void QWebSocketManager::MsgRecievd_Server_BinaryMessageReceived(const QByteArray message)
{//qDebug() QStringLiteral(MsgRecievd_Server_BinaryMessageReceived内容) message;m_dwReciveTime GetTickCount(); //更新接收时间{std::lock_guardstd::mutex lck(m_mutexPcm); //C11用法m_ArrayAudio.append(message);}if (m_bRunningAudio false){m_bRunningAudio true; //开启数据处理线程m_threadAudio std::thread(QWebSocketManager::ThreadProcessingPCMData, this, this);}
}
代码解析
当接收到第一条音频数据时开启线程将音频播放处理放到线程中进行判断只有把上一次播放的音频缓存释放完成后才能够从缓存m_ArrayAudio中获取m_nPeriodSize大小的数据
线程实现代码如下
unsigned int QWebSocketManager::ThreadProcessingPCMData(void* pParam)
{QWebSocketManager* pThis reinterpret_castQWebSocketManager*(pParam);while (pThis-m_bRunningAudio true){//只有满足一个完整包数据时才需要处理if (pThis-m_ArrayAudio.count() m_nPeriodSize){if (m_pAudioOutput-bytesFree() m_nPeriodSize){Sleep(5);continue; //当前音频释放大小 固定大小时不处理}std::lock_guardstd::mutex lck(m_mutexPcm); //C11用法QByteArray array pThis-m_ArrayAudio.mid(0, m_nPeriodSize);pThis-m_pStreamOutput-write(array);pThis-m_ArrayAudio.remove(0, m_nPeriodSize);qDebug() QStringLiteral(处理一次完整的音频此时剩余大小 ) pThis-m_ArrayAudio.count();}else{Sleep(1000);}}return 0;
}
以上就是核心的实现流程了如果需要查看原始代码的请看下面链接
Qt中使用QWebSocket与Web进行通讯实时语音通话 我是糯诺诺米团一名C开发程序媛~