zoukankan      html  css  js  c++  java
  • Qt实现基于G.729A(G729A)的语音聊天

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

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

    2.编码发送

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

    3.接收解码

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    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]={''};  
    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

  • 相关阅读:
    管理心理学[9095]
    汽车文化[1196]
    小四轴——空心杯电机引起的电源干扰
    38 时序电路扩展2
    37 时序电路扩展1
    36 时序电路的动态特性分析2
    35 时序电路的动态特性分析1
    34 同步时序电路的设计方法2
    33 同步时序电路的设计方法1
    60. 第k个排列
  • 原文地址:https://www.cnblogs.com/findumars/p/5706009.html
Copyright © 2011-2022 走看看