一、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. 【转】Install SmartGit via PPA in Ubuntu 13.10/13.04/12.04/Linux Mint

    原文网址:http://ubuntuhandbook.org/index.php/2013/09/install-smartgit-via-ppa-ubuntu-linux-mint/ This tu ...

  2. Delphi 调用系统中的计算器、记事本、画图软件方法

    1.直接调用    前面uses加 ShellAPI ShellExecute(Handle, 'open', PChar('calc.exe'), nil, nil, SW_SHOW); 2.直接调 ...

  3. j2ee开源项目——IT学习者博客(itxxzblog v1.0)

    大家好,我是IT学习者-螃蟹,已经有近一周的时间没有更新文章了,作为回报,今天起将更新一个大件,也就是螃蟹还在进行中的IT学习者博客. IT学习者博客的初期设计已经完成,功能也已经完成了大半,具备了当 ...

  4. sicily 1099 Packing Passengers

    /****  题意:  求x,y 满足 x*pa+y*pb=n 同时使得 p = x*ca+y*cb的值最小,若有多种可能,则选择最大x值的组合**  分析:  x*pa+y*pb=n 可以用 线性同 ...

  5. Android Studio使用技巧系列教程(二)

    尊重劳动成果,转载请注明出处:http://blog.csdn.net/growth58/article/details/46764575 关注新浪微博:@于卫国 邮箱:yuweiguocn@gmai ...

  6. [Redux] Extracting Presentational Components -- TodoApp

    Finally, I just noticed that the to-do app component doesn't actually have to be a class. I can turn ...

  7. (转)走进JVM,浅水也能捉鱼

    这不是一篇描述jvm是什么的文章,也不介绍jvm跨平台的特性,也不是讲述jvm安全特性的文章,更不是讲解jvm指令操作,数据运算的文章,本文重点讲述类型的生命周期. 类型的生命周期涉及到:类的装载.j ...

  8. 作为java应届生,面试求职那点事

    找工作两星期多了.心情不爽,写点记录打发时间. 嘘~~自己的破事:  刚毕业,也过了实习,本理所应当的留在公司转正.可是为了谈了两年的女朋友回家见面.一切都顺利进行,妈妈也开心给了一万见面礼,一切都以 ...

  9. UITableView 属性集合

    UITableView-------表视图--继承UIScrollView并遵守NSCoding协议 属性 frame-------------设置控件的位置和大小 backgroundColor-- ...

  10. exc_bad_access(code=1, address=0x789870)野指针错误

    原因: exc_bad_access(code=1, address=0x789870)野指针错误,主要的原因是,当某个对象被完全释放,也就是retainCount,引用计数为0后.再去通过该对象去调 ...