Qt实现基于G.729A(G729A)的语音聊天
来源:互联网 发布:淘宝联盟返利真假 编辑:程序博客网 时间:2024/05/13 12:22
一、G.729协议简介
G.729协议是由ITU-T的第15研究小组提出的,并在1996年3月通过的8Kbps的语音编码协议。
G.729系列主要有以下几种:
G.729—最基本的G.729标准协议,原始版
G.729A—精简版的G.729,兼容原始版G.729,对G.729一些算法进行简单处理,相当于降低了算法的复杂度
G.729B—加入了语音端点检测模块,在编码前对语音进行语音和静默音进行检测,然后分别对不同情况进行编码
G.729AB—就是G.729A中加入语音端点检测模块,兼容G.729B,目前G.729AB用得比较多
1.占用带宽小
使用普通编码的语音通讯需要占用64Kbps的带宽,而G.729仅仅需要8Kbps。
2.占用CPU时间多
使用G.729时CPU的使用时间大约为G.711的4倍,所以使用G.729时需要注意设备是否有足够的处理能力。
参考链接:https://en.wikipedia.org/wiki/G.729
G.729协议是由ITU-T的第15研究小组提出的,并在1996年3月通过的8Kbps的语音编码协议。
G.729系列主要有以下几种:
G.729—最基本的G.729标准协议,原始版
G.729A—精简版的G.729,兼容原始版G.729,对G.729一些算法进行简单处理,相当于降低了算法的复杂度
G.729B—加入了语音端点检测模块,在编码前对语音进行语音和静默音进行检测,然后分别对不同情况进行编码
G.729AB—就是G.729A中加入语音端点检测模块,兼容G.729B,目前G.729AB用得比较多
G.729协议的实现是开源的,源码可以从ITU官网下载。
下载链接:https://www.itu.int/rec/T-REC-G.729/e
本文采用VoiceAge公司封装的G.729A静态库进行语音的编解码。
下载链接:http://download.csdn.net/detail/caoshangpa/9496833
由于低带宽的需求,G.729通常应用于VoIP(Voice over Internet Protocol),比如说视频会议。G.729有两大特点。1.占用带宽小
使用普通编码的语音通讯需要占用64Kbps的带宽,而G.729仅仅需要8Kbps。
2.占用CPU时间多
使用G.729时CPU的使用时间大约为G.711的4倍,所以使用G.729时需要注意设备是否有足够的处理能力。
二、聊天过程
1.初始化
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); //设置采样格式 QAudioFormat audioFormat; //设置采样率 audioFormat.setSampleRate(8000); //设置通道数 audioFormat.setChannelCount(1); //设置采样大小,一般为8位或16位 audioFormat.setSampleSize(16); //设置编码方式 audioFormat.setCodec("audio/pcm"); //设置字节序 audioFormat.setByteOrder(QAudioFormat::LittleEndian); //设置样本数据类型 audioFormat.setSampleType(QAudioFormat::UnSignedInt); //获取设备信息 QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(audioFormat)) { qDebug()<<"default format not supported try to use nearest"; audioFormat = info.nearestFormat(audioFormat); } info = QAudioDeviceInfo::defaultOutputDevice(); if (!info.isFormatSupported(audioFormat)) { qDebug()<<"default format not supported try to use nearest"; audioFormat = info.nearestFormat(audioFormat); } audioInput = new QAudioInput(audioFormat, this); //将麦克风的音频数据传输到输入设备 streamIn = audioInput->start(); //当输入设备检测到数据时,调用槽函数slogReadData connect(streamIn, SIGNAL(readyRead()), SLOT(slogReadData())); audioOutput = new QAudioOutput(audioFormat, this); //将音频数据传输到输出设备,再由输出设备写入到扬声器 streamOut = audioOutput->start(); //创建UDP线程 CUdpThread *udpThread=new CUdpThread(); udpThreadFather=new QThread(); udpThread->moveToThread(udpThreadFather); connect(udpThreadFather,SIGNAL(started()),udpThread,SLOT(run())); //启动线程 udpThreadFather->start(); connect(this,SIGNAL(signalSendData(const QByteArray &)),udpThread,SLOT(slotSendData(const QByteArray &))); connect(udpThread,SIGNAL(signalSendData(const QByteArray &)),this,SLOT(slotSendData(const QByteArray &)));}2.编码发送
void Widget::slogReadData(){ short srcAudio[L_FRAME]={0}; unsigned char dstAudio[L_FRAME_COMPRESSED]={'\0'}; if (!audioInput) { qDebug() << "AudioInput Error"; return; } QByteArray dataBuffer(BUFFER_SIZE,0); qint64 len1 = audioInput->bytesReady(); if (len1 > BUFFER_SIZE) { qDebug()<<"BUFFER_SIZE too small"; return; } qint64 len2 = streamIn->read(dataBuffer.data(), len1); tempBuffer.append(dataBuffer.data(),len2); for(int i=0;i<tempBuffer.length()/(L_FRAME*2);i++) { //char转short memcpy(srcAudio,tempBuffer.data()+i*L_FRAME*2,L_FRAME*2); //编码 cg729Encoder.encode(srcAudio, dstAudio); QByteArray frame; //reinterpret_cast用于强制转换,这里将unsigned char *转换为const char *。 frame.append(reinterpret_cast<const char*>(dstAudio),L_FRAME_COMPRESSED); signalSendData(frame); } tempBuffer.clear();}3.接收解码
void Widget::slotSendData(const QByteArray &byte_array){ for(int i=0;i<byte_array.length()/L_FRAME_COMPRESSED;i++) { unsigned char srcAudio[L_FRAME_COMPRESSED]={'\0'}; short dstAudio[L_FRAME]={0}; memcpy(srcAudio,(unsigned char*)byte_array.data()+i * L_FRAME_COMPRESSED,L_FRAME_COMPRESSED); //G729解码 cg729Decoder.decode(srcAudio,dstAudio,0); //short转char tempframe.append((char *)dstAudio,L_FRAME * 2); if(audioOutput&&audioOutput->state()!=QAudio::StoppedState&& audioOutput->state()!=QAudio::SuspendedState) { int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); while (chunks) { if (tempframe.length() >= audioOutput->periodSize()) { //写入到扬声器 streamOut->write(tempframe.data(),audioOutput->periodSize()); tempframe = tempframe.mid(audioOutput->periodSize()); } else { //写入到扬声器 streamOut->write(tempframe); tempframe.clear(); break; } --chunks; } } }}
三、演示效果
程序启动后,麦克风就开始工作了,聊天双方指定目的IP后,点击按钮1就可以进行聊天。如果不想对方听到自己的声音,点击按钮2关闭声音发送。
参考链接:https://en.wikipedia.org/wiki/G.729
参考链接:http://blog.csdn.net/jdh99/article/details/39525451
源码链接:见http://blog.csdn.net/caoshangpa/article/details/51225733的评论
11 1
- Qt实现基于G.729A(G729A)的语音聊天
- 基于Internet的语音聊天软件设计与实现
- 基于Bmob服务的简单聊天(另加语音通知)
- 从pcm,u,A率到g729A转化的实现,delphi代码
- 基于IPv6的语音聊天程序
- 基于mina框架的语音聊天服务器
- 基于Qt的UDP传输文字聊天小软件实现
- qt中采用G.729A进行网络语音通话实验程序
- iOS 基于环信SDK实现即时通讯-语音、视频聊天
- 语音聊天实现(Delphi)
- 实现语音聊天
- C++实现语音聊天
- IOS语音聊天实现
- 基于QT的聊天小工具(未完成)
- 基于Qt的语音识别功能
- 基于网络视频聊天语音通话的开源框架
- 基于QT的网络聊天系统
- 基于Qt的局域网聊天软件
- Hexo+github 搭建个人博客
- 反转链表
- 一个帖子学会Android开发四大组件
- 旧文重发:做人、做事,做架构师——架构师能力模型解析
- coco2d-js菜单项cc.MenuItem详解
- Qt实现基于G.729A(G729A)的语音聊天
- 从“架构师书单”讲开去
- WPF代码模板-布局部分
- 3-2 Verilog 4位行波进位加法器
- 【浅谈bootsrap响应式布局】
- 数据结构--单链表
- spring多数据源配置
- (1)VMware虚拟机安装与创建虚拟机
- 下载老版本的xcode