一、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.初始化

  1. Widget::Widget(QWidget *parent) :
  2. QWidget(parent),
  3. ui(new Ui::Widget)
  4. {
  5. ui->setupUi(this);
  6. //设置采样格式
  7. QAudioFormat audioFormat;
  8. //设置采样率
  9. audioFormat.setSampleRate(8000);
  10. //设置通道数
  11. audioFormat.setChannelCount(1);
  12. //设置采样大小,一般为8位或16位
  13. audioFormat.setSampleSize(16);
  14. //设置编码方式
  15. audioFormat.setCodec("audio/pcm");
  16. //设置字节序
  17. audioFormat.setByteOrder(QAudioFormat::LittleEndian);
  18. //设置样本数据类型
  19. audioFormat.setSampleType(QAudioFormat::UnSignedInt);
  20. //获取设备信息
  21. QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
  22. if (!info.isFormatSupported(audioFormat))
  23. {
  24. qDebug()<<"default format not supported try to use nearest";
  25. audioFormat = info.nearestFormat(audioFormat);
  26. }
  27. info = QAudioDeviceInfo::defaultOutputDevice();
  28. if (!info.isFormatSupported(audioFormat)) {
  29. qDebug()<<"default format not supported try to use nearest";
  30. audioFormat = info.nearestFormat(audioFormat);
  31. }
  32. audioInput = new QAudioInput(audioFormat, this);
  33. //将麦克风的音频数据传输到输入设备
  34. streamIn = audioInput->start();
  35. //当输入设备检测到数据时,调用槽函数slogReadData
  36. connect(streamIn, SIGNAL(readyRead()), SLOT(slogReadData()));
  37. audioOutput = new QAudioOutput(audioFormat, this);
  38. //将音频数据传输到输出设备,再由输出设备写入到扬声器
  39. streamOut = audioOutput->start();
  40. //创建UDP线程
  41. CUdpThread *udpThread=new CUdpThread();
  42. udpThreadFather=new QThread();
  43. udpThread->moveToThread(udpThreadFather);
  44. connect(udpThreadFather,SIGNAL(started()),udpThread,SLOT(run()));
  45. //启动线程
  46. udpThreadFather->start();
  47. connect(this,SIGNAL(signalSendData(const QByteArray &)),udpThread,SLOT(slotSendData(const QByteArray &)));
  48. connect(udpThread,SIGNAL(signalSendData(const QByteArray &)),this,SLOT(slotSendData(const QByteArray &)));
  49. }

2.编码发送

  1. void Widget::slogReadData()
  2. {
  3. short srcAudio[L_FRAME]={0};
  4. unsigned char dstAudio[L_FRAME_COMPRESSED]={'\0'};
  5. if (!audioInput)
  6. {
  7. qDebug() << "AudioInput Error";
  8. return;
  9. }
  10. QByteArray dataBuffer(BUFFER_SIZE,0);
  11. qint64 len1 = audioInput->bytesReady();
  12. if (len1 > BUFFER_SIZE)
  13. {
  14. qDebug()<<"BUFFER_SIZE too small";
  15. return;
  16. }
  17. qint64 len2 = streamIn->read(dataBuffer.data(), len1);
  18. tempBuffer.append(dataBuffer.data(),len2);
  19. for(int i=0;i<tempBuffer.length()/(L_FRAME*2);i++)
  20. {
  21. //char转short
  22. memcpy(srcAudio,tempBuffer.data()+i*L_FRAME*2,L_FRAME*2);
  23. //编码
  24. cg729Encoder.encode(srcAudio, dstAudio);
  25. QByteArray frame;
  26. //reinterpret_cast用于强制转换,这里将unsigned char *转换为const char *。
  27. frame.append(reinterpret_cast<const char*>(dstAudio),L_FRAME_COMPRESSED);
  28. signalSendData(frame);
  29. }
  30. tempBuffer.clear();
  31. }

3.接收解码

  1. void Widget::slotSendData(const QByteArray &byte_array)
  2. {
  3. for(int i=0;i<byte_array.length()/L_FRAME_COMPRESSED;i++)
  4. {
  5. unsigned char srcAudio[L_FRAME_COMPRESSED]={'\0'};
  6. short dstAudio[L_FRAME]={0};
  7. memcpy(srcAudio,(unsigned char*)byte_array.data()+i * L_FRAME_COMPRESSED,L_FRAME_COMPRESSED);
  8. //G729解码
  9. cg729Decoder.decode(srcAudio,dstAudio,0);
  10. //short转char
  11. tempframe.append((char *)dstAudio,L_FRAME * 2);
  12. if(audioOutput&&audioOutput->state()!=QAudio::StoppedState&&
  13. audioOutput->state()!=QAudio::SuspendedState)
  14. {
  15. int chunks = audioOutput->bytesFree()/audioOutput->periodSize();
  16. while (chunks)
  17. {
  18. if (tempframe.length() >= audioOutput->periodSize())
  19. {
  20. //写入到扬声器
  21. streamOut->write(tempframe.data(),audioOutput->periodSize());
  22. tempframe = tempframe.mid(audioOutput->periodSize());
  23. }
  24. else
  25. {
  26. //写入到扬声器
  27. streamOut->write(tempframe);
  28. tempframe.clear();
  29. break;
  30. }
  31. --chunks;
  32. }
  33. }
  34. }
  35. }

三、演示效果

程序启动后,麦克风就开始工作了,聊天双方指定目的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的评论

http://blog.csdn.net/caoshangpa/article/details/51225733

Qt实现基于G.729A(G729A)的语音聊天的更多相关文章

  1. C#基于UDP实现的P2P语音聊天工具(1)

    这篇文章主要是一个应用,使用udp传送语音和文本等信息.在这个系统中没有服务端和客户端,相互通讯都是直接相互联系的.能够很好的实现效果. 语音获取 要想发送语音信息,首先得获取语音,这里有几种方法,一 ...

  2. c#基于udp实现的p2p语音聊天工具

    原创性申明 此博文的出处 为 http://blog.csdn.net/zhujunxxxxx/article/details/40124773假设进行转载请注明出处.本文作者原创,邮箱zhujunx ...

  3. qt中采用宽带speex进行网络语音通话实验程序

    qt中采用宽带speex进行网络语音通话实验程序 本文博客链接:http://blog.csdn.NET/jdh99,作者:jdh,转载请注明.   环境: 主机:WIN8 开发环境:Qt5 3.1. ...

  4. 基于C#局域网语音聊天

    基​于​C​#​局​域​网​语​音​聊​天​室​,​可​实​现​文​本​消​息​的​发​送​.​接​收​及​语​音​聊​天​,​是​一​个​很​不​错​的​,​适​合​初​学​者​的​软​件​开​发​ ...

  5. 搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 (1)

    搭建QQ聊天通信的程序:(1)基于 networkcomms.net 创建一个WPF聊天客户端服务器应用程序 原文地址(英文):http://www.networkcomms.net/creating ...

  6. C#实现多人语音聊天

    在上一篇文章 实现一个简单的语音聊天室(多人语音聊天系统)中,我用C#实现了一个简单的语音聊天室,并给出了源代码下载.尽管有源代码,可是非常多朋友反映,理解起来还是有些模糊.不够清楚.如今想来,是由于 ...

  7. 基于Android Classic Bluetooth的蓝牙聊天软件

    代码地址如下:http://www.demodashi.com/demo/12133.html BluetoothChat 基于Android Classic Bluetooth的蓝牙聊天软件,目前仅 ...

  8. Android 高仿微信语音聊天页面高斯模糊效果

    目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高 ...

  9. C# 实现语音聊天

    一.语音聊天说专业点就是即时语音,是一种基于网络的快速传递语音信息的技术,普遍应用于各类社交软件中,优势主要有以下几点: (1)时效性:视频直播会因为带宽问题有时出现延迟高的问题,而语音直播相对来说会 ...

随机推荐

  1. 各类XML parser的比较

    基于以上的比较 再为公司的项目选择解析器的时候,我选择Xerces.准备把Qt自带的XML库给去掉. references: http://stackoverflow.com/questions/17 ...

  2. logstash 安装zabbix插件

    <pre name="code" class="html">[root@xxyy yum.repos.d]# yum install ruby Lo ...

  3. MVC4.0 上传Excel并存入数据库

    这里的这个功能实现在WebForm很好实现,上传阶段简单的一个FileUoLoad控件就搞定了,什么取值,什么上传都是浮云,微软都帮我们封装好了,我们只需要一拖一拽就OK了,但这些在MVC中是不行的! ...

  4. poj2752 Seek the Name, Seek the Fame

    Description The little cat is so famous, that many couples tramp over hill and dale to Byteland, and ...

  5. hdu 4869 Turn the pokers(组合数+费马小定理)

    Problem Description During summer vacation,Alice stay at home for a long time, with nothing to do. S ...

  6. STM32F051 IAP源代码分享

    STM32F051 IAP源代码分享 假设不懂IAP的请自己主动脑补或者怒戳这里 http://www.openedv.com/posts/list/11494.htm 然后STM32F051的IAP ...

  7. 【leetcode】Merge Sorted Array(合并两个有序数组到其中一个数组中)

    题目: Given two sorted integer arrays A and B, merge B into A as one sorted array. Note: You may assum ...

  8. LoadRunner监控windows资源报错Monitor name :Windows Resources. Cannot connect to machine

    目标机:被监控的机器,windows server 2008 R2. 测试机:执行control的机器,windows7 操作:在测试机上执行Control,添加windows的监控 问题现象:Mon ...

  9. Traceroute程序

    Linux和Unix中为traceroute,Windows中对应的是Tracert.如:Tracert www.baidu.com   输出为路由信息. C:\Users\Administrator ...

  10. HashSet的分析(转)

    一.  HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素. 二.  ...