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]={'\0'};  
  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]={'\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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM