一、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的評論
http://blog.csdn.net/caoshangpa/article/details/51225733